代码随想录自刷2:链表篇

65 阅读2分钟

代码随想录自刷2:链表篇

203.移除链表元素

203. 移除链表元素

思路:避免头节点就是要删除的节点之一,要用一个虚拟头节点dummy接上:dummy.next=head 返回的时候直接dummy.next即可!

  • 需要有一个前节点pre,方便接上断开的链
  • 需要有一个当前节点cur,方便遍历
public ListNode removeElements(ListNode head, int val) {
        ListNode dummy=new ListNode();
        ListNode cur=head;
        dummy.next=cur;
        ListNode pre=dummy;
        while(cur!=null){
            if(cur.val==val){
                //接上断开的节点
                pre.next=cur.next;
            }else{
                //pre往下移动
                pre=cur;
            }
            //遍历下一个
            cur=cur.next;
        }
        return dummy.next;
    }

707. 设计链表

707. 设计链表

思路:设计一个ListNode,要有valnext属性。

  • MyLinkedList要有size记录链表长度(不含虚拟头节点),和dummy(虚拟头节点)
class MyLinkedList {
    private class ListNode{
        int val;
        ListNode next;
        public ListNode(){}
        public ListNode(int val){
            this.val=val;
        }
    }

    ListNode dummy;
    int size;
    
    public MyLinkedList() {
        dummy=new ListNode();
        size=0;
    }
    
    public int get(int index) {
        if(index>=size || index<0)
            return -1;
        ListNode cur=dummy;
        //包含index是要把dummy给过滤掉
        for(int i=0;i<=index;i++){  
            cur=cur.next;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
       addAtIndex(0,val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    
    public void addAtIndex(int index, int val) {
        if(index>size)
            return;
        if(index<0)
            index=0;
        //插入一个元素所以++
        size++;
        ListNode cur=dummy;
        //找到要插入位置的前一个元素
        for(int i=0;i<index;i++){
            cur=cur.next;
        }
        ListNode tmp=cur.next;
        cur.next=new ListNode(val);
        cur.next.next=tmp;
    }


    
    public void deleteAtIndex(int index) {
        if(index>=size || index<0)
            return;
        ListNode cur=dummy;
        for(int i=0;i<index;i++){
            cur=cur.next;
        }
        size--;
        cur.next=cur.next.next;
    }
}

206. 反转链表

206. 反转链表

思路:链表的增删,通常都需要有虚拟头节点!但这题不需要dummy,因为反转到最后发现头节点跑到后面去了,而pre此时正好停留在头节点位置上,所以到时候直接返回pre就是反转后的头节点啦

  • 主要修改节点间的next关系
  • 需要有两个节点遍历原来的链表:precur
  • 遍历中需要记录cur.next,因为在修改cur.next关系指向pre时,就把链表断开了,cur要想移动到下一个节点就需要通过这个记录来移动
public ListNode reverseList(ListNode head) {
        ListNode pre=null;
        ListNode cur=head;
        while(cur!=null){
            ListNode tmp=cur.next;
            cur.next=pre;
            pre=cur;    
            cur=tmp;    //移动
        }
        return pre;
    }

24. 两两交换链表中的节点

24. 两两交换链表中的节点

思路:需要用到的节点比较多,最好画图辅助理解交换的过程。注意cur当前指向的是dummy!!

参考代码随想录的图:

24.两两交换链表中的节点1.png

public ListNode swapPairs(ListNode head) {
        ListNode dummy=new ListNode();
        dummy.next=head;
        ListNode cur=dummy;
        ListNode tmp; // 临时节点,保存两个节点后面的节点
        ListNode first; // 临时节点,保存两个节点之中的第一个节点
        ListNode second; // 临时节点,保存两个节点之中的第二个节点
        while(cur.next!=null && cur.next.next!=null){
            first=cur.next;
            second=cur.next.next;
            tmp=cur.next.next.next;

            cur.next=second;
            second.next=first;
            first.next=tmp;
            //移动
            cur=first;
        }
        return dummy.next;
    }

19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点

思路:使用快慢指针,涉及到删除所以用到dummy,注意这里快慢指针都先指向dummy

  • 根据删除倒数第n个节点,快指针先走n+1步(因为一开始指向dummy所以+1)
  • 快慢指针再一起遍历,直到快指针走完链表
  • 此时慢指针来到要删除的节点前一个位置
public ListNode removeNthFromEnd(ListNode head, int n) {
        ListNode dummy=new ListNode();
        ListNode fast=dummy;
        ListNode slow=dummy;
        dummy.next=head;
        while(n>=0){
            fast=fast.next;
            n--;
        }
        while(fast!=null){
            fast=fast.next;
            slow=slow.next;
        }
        slow.next=slow.next.next;
        return dummy.next;
    }

面试题 02.07. 链表相交

面试题 02.07. 链表相交

思路:

  • 两个链表先各自遍历计算长度,自己规定链表A永远代表较长的链表(方便后续计算),所以计算完后如果当前链表A较短要做对调操作。
  • 两个长度相减得到差值,让链表A先走这么多步,这样两个链表的起点位置就相同了
  • 开始遍历链表找出相同节点就是相交处
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode nodeA=headA;
        ListNode nodeB=headB;
        int lA=0;
        int lB=0;
        while(nodeA!=null){
            nodeA=nodeA.next;
            lA++;
        }
        while(nodeB!=null){
            nodeB=nodeB.next;
            lB++;
        }
        nodeA=headA;
        nodeB=headB;
        if(lA<lB){
            int tmp=lA;
            lA=lB;
            lB=tmp;
            nodeA=headB;
            nodeB=headA;
        }
        int num=lA-lB;
        while(num>0){
            nodeA=nodeA.next;
            num--;
        }
        while(nodeA!=null){
            if(nodeA==nodeB){
                return nodeA;
            }
            nodeA=nodeA.next;
            nodeB=nodeB.next;
        }
        return null;
    }

142. 环形链表 II

142. 环形链表 II

思路:主要用快慢指针,快指针永远走2步,慢指针走1步。然后分两部分判断:一是否有循环、二循环口在哪里

  • 如果快指针和慢指针相等了,就表示走进循环中
    • (此时选定慢指针,让其从当前点继续每次走1步)
    • 一个指针从头开始(每次走1步)如果和慢指针相等了就表示当前点就是循环口!
  • 没有相等表示没有循环!

具体公式参考代码随想录的推导

public ListNode detectCycle(ListNode head) {
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast){
                ListNode cur=head;
                while(cur!=fast){
                    cur=cur.next;
                    fast=fast.next;
                }
                return cur;
            }
        }
        return null;
    }