算法训练--链表

318 阅读8分钟

算法训练--链表

链表基本概念

  • 链表的定义:链表是一种递归的数据结构,他或者为空(null),或者是指向一个结点(node)的引用,该结点含有一个泛型的元素和一个指向另一条链表的引用

    链表是一种常见且基础的数据结构,是一种线性表,但是他不是按线性顺序存取数据,而是在每一个节点里存到下一个节点的地址。我们也可以这样理解,链表是通过指针串联在一起的线性结构,每一个链表结点由两部分组成,数据域及指针域,链表的最后一个结点指向null。也就是我们所说的空指针

  • 链表常用的有 3 类: 单链表、双向链表、循环链表

    • 单链表:单链表 [Linked List]:由各个内存结构通过一个 Next 指针链接在一起组成,每一个内存结构都存在后继内存结构【链尾除外】,内存结构由数据域和 Next 指针域组成

      img

    • 双向链表:双向链表 [Double Linked List]:,由各个内存结构通过指针 Next 和指针 Prev 链接在一起组成,每一个内存结构都存在前驱内存结构和后继内存结构【链头没有前驱,链尾没有后继】,内存结构由数据域、Prev 指针域和 Next 指针域组成

      img

    • 循环链表:由各个内存结构通过一个指针 Next 链接在一起组成,每一个内存结构都存在后继内存结构,内存结构由数据域和 Next 指针域组成链表首尾相连

      链表4

  • 链表数组性能分析

    image.png

相关题目练习

206. 反转链表

  • 题目描述

    image.png

  • 题解

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode reverseList(ListNode head) {
            if(head==null){
                return head;
            }
          	//前指针节点
            ListNode pre=null;
          	//当前指针节点
            ListNode curr=head;
            while(curr!=null){
                ListNode temp=curr.next;//临时节点,存放当前节点的下一个节点
                curr.next=pre;//将当前节点的指针指向前面的节点
                pre=curr;//前指针后移
                curr=temp;//当前指针后移
            }
            return pre;
        }
    }
    

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

  • 题目描述

    image.png

  • 题解

    image.png

    // 虚拟头结点
    class Solution {
        public ListNode swapPairs(ListNode head) {
            ListNode dummy=new ListNode(0);
            dummy.next=head;
            ListNode pre=dummy;
            while(pre.next!=null && pre.next.next!=null){
                ListNode temp=head.next.next;//缓存head.next.next为temp
                pre.next=head.next;//步骤一:pre的next指向head的next
                head.next.next=head;//步骤二:head.next的next指向head
                head.next=temp;//步骤三:head的next指向temp
                pre=head;//后移
                head=head.next;//后移
            }
            return dummy.next;//输出排除虚拟头节点
        }
    }
    

141. 环形链表

  • 题目描述

    image.png

  • 题解

    /**
    	快慢指针,快指针每次移动两位,慢指针移动一位
    */
    public class Solution {
        public boolean hasCycle(ListNode head) {
            ListNode fast=head;
            ListNode slow=head;
            while(fast!=null && fast.next!=null){
                fast=fast.next.next;
                slow=slow.next;
                if(fast==slow){
                    return true;
                }
            }
            return false;
        }
    }
    

142. 环形链表 II

  • 题目描述

    image.png

  • 题解

    从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点

    /**
    	两个步骤
    		1.通过快慢指针的方法判断链表是否有环
    		2.如果有环,则寻找入环的第一个节点:
    				一个从起点开始的新指针start和从节点B开始的慢指针slow同步走,相遇的地方必然是入环的第一个节点A
    */
    public class Solution {
        public ListNode detectCycle(ListNode head) {
            ListNode fast=head;
            ListNode slow=head;
            while(fast!=null && fast.next!=null){
                slow=slow.next;
                fast=fast.next.next;
                if(slow==fast){
                    //有环
                    ListNode index1=fast;
                    ListNode index2=head;
                  	//两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
                    while(index1!=index2){
                        index2=index2.next;
                        index1=index1.next;
                    }
                    return index1;
                }
            }
            return null;
        }
    }
    

