万物的算法日记|第三天

笔者自述:

一直有一个声音也一直能听到身边的大佬经常说,要把算法学习搞好,一定要重视平时的算法学习,虽然每天也在学算法,但是感觉自己一直在假装努力表面功夫骗了自己,没有规划好自己的算法学习和总结,因为后半年也该找实习了,所以每日的算法题要进行恶补,勤能补拙,因此有了这一个算法日记系列;

必读: 大佬你好,感谢您的阅读,这篇文章是我的算法笔记,方便我每日回顾;
为了不耽误您的时间,我把本篇日记的考点方向和算法知识总结列出来,如果对您有需要要就进行阅读

也希望对您有帮助,和您一起通关算法!致谢

请添加图片描述

算法语言:java
题目来源:力扣–书本–初级算法,可以在力扣中搜索相关题名找到更多解法和大神方法
本文知识点:

  1. 使用LInkedList模拟栈
    因为LInkedList内实现了栈数据结构的所有基本方法,例如addLast(),removeLast()等,同时也支持在任意位置进行插入,删除和读取操作。

addLast(E e):将元素添加到链表末尾,相当于将元素压入栈顶。
removeLast():从链表末尾删除一个元素,相当于弹出栈顶元素。
getLast():获得链表末尾的元素,相当于读取栈顶元素。 创建栈:

LinkedList<Integer> stack = new LinkedList<>(); 创建栈,
stack.addLast(1); 将1入栈
int top = stack.removeLast(); 将 1出栈,并赋值给 top
int top = stack.getLast(); 获得栈顶元素,即 1
stack.add(1, 4); 在栈中的第二个位置插入元素 4
stack.remove(1);删除栈中的第二个元素

  1. 哈希表的简单使用
    为什么会想到使用哈希表解决?
    原因:我们要保证复制出来的新链表中节点的random指针指向正确,需要使用一种方法来将原链表中的每个节点与其新表中对应的节点建立映射关系,方便后续在复制random时,找到对应关系
    哈希表:适合存储键值对对应关系,适用场景:快速查找,增删操作,当数据规模大的时候,性能优于数组和链表
  2. 学会使用StringBuilder对字符串类型进行处理
    什么是StringBuilder: 具有修改String的能力,相对于直接使用String对象,避免了不必要的内存开销
    适用场景:需要频繁对字符串拼接或者修改的情况下使用,避免了频繁创建对象的开销,性能更好
    方法:
    StringBuilder(): 无参构造方法,创建一个空的StringBuilder对象
    appen():向StringBuilder对象的末尾添加指定的数据
    insert() 向指定位置插入数据
    delete() 删除指定位置的数据
    replace() 替换指定位置的数据
    reverse() 将对象中的顺序颠倒
    lenth() 获取StringBuilder对象中的长度

切记:最后需要转化成对应的输出类型。:

详细可针对具体题目具体分析~~

剑指Offer06.从尾到头打印链表

在这里插入图片描述
代码:

public class day6_14_0_update {
    
    
    //递归思路 递归到尾部 然后进行回溯
    //代码非常的简单,而且也容易理解
    ArrayList<Integer> tmp = new ArrayList<>();
    public int[] reversePrint(ListNode head){
    
    
        recur(head);
        int[] res = new int[tmp.size()];
        for(int i =0;i<res.length;i++)
            res[i] = tmp.get(i);
        return res;
    }
    //通过递归到链表尾部,如果是空的话,就一层一层回溯,就实现了将尾部值依次添加到链表中
    public void recur(ListNode head){
    
    
        if(head == null)
            return;
        recur(head.next);
        tmp.add(head.val);
    }
//-------------------------------------------------------------------
    //使用辅助栈来 栈的特性 先进后出 使用LinkedList 来模拟栈
    public int[] reversePrint1(ListNode head){
    
    
        LinkedList<Integer> stack = new LinkedList<Integer>();
        while(head != null){
    
    
            stack.addLast(head.val);
            head = head.next;
        }
        int [] res = new int[stack.size()];
        for(int i =0;i<res.length;i++){
    
    
            res[i] = stack.removeLast();
        }
        return res;
    }
}

学到的知识:

  1. 之前的文章也说过了ArrayList和LInked的底层原理区别和对应的适用场景。
    通过LInkedList模拟栈,因为LInkedList内实现了栈数据结构的所有基本方法,例如addLast(),removeLast()等,同时也支持在任意位置进行插入,删除和读取操作。

addLast(E e):将元素添加到链表末尾,相当于将元素压入栈顶。
removeLast():从链表末尾删除一个元素,相当于弹出栈顶元素。
getLast():获得链表末尾的元素,相当于读取栈顶元素。 创建栈:

LinkedList<Integer> stack = new LinkedList<>(); 创建栈,
stack.addLast(1); 将1入栈
int top = stack.removeLast(); 将 1出栈,并赋值给 top
int top = stack.getLast(); 获得栈顶元素,即 1
stack.add(1, 4); 在栈中的第二个位置插入元素 4
stack.remove(1);删除栈中的第二个元素

剑指Offer24.反转链表

在这里插入图片描述
代码:


public class day6_14_1 {
    
    
    //反转链表
    //遍历 : 从头开始 直接修改指针指向
    public ListNode reverseList(ListNode head){
    
    
        ListNode cur = head,pre = null;
        while(cur!= null){
    
    
            ListNode temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur= temp;
        }
        return pre;
    }