25. K 个一组翻转链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode reverseKGroup(ListNode head, int k) {
            if(head==null || head.next==null) return head;
            ListNode tail=head;
            for(int i=0;i<k;i++){
                if(tail==null)return head;
                tail=tail.next;
            }
          	//反转前k个元素,剩余数量小于k的话则不需要反转
          	ListNode pre=reverse(head,tail);
          	//下一轮开始的地方就是tail
          	head.next=reverseKGroup(tail,k);
          	return pre;
        }
        public ListNode reverse(ListNode head,ListNode tail){
            ListNode curr=head;
            ListNode pre=null;
            while(curr!=tail){
                ListNode temp=curr.next;
                curr.next=pre;
                pre=curr;
                curr=temp;
            }
            return pre;
        }
    }
    

21. 合并两个有序链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
            if(list1==null){
                return list2;
            }
            if(list2==null){
                return list1;
            }
            if(list1.val>list2.val){
                //当前节点谁小,就让这个较小的节点的next和另一个链表继续递归合并
                list2.next=mergeTwoLists(list1,list2.next);
                return list2;
            }else{
                list1.next=mergeTwoLists(list2,list1.next);
                return list1;
            }
        }
    }
    

203. 移除链表元素

  • 题目描述

    image.png

  • 题解

    链表操作的两种方式:

    • 直接使用原来的链表来进行删除操作
    • 设置一个虚拟头结点在进行删除操作

    移除头结点和移除其他节点的操作是不一样的,因为链表的其他节点都是通过前一个节点来移除当前节点,而头结点没有前一个节点,所以头结点如何移除呢,其实只要将头结点向后移动一位就可以,这样就从链表中移除了一个头结点

    其实可以设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head==null){
            return head;
        }
        //构建一个虚拟的头结点
        ListNode dummy=new ListNode(-1,head);
        ListNode pre=dummy;
        ListNode cur=head;
        while(cur!=null){
            if(cur.val==val){
                //删除节点,pre的next指向cur的下一个节点
                pre.next=cur.next;
            }else{
                //pre节点后移
                pre=cur;
            }
            //cur节点后移
            cur=cur.next;
        }
        //输出时,排除虚拟头节点
        return dummy.next;
    }
}

707. 设计链表

  • 题目描述

    image.png

  • 题解

    // 定义链表节点结构体(单链表)
    class ListNode{
        int val;
        ListNode next;
        ListNode(){};
        ListNode(int val){
            this.val=val;
        }
    }
    class MyLinkedList {
        //链表元素个数
        size=0;
        // 这里定义的头结点 是一个虚拟头结点,而不是真正的链表头结点
        ListNode dummy;
        public MyLinkedList() {
            size=0;
            dummy=new ListNode(0);
        }
        
        public int get(int index) {
            //如果index非法,返回-1
            if(index<0 || index>=size){
                return -1;
            }
            ListNode cur=dummy;
            //包含一个虚拟头节点,所以查找第 index+1 个节点
            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 pre=dummy;
            //找到要插入节点的前驱
            for(int i=0;i<index;i++){
                pre=pre.next;
            }
            ListNode addNode=new ListNode(val);
            addNode.next=pre.next;
            pre.next=addNode;
        }
        
        public void deleteAtIndex(int index) {
            if(index<0 || index>=size){
                return;
            }
            size--;
            ListNode pre=dummy;
            //找到要删除节点的前驱
            for(int i=0;i<index;i++){
                pre=pre.next;
            }
            pre.next=pre.next.next;
        }
    }
    

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

  • 题目描述

    image.png

  • 题解

    • 双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了
    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode() {}
     *     ListNode(int val) { this.val = val; }
     *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
     * }
     */
    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode dummy=new ListNode(-1);
            dummy.next=head;
            ListNode fast=dummy;
            ListNode slow=dummy;
            while(n>0){
                fast=fast.next;
                n--;
            }
            //找到待删除节点slow的前驱节点
            ListNode pre=dummy;
            while(fast!=null){
                pre=slow;
                slow=slow.next;
                fast=fast.next;
            }
            //绕过待删除节点slow,前驱的next指针直接指向slow.next节点
            pre.next=slow.next;
            return dummy.next;
        }
    }
    

面试题 02.07. 链表相交

  • 题目描述

    image.png

  • 题解

    简单来说,就是求两个链表交点节点的指针。 这里要注意,交点不是数值相等,而是指针相等

    目前curA指向链表A的头结点,curB指向链表B的头结点;我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置

    此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点,否则循环退出返回空指针

    /**
     * Definition for singly-linked list.
     * public class ListNode {
     *     int val;
     *     ListNode next;
     *     ListNode(int x) {
     *         val = x;
     *         next = null;
     *     }
     * }
     */
    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode curA=headA;
            ListNode curB=headB;
            int lenA=0,lenB=0;
            while(curA!=null){
                lenA++;
                curA=curA.next;
            }
            while(curB!=null){
                lenB++;
                curB=curB.next;
            }
            curA=headA;
            curB=headB;
          	//让curA为最长链表的头,lenA为其长度
            if(lenB>lenA){
                //swap lenA lenB
                int temp=lenB;
                lenB=lenA;
                lenA=temp;
                //swap curA curB
                ListNode tempNode=curB;
                curB=curA;
                curA=tempNode;
            }
            int gap=lenA-lenB;
          	//让curA和curB在同一起点上(末尾位置对齐)
            while(gap>0){
                curA=curA.next;
                gap--;
            }
          	// 遍历curA 和 curB,遇到相同则直接返回
            while(curA!=null){
                if(curA==curB){
                    return curA;
                }
                curA=curA.next;
                curB=curB.next;
            }
            return null;
        }
    }
    

翻转链表二

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode reverseBetween(ListNode head, int left, int right) {
            if(head==null) return head;
            ListNode dummy=new ListNode(-1);
            dummy.next=head;
            ListNode pre=dummy;
            //找到入口节点 cur 注意是left-1
            for(int i=0;i<left-1;i++){
                pre=pre.next;
            }
            ListNode cur=pre.next;
            //翻转
            for(int i=0;i<right-left;i++){
                ListNode temp=cur.next;
                cur.next=temp.next;
                temp.next=pre.next;
                pre.next=temp;
            }
            return dummy.next;
        }
    }
    

CodeTop系列

25. K 个一组翻转链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode reverseKGroup(ListNode head, int k) {
            if(head==null || head.next==null) return head;
            ListNode tail=head;
            for(int i=0;i<k;i++){
                if(tail==null) return head;
                tail=tail.next;
            }
            ListNode pre=reverse(head,tail);
            head.next=reverseKGroup(tail,k);
            return pre;
        }
        public ListNode reverse(ListNode head,ListNode tail){
            ListNode pre=null;
            ListNode cur=head;
            while(cur!=tail){
                ListNode temp=cur.next;
                cur.next=pre;
                pre=cur;
                cur=temp;
            }
            return pre;
        }
    }
    

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

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode swapPairs(ListNode head) {
            if(head==null) return head;
            ListNode dummy=new ListNode(-1);
            dummy.next=head;
            ListNode cur=head;
            ListNode pre=dummy;
            while(cur!=null && cur.next!=null){
                ListNode temp=cur.next.next;
                pre.next=cur.next;
                cur.next.next=cur;
                cur.next=temp;
                pre=cur;
                cur=cur.next;
            }
            return dummy.next;
        }
    }
    