    //递归回溯
    public ListNode reverseList1(ListNode head){
    
    
        return recur(head,null);
    }
    public ListNode recur(ListNode cur,ListNode pre){
    
    
        if(cur == null) return pre; //终止条件
        ListNode res = recur(cur.next,cur); //递归后继节点
        cur.next = pre; // 修改节点引用指向
        return res;  // 返回反转链表的头节点
    }

}

学到的知识:

  1. 递归的简单入门,递归的思路很简洁,要代码实现正确难一点,需要多练习和理解

剑指 Offer 35. 复杂链表的复制

在这里插入图片描述
首先我们先看一下简单的链表复制操作:

//简单的链表复制
    public Node copyEasyList(Node head){
    
    
        Node cur = head;
        Node dum = new Node(0),pre = dum;
        while(cur != null){
    
    
            Node node = new Node(cur.val);
            pre.next = node;
            cur = cur.next;
            pre = node;
        }
        return dum.next;
    }

理解了这一步,对于下面的复杂链表复制,只需要搞懂两个循环内部意思即可
代码:

/*
// Definition for a Node.
class Node {
    int val;
    Node next;
    Node random;

    public Node(int val) {
        this.val = val;
        this.next = null;
        this.random = null;
    }
}
*/
class Solution {
    
    
    public Node copyRandomList(Node head){
    
    
        if(head == null){
    
    
            return null;
        }
        Node cur = head;
        //使用哈希表来进行存储节点信息
        Map<Node,Node> map = new HashMap<Node,Node>();
        //复制各节点,并建立 原节点- 新节点的map映射
        while(cur != null){
    
    
            map.put(cur,new Node(cur.val));
            cur =cur.next;
        }
        cur = head;
        while(cur != null){
    
    
            map.get(cur).next = map.get(cur.next);
            map.get(cur).random = map.get(cur.random);
            cur =cur.next;
        }
        return map.get(head);
    }
}

学到的知识点: 这题感觉挺难的,对我而言 哈哈

  1. 我之前错误的认为链表中的两个元素之间的指向是真实存在的哈哈,其实只是为了方便假象的。对于第二个while循环内的随机数赋值操作,其实可以理解成 赋值后就完成了对应的指针指向。
  2. 为什么会想到使用哈希表解决?
    原因:我们要保证复制出来的新链表中节点的random指针指向正确,需要使用一种方法来将原链表中的每个节点与其新表中对应的节点建立映射关系,方便后续在复制random时,找到对应关系
    哈希表:适合存储键值对对应关系,适用场景:快速查找,增删操作,当数据规模大的时候,性能优于数组和链表

剑指Offer 05.替换空格

在这里插入图片描述
代码:

public class day6_14_3 {
    
    
    // 拆分字符,遇见空格替换 时间复杂度为O(n)
    public String replaceSpace(String s){
    
    
        String a ="";
        for(int i =0;i<s.length();i++){
    
    
            if(s.charAt(i) == ' '){
    
    
                a+="%20";
                continue;
            }
            a+=s.charAt(i);
        }
        return a;
    }
    //使用 StringBuilder 来进行修改
    public String replaceSpace1(String s){
    
    
        StringBuilder res = new StringBuilder();
        for(Character c: s.toCharArray()){
    
    
            if(c == ' ')
                res.append("%20");
            else
                res.append(c);
        }
        return res.toString();
    }
    //直接使用API中的方法
    public String replaceSpace2(String s){
    
    
        return s.replace(" ","%20");
    }
}

学到的知识:

  1. s.charAt(i) 可以提取字符串中的固定位置字符
  2. 学会使用StringBuilder对字符串类型进行处理
    什么是StringBuilder: 具有修改String的能力,相对于直接使用String对象,避免了不必要的内存开销
    适用场景:需要频繁对字符串拼接或者修改的情况下使用,避免了频繁创建对象的开销,性能更好
    方法:
    StringBuilder(): 无参构造方法,创建一个空的StringBuilder对象
    appen():向StringBuilder对象的末尾添加指定的数据
    insert() 向指定位置插入数据
    delete() 删除指定位置的数据
    replace() 替换指定位置的数据
    reverse() 将对象中的顺序颠倒
    lenth() 获取StringBuilder对象中的长度

切记:最后需要转化成对应的输出类型。

  1. 记住一些API自带的一些方法,可以省去很多时间,s.replace(" “,”%20"),将A替换成B

剑指 Offer 58 II. 左旋转字符串

在这里插入图片描述

代码:


public class day6_14_4 {
    
    
    //简陋的实现方法 时间复杂度高
    public String reverseLeftWords(String s,int n){
    
    
        String a = "";
        String b = "";
        for(int i =0;i<n;i++){
    
    
            a += s.charAt(i);
        }
        b = s.replace(a,"");
        return b+a;
    }
    //使用StringBuilder
    public String reverseLeftWords1(String s ,int n){
    
    
        StringBuilder sh = new StringBuilder(s);
        for(int i =0;i<n;i++){
    
    
            sh.append(s.charAt(i));
            sh.deleteCharAt(0);
        }
        return  sh.toString();
    }
    //利用字符串切割操作
    public String reverseLeftWords2(String s,int n){
    
    
        return s.substring(n,s.length())+s.substring(0,n);
    }
}

学到的知识:

  1. 可以使用字符串自带的方法去切割,使用StringBuilder()来进行添加修改,以及使用列表或者二倍字符串来进行分隔等等,都可以实现,当做练手题目。

猜你喜欢

转载自blog.csdn.net/weixin_54174102/article/details/131199856