面试题 02.05. 链表求和

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            int carry=0;
            ListNode dummy=new ListNode(-1);
            ListNode cur=dummy;
            while(l1!=null || l2!=null || carry!=0){
                int num1=l1==null?0:l1.val;
                int num2=l2==null?0:l2.val;
                int sum=num1+num2+carry;
                ListNode temp=new ListNode(sum%10);
                cur.next=temp;
                cur=cur.next;
                carry=sum/10;
                l1=l1==null?null:l1.next;
                l2=l2==null?null:l2.next;
            }
            return dummy.next;
        }
    }
    

2. 两数相加

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            ListNode dummy=new ListNode(-1);
            ListNode pre=dummy;
            int t=0;
            while(l1!=null || l2!=null || t!=0){
                if(l1!=null){
                    t+=l1.val;
                    l1=l1.next;
                }
                if(l2!=null){
                    t+=l2.val;
                    l2=l2.next;
                }
                pre.next=new ListNode(t%10);
                pre=pre.next;
                t/=10;
            }
            return dummy.next;
        }
    }
    

445. 两数相加 II

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            Stack<Integer> s1=buildStack(l1);
            Stack<Integer> s2=buildStack(l2);
            int carry=0;
            ListNode dummy=new ListNode(-1);
            ListNode cur=dummy;
            while(!s1.isEmpty() || !s2.isEmpty() || carry!=0){
                int num1=s1.isEmpty()?0:s1.pop();
                int num2=s2.isEmpty()?0:s2.pop();
                int sum=num1+num2+carry;
                ListNode temp=new ListNode(sum%10);
                temp.next=cur.next;
                cur.next=temp;
                carry=sum/10;
            }
            return dummy.next;
        }
    
        public Stack<Integer> buildStack(ListNode node){
            Stack<Integer> stack=new Stack<>();
            while(node!=null){
                stack.push(node.val);
                node=node.next;
            }
            return stack;
        }
    }
    

148. 排序链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        /**
         * 参考:Sort List——经典(链表中的归并排序) https://www.cnblogs.com/qiaozhoulin/p/4585401.html
         * 
         * 归并排序法:在动手之前一直觉得空间复杂度为常量不太可能,因为原来使用归并时,都是 O(N)的,
         * 需要复制出相等的空间来进行赋值归并。对于链表,实际上是可以实现常数空间占用的(链表的归并
         * 排序不需要额外的空间)。利用归并的思想,递归地将当前链表分为两段,然后merge,分两段的方
         * 法是使用 fast-slow 法,用两个指针,一个每次走两步,一个走一步,知道快的走到了末尾,然后
         * 慢的所在位置就是中间位置,这样就分成了两段。merge时,把两段头部节点值比较,用一个 p 指向
         * 较小的,且记录第一个节点,然后 两段的头一步一步向后走,p也一直向后走,总是指向较小节点,
         * 直至其中一个头为NULL,处理剩下的元素。最后返回记录的头即可。
         * 
         * 主要考察3个知识点,
         * 知识点1:归并排序的整体思想
         * 知识点2:找到一个链表的中间节点的方法
         * 知识点3:合并两个已排好序的链表为一个新的有序链表
         */
        public ListNode sortList(ListNode head) {
            return head == null ? null : mergeSort(head);
        }
    
        private ListNode mergeSort(ListNode head) {
            if (head.next == null) {
                return head;
            }
            ListNode p = head, q = head, pre = null;
            while (q != null && q.next != null) {
                pre = p;
                p = p.next;
                q = q.next.next;
            }
            pre.next = null;
            ListNode l = mergeSort(head);
            ListNode r = mergeSort(p);
            return merge(l, r);
        }
    
        ListNode merge(ListNode l, ListNode r) {
            ListNode dummyHead = new ListNode(0);
            ListNode cur = dummyHead;
            while (l != null && r != null) {
                if (l.val <= r.val) {
                    cur.next = l;
                    cur = cur.next;
                    l = l.next;
                } else {
                    cur.next = r;
                    cur = cur.next;
                    r = r.next;
                }
            }
            if (l != null) {
                cur.next = l;
            }
            if (r != null) {
                cur.next = r;
            }
            return dummyHead.next;
        }
    }
    

143. 重排链表

  • 题目描述

    image.png

  • 题解

    class Solution {
            public void reorderList(ListNode head) {
            if(head==null) return;
            ListNode l1 = head;
            //找链表中点
            ListNode mid = middleNode(head);
            ListNode l2 = mid.next;
            //断开中点
            mid.next = null;
            //反转右半段
            l2 = reverse(l2);
            //合并两个链表
            merge(l1, l2);
        }
    
        public ListNode middleNode(ListNode head) {
            ListNode fast = head;
            ListNode slow = head;
            while (fast != null && fast.next != null) {
                fast=fast.next.next;
                slow = slow.next;
            }
            return slow;
        }
    
        public ListNode reverse(ListNode head) {
            ListNode pre = null;
            ListNode cur = head;
            while (cur != null) {
                ListNode temp=cur.next;
                cur.next=pre;
                pre=cur;
                cur = temp;
            }
            return pre;
        }
    
        public void merge(ListNode l1, ListNode l2) {
            while (l1 != null && l2 != null) {
                ListNode temp1 = l1.next;
                ListNode temp2 = l2.next;
                l1.next=l2;
                l1 = temp1;
                l2.next=l1;
                l2 = temp2;
            }
        }
    }
    

138. 复制带随机指针的链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        Map<Node, Node> map = new HashMap<>();
        public Node copyRandomList(Node head) {
            if(head==null) return head;
            if(!map.containsKey(head)){
                Node node=new Node(head.val);
                map.put(head, node);
                node.next=copyRandomList(head.next);
                node.random=copyRandomList(head.random);
            }
            return map.get(head);
        }
    }
    

328. 奇偶链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode oddEvenList(ListNode head) {
            if(head==null || head.next==null) return head;
            ListNode cur1=head;
            ListNode head2=head.next;
            ListNode cur2=head2;
            while(cur1.next!=null && cur2.next!=null){
                cur1.next=cur2.next;
                cur1=cur1.next;
                cur2.next=cur1.next;
                cur2=cur2.next;
            }
            cur1.next=head2;
            return head;
        }
    }
    

23. 合并K个升序链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            if(lists.length==0) return null;
            else if(lists.length==1) return lists[0];
            else if(lists.length==2) return mergeTwoList(lists[0],lists[1]);
            else{
                int mid=lists.length/2;
                ListNode[] l1=new ListNode[mid];
                for(int i=0;i<mid;i++){
                    l1[i]=lists[i];
                }
                ListNode[] l2=new ListNode[lists.length-mid];
                for(int i=mid,j=0;i<lists.length;i++,j++){
                    l2[j]=lists[i];
                }
                return mergeTwoList(mergeKLists(l1),mergeKLists(l2));
            }
        }
        public ListNode mergeTwoList(ListNode l1,ListNode l2){
                if(l1==null) return l2;
                if(l2==null) return l1;
                if(l1.val>l2.val){
                    l2.next=mergeTwoList(l1,l2.next);
                    return l2;
                }else{
                    l1.next=mergeTwoList(l1.next,l2);
                    return l1;
                }
            }
    }
    

234. 回文链表

  • 题目描述

    image.png

  • 题解

    class Solution {
        public boolean isPalindrome(ListNode head) {
            //1、快慢指针找出链表中点
            if(head==null || head.next==null) return true;
            ListNode fast=head;
            ListNode slow=fast;
            while(fast!=null && fast.next!=null){
                fast=fast.next.next;
                slow=slow.next;
            }
            ListNode pre=null;
            if(fast!=null) slow=slow.next;  //偶数节点
            //2、反转后半部分
            while(slow!=null){
                ListNode temp=slow.next;
                slow.next=pre;
                pre=slow;
                slow=temp;
            }
            //3、比较从头结点到中间节点是否相同
            while(head!=null && pre !=null){
                if(head.val!=pre.val) return false;
                head=head.next;
                pre=pre.next;
            }
            return true;
        }
    }
    

83. 删除排序链表中的重复元素

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            ListNode cur=head;
            while(cur!=null && cur.next!=null){
                if(cur.val==cur.next.val){
                    cur.next=cur.next.next;
                }else{
                    cur=cur.next;
                }
            }
            return head;
        }
    }
    

82. 删除排序链表中的重复元素 II

  • 题目描述

    image-20220801213500272.png

  • 题解

    class Solution {
        public ListNode deleteDuplicates(ListNode head) {
            if (head == null || head.next == null) return head;
            ListNode dummy = new ListNode(-1);
            dummy.next=head;
            ListNode cur = dummy;
            while (cur.next != null && cur.next.next != null) {
                if (cur.next.val == cur.next.next.val) {
                    int x = cur.next.val;
                    while (cur.next != null && cur.next.val == x) {
                        cur.next = cur.next.next;
                    }
                } else {
                    cur = cur.next;
                }
            }
            return dummy.next;
        }
    }
    

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

  • 题目描述

    image.png

  • 题解

    class Solution {
        public ListNode removeNthFromEnd(ListNode head, int n) {
            ListNode dummy=new ListNode(-1);
            dummy.next=head;
            ListNode fast=dummy;
            ListNode slow=dummy;
            while(n>=0){
                fast=fast.next;
                n--;
            }
            while(fast!=null){
                fast=fast.next;
                slow=slow.next;
            }
            slow.next=slow.next.next;
            return dummy.next;
        }
    }
    

剑指22. 链表中倒数第k个节点

  • 题目描述

    image-20220801214216660.png

  • 题解

    class Solution {
        public ListNode getKthFromEnd(ListNode head, int k) {
            ListNode dummy = new ListNode(-1);
            dummy.next = head;
            ListNode fast = dummy;
            ListNode slow = dummy;
            while (k-- >= 0) {
                fast = fast.next;
            }
            while (fast != null) {
                fast = fast.next;
                slow = slow.next;
            }
            return slow.next;
        }
    }
    

160. 相交链表

  • 题目描述

    image.png

  • 题解

    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode l1 = headA;
            ListNode l2 = headB;
            int len1 = 0, len2 = 0;
            while (l1 != null) {
                len1++;
                l1 = l1.next;
            }
            while (l2 != null) {
                len2++;
                l2 = l2.next;
            }
            l1 = headA;
            l2 = headB;
            if (len2 > len1) {
                int temp=len2;
                len2 = len1;
                len1 = temp;
                ListNode tempNode = l2;
                l2 = l1;
                l1 = tempNode;
            }
            int gap = len1 - len2;
            while (gap-- > 0) {
                l1 = l1.next;
            }
            while (l1 != l2) {
                l1 = l1.next;
                l2 = l2.next;
            }
            return l1;
        }
    }
    

92. 反转链表 II

  • 题目描述

    image-20220801214531854.png

  • 题解

    class Solution {
        public ListNode reverseBetween(ListNode head, int left, int right) {
            if(head==null) return head;
            ListNode dummy=new ListNode(-1);
            dummy.next=head;
            ListNode pre=dummy;
            //找到反转的入口节点的前驱节点
            for(int i=0;i<left-1;i++){
                pre=pre.next;
            }
            ListNode cur=pre.next;
            for(int i=0;i<right-left;i++){
                ListNode temp=cur.next;
                cur.next=temp.next;
                temp.next=pre.next;
                pre.next=temp;
            }
            return dummy.next;
        }
    }
    

142. 环形链表 II

  • 题目描述

    image.png

  • 题解

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