leetcode刷题记录总结-2.链表

news2024/9/21 8:29:53

文章目录

  • 一、重排列表
    • [1. 奇偶链表](https://leetcode.cn/problems/odd-even-linked-list/solutions/)
      • 题解
  • 二、链表的增、删、改、查
    • [203. 移除链表元素](https://leetcode.cn/problems/remove-linked-list-elements/)
      • 题解
        • 不简洁代码
        • 简洁代码
    • 707.设计链表
      • 题解
        • 不简洁代码
        • 优化后的简洁代码
    • 19.删除链表的倒数第N个节点
      • 题解
  • 三、反转(交换)链表
    • 206.反转链表
      • 题解
        • 迭代
        • 递归
    • [24. 两两交换链表中的节点](https://leetcode.cn/problems/swap-nodes-in-pairs/)
        • 题解
    • [25. K 个一组翻转链表](https://leetcode.cn/problems/reverse-nodes-in-k-group/)
      • 题解
  • 四、链表相交-环形链表
    • [面试题 02.07. 链表相交](https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/)
      • 题解1:O(n)空间
      • 题解2:O(1)空间
    • [141. 环形链表](https://leetcode.cn/problems/linked-list-cycle/)
      • 题解
    • [142. 环形链表 II](https://leetcode.cn/problems/linked-list-cycle-ii/)
      • 题解

一、重排列表

1. 奇偶链表

给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QAslc9U9-1674527294574)(https://assets.leetcode.com/uploads/2021/03/10/oddeven-linked-list.jpg)]

输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]

示例 2:

img

输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]

提示:

  • n == 链表中的节点数
  • 0 <= n <= 104
  • -106 <= Node.val <= 106

题解

  • 实质上是将链表拆分,在重组
  • 将链表分割成奇链表和偶链表要同时操作,不能用两个循环,因为奇数的指向改变了,找不到下个偶链表位置

注意

  • 偶链表最后更新,如果偶链表不为null,奇链表定不为null,最后奇一定不为null
  • 不能用奇链表为null作为循环终止,这样,奇链表连接偶链表就没办法了
class Solution {
    public ListNode oddEvenList(ListNode head) {
        if(head == null) return null;
        ListNode oddHead = head;	  // 奇链表头
        ListNode evenHead = head.next; // 偶链表的头节点
        ListNode oddCur = head, evenCur = evenHead;//改变节点指向指针
        // 一个循环内进行拆分
        // 偶链表最后更新,如果偶链表不为null,奇链表定不为null,最后奇一定不为null
        while(evenCur != null && evenCur.next != null) {
            // 先拆开奇链表
            oddCur.next = evenCur.next;
            oddCur = oddCur.next;
            // 再同时拆偶链表-在下个奇链表没有改变前,能找到偶链表位置
            evenCur.next = oddCur.next;
            evenCur = evenCur.next;
        }
        // 合并
        oddCur.next = evenHead;
        return oddHead;
    }
}

二、链表的增、删、改、查

203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

示例 1:

img

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

提示:

  • 列表中的节点数目在范围 [0, 104]
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

题解

注意:

  • 对重复节点的删除,下个指针一直往下遍历,要用while循环

不简洁代码

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null) return null;
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode pre = dummyHead; // 删除链表,要找到前驱节点
        while(pre != null && pre.next != null) {
            ListNode cur = pre.next;
            while(cur != null && cur.val == val) {// 删除重复节点
                pre.next = cur.next;
                cur = cur.next;
            }
            pre = cur;
        }
        return dummyHead.next;
    }
}

简洁代码

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null) return null;
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode pre = dummyHead; // 删除链表,要找到前驱节点
        while(pre.next != null) { // 链表中真实的每个cur
            if(pre.next.val == val) {
                ListNode cur = pre.next; // 先记录当前节点再删除
                pre.next = cur.next;     // 指向cur的指针改变
                cur.next = null;         // 清空cur指向的指针
            } else { // 连续等于val的元素全部删除后才移动pre
                pre = pre.next;
            }
        }
        return dummyHead.next;
    }
}

707.设计链表

设计链表的实现。您可以选择使用单链表或双链表。单链表中的节点应该具有两个属性:valnextval 是当前节点的值,next 是指向下一个节点的指针/引用。如果要使用双向链表,则还需要一个属性 prev 以指示链表中的上一个节点。假设链表中的所有节点都是 0-index 的。

在链表类中实现这些功能:

  • get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1
  • addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
  • addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
  • addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
  • deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。

示例:

MyLinkedList linkedList = new MyLinkedList();
linkedList.addAtHead(1);
linkedList.addAtTail(3);
linkedList.addAtIndex(1,2);   //链表变为1-> 2-> 3
linkedList.get(1);            //返回2
linkedList.deleteAtIndex(1);  //现在链表是1-> 3
linkedList.get(1);            //返回3

提示:

  • 0 <= index, val <= 1000
  • 请不要使用内置的 LinkedList 库。
  • get, addAtHead, addAtTail, addAtIndexdeleteAtIndex 的操作次数不超过 2000

题解

  • 注意节点数量的维护,特别是直接调用函数时,要记得size已经维护过了,不能重复维护

不简洁代码

class MyLinkedList {
    class Node {
        int val;
        Node next;
        public Node(){}
        public Node(int val) {
            this.val = val;
            this.next = null;
        } 
    }
    Node dummyHead; // 保护节点
    int size;       // 记录节点数量-添加时要记得维护
    public MyLinkedList() {
        dummyHead = new Node();
        this.size = 0;
    }
    
    public int get(int index) {
        if(index < 0 || index >= size) return -1;
        Node cur = dummyHead.next;
        for(int i = 0; i < index; i ++) {
            cur = cur.next;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        Node cur = new Node(val);   // 先建立新节点,再连接新节点
        Node next = dummyHead.next;
        dummyHead.next = cur;
        cur.next = next;
        size ++;
    }
    
    public void addAtTail(int val) {
        // 先找到最后一个位置
        Node cur = dummyHead;
        for(int i = 0; i < size; i ++) {
            System.out.println(cur.val);
            cur = cur.next;
        }
        // 在最后一个位置后再新建一个节点连接上
        cur.next = new Node(val);
        size ++;
    }
    
    public void addAtIndex(int index, int val) {
        if(index > size) return;
        if(index < 0) addAtHead(val); // 如果调用函数,size已经维护了
        else if(index == size) addAtTail(val);
        else {
            Node cur = dummyHead;
            for(int i = 0; i < index; i ++) {
                cur = cur.next; // 跑到index-1的位置
            }
            Node next = cur.next;       // 记录下个位置
            cur.next = new Node(val);   // 创建新节点,并创建连接
            cur.next.next = next;
            size ++;
        }
        
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index >= size) return;
        // 先找到要删除节点的前一个节点,再断开连接
        Node cur = dummyHead;  
        for(int i = 0; i < index; i ++) {
            cur = cur.next;
        }     
        Node next = cur.next;
        cur.next = next.next;
        next.next = null;
        size --;
    }
}

优化后的简洁代码

class MyLinkedList {
    class Node {
        int val;
        Node next;
        public Node(){}
        public Node(int val) {
            this.val = val;
            this.next = null;
        } 
    }
    Node dummyHead; // 保护节点
    int size;       // 记录节点数量-添加时要记得维护
    public MyLinkedList() {
        dummyHead = new Node();
        this.size = 0;
    }
    
    public int get(int index) {
        if(index < 0 || index >= size) return -1;
        Node cur = dummyHead; // 初始为虚拟头节点,遍历位置与下标相同
        for(int i = 0; i <= index; i ++) {
            cur = cur.next;
        }
        return cur.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0, val);     // 直接复用代码,size已经维护好了
    }
    
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }
    
    public void addAtIndex(int index, int val) {
        if(index > size) return;
        if(index < 0) index = 0;   // 重置0,相当于在头部添加节点
        // 为了防止忘记维护size,要先进行维护
        size ++;
        // 找到插入节点的前节点-与get逻辑相同
        Node cur = dummyHead;
        for(int i = 0; i < index; i ++) cur = cur.next;
        // 插入新节点,创建连接
        Node next = cur.next;   // 改变连接前,要先记录
        cur.next = new Node(val);
        cur.next.next = next;
    }
    
    public void deleteAtIndex(int index) {
        if(index < 0 || index >= size) return;
        // 先找到要删除节点的前一个节点,再断开连接
        Node cur = dummyHead;  
        for(int i = 0; i < index; i ++) {
            cur = cur.next;
        }
        // 断开连接     
        Node delNode = cur.next;
        cur.next = delNode.next;
        delNode.next = null;
        size --;
    }
}

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

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

img

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

**进阶:**你能尝试使用一趟扫描实现吗?

题解

  • 因为无法知道链表中一共有多少个元素,如果想使用一趟扫描,需要使用双指针
  • 双指针可以利用差值,找到要删除节点的前置节点
/**
 * 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 dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode slow = dummyHead;
        ListNode fast = dummyHead;
        // 1. fast先走n步
        while(n-- > 0) fast = fast.next; 
        // 2. 同步移动,当fast移动到最后一个位置时,往前n步的slow是前置节点
        while(fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
        // 3. 通过前置节点删除要删除的下个节点
        ListNode del = slow.next;
        slow.next = del.next;
        del.next = null;

        return dummyHead.next;
    }
}

三、反转(交换)链表

206.反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

**进阶:**链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

题解

迭代

/**
 * 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 null;
        // 需要三个指针,前,中,后,维护指针,改变指向
        ListNode pre = null;
        ListNode cur = head;
        ListNode next;
        while(cur != null) {
            // 1.先记录后面的节点
            next = cur.next;
            // 2.改变指向
            cur.next = pre;
            // 3.移动指针,处理下个节点
            pre = cur;
            cur = next;
        }
        return pre;
    }
}

递归

class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null, head);
    }
    private ListNode reverse(ListNode pre, ListNode cur) {
        if(cur == null) return pre;
        ListNode next = cur.next;
        cur.next = pre;
        return reverse(cur, next);
    }
}

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

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:

img

输入:head = [1,2,3,4]
输出:[2,1,4,3]

示例 2:

输入:head = []
输出:[]

示例 3:

输入:head = [1]
输出:[1]

提示:

  • 链表中节点的数目在范围 [0, 100]
  • 0 <= Node.val <= 100

题解

/**
 * 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 swapPairs(ListNode head) {
        if(head == null || head.next == null) return head;
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        ListNode pre = dummyHead;
        ListNode cur = head;
        while(cur != null && cur.next != null) {
            // 1.先记录要改变的节点
            ListNode right = cur.next;
            ListNode nextHead = null;
            if(right.next != null) {
                nextHead = right.next;
            }
            // 2.改变指向
            pre.next = right;
            right.next = cur;
            cur.next = nextHead;
            // 3.移动指针,进行下轮循环
            pre = cur;
            cur = nextHead;
        }
        return dummyHead.next;
    }
}

25. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:

img

输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]

示例 2:

img

输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]

提示:

  • 链表中的节点数目为 n
  • 1 <= k <= n <= 5000
  • 0 <= Node.val <= 1000

题解

链表反转,实质上,要记录好三个指针:前一个节点,当前节点,下个节点

分组反转也是,记录好三组:前一组最后一个节点,当前组头一个节点和最后一个节点,下一组头节点

  • 1.先分组,找到每组的head,end
  • 2.组内进行反转
  • 3.处理组与组之间的连接问题,即当前组的边界的前一个、后一个节点
  • 注意:
    • 注意边界,最后一组不完整
    • 设置保护节点-虚拟头节点,不仅可以找到pre,也可以保存新链表头节点的位置
class Solution {
    public ListNode reverseKGroup(ListNode head, int k) {
        if(head == null || head.next == null) return head;
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        
        ListNode preGropuEnd = dummyHead; // 前一组的尾,要连接上当前组的头
        ListNode cur = head;      // 当前组反转前的头,也是反转后的尾
        ListNode end;             // 当前组反转前的尾,也是反转后的头
        ListNode nextGropuHead;   // 后一组的头,要连接上当前组的尾

        // 迭代法遍历每个节点进行分组处理
        while(cur != null) {
            // 1. 分组---已知上一组尾,当前组头,需要找到当前组尾,下组头
            end = findEnd(cur, k);
            if(end == null) break; // 边界:没有k的长度,不做反转
            nextGropuHead = end.next;
            // 2. 对当前组进行反转
            reverse(cur, nextGropuHead);// 遍历到下组头时停止
            // 3. 连接前一组和后一组
            preGropuEnd.next = end;
            cur.next = nextGropuHead;
            // 4. 移动指针,处理下一组
            preGropuEnd = cur;
            cur = nextGropuHead;
        }
        return dummyHead.next;
    }

    private ListNode findEnd(ListNode cur, int k) {
        // 要防止链表长度小于k的情况
        while(cur != null && --k > 0) cur = cur.next;
        return cur;
    }

    private void reverse(ListNode cur, ListNode stop) {
        // 第一个节点反转后变成尾节点,指向下组,当前组逻辑不做处理
        ListNode pre = cur;
        cur = cur.next;
        ListNode next;
        while(cur != stop) {
            next = cur.next;
            cur.next = pre;
            pre = cur;
            cur = next;
        } 
    }
}

四、链表相交-环形链表

面试题 02.07. 链表相交

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null

图示两个链表在节点 c1 开始相交**:**

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ra0b7sRA-1674527294583)(assets/160_statement.png)]

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ETX9k6ek-1674527294584)(assets/160_example_1.png)]

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A[4,1,8,4,5],链表 B[5,0,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

示例 2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B7AXm8J1-1674527294584)(assets/160_example_2.png)]

输入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at '2'
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A[0,9,1,2,4],链表 B[3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5e68vB7G-1674527294585)(assets/160_example_3.png)]

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A[2,6,4],链表 B[1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 0 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交点,intersectVal == listA[skipA + 1] == listB[skipB + 1]

**进阶:**你能否设计一个时间复杂度 O(n) 、仅用 O(1) 内存的解决方案?

题解1:O(n)空间

  • 可以直接使用集合记录节点,重复的节点就是相交的节点
public class Solution {
    // 实际上找重复的第一个节点,可以用set处理
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        HashSet<ListNode> set = new HashSet<>();
        // 1. 先记录a链表的节点
        while(headA != null) {
            set.add(headA);
            headA = headA.next;
        }
        // 2. 遍历b链表的节点,查找重复的链表
        while(headB != null) {
            if(set.contains(headB)) return headB;
            headB = headB.next;
        }
        return null;
    }
}

题解2:O(1)空间

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWx2V70N-1674527294585)(assets/image-20221120214525644.png)]

证明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dJkdyJzJ-1674527294586)(assets/image-20221120214712154.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3zhA31x-1674527294586)(assets/image-20221120214733231.png)]

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    // 实际上找重复的第一个节点,可以用set处理
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA == null || headB == null) return null;
        ListNode pA = headA;
        ListNode pB = headB;
        while(pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}

141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hP7ImmAN-1674527294587)(assets/circularlinkedlist.png)]

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4TYoTjYU-1674527294587)(assets/circularlinkedlist_test2.png)]

输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gDHzMTgz-1674527294587)(assets/circularlinkedlist_test3.png)]

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

提示:

  • 链表中节点的数目范围是 [0, 104]
  • -10^5 <= Node.val <= 10^5
  • pos-1 或者链表中的一个 有效索引

题解

  • 1.可以用**哈希表记录链表遍历过的节点,**如果有重复,说明有环,空间O(n)

  • 2.也可以原地解决,使用快慢指针,如果有环,快指针一定能与慢指针相遇

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8hlTeRLd-1674527294588)(assets/34c6bd80278a4c05a713f7aa279d4f31.png)]

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null || head.next == null) return false;
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow) return true;
        }
        return false;
    }
}

142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ctgBxEol-1674527294588)(assets/circularlinkedlist-166895576765318.png)]

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jaBUBKSx-1674527294588)(assets/circularlinkedlist_test2-166895576765720.png)]

输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8qFnrYSa-1674527294588)(assets/circularlinkedlist_test3-166895576765722.png)]

输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。

提示:

  • 链表中节点的数目范围在范围 [0, 10^4]
  • -10^5 <= Node.val <= 10^5
  • pos 的值为 -1 或者链表中的一个有效索引

题解

  • 1.此题若不开额外空间,需要找规律,列数学表达式

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zRQ4yNKb-1674527294589)(assets/e384a94b6efa48cda6b361c9792a33f8.png)]

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null || head.next == null) return null;
        ListNode slow = head.next; // 先走一步,不然进不了循环
        ListNode fast = head.next.next;
        // 1. 先找到快慢指针的相遇点
        while(fast != slow && fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        // 2. 判断是否有环
        if(fast != slow) return null;
        // 3. 如果有环,根据公式,计算出环的起点
        while(head != slow) {
            head = head.next;
            slow = slow.next;
        }
        return head;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/177441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mysql主从复制配置(windows和linux操作都有)

我是目录主从复制是什么&#xff1f;操作实践验证主从设置主从复制是什么&#xff1f; mysql主从复制是一个异步的复制过程&#xff0c;底层是基于mysql数据库自带的二进制日志功能。就是一台或多台mysal数据库&#xff08;slave&#xff0c;即从库&#xff09;从另一台mysql数…

macOS Big Sur 11.7.3 (20G1116) 正式版 ISO、PKG、DMG、IPSW 下载

本站提供的 macOS Big Sur 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U 盘安装&#xff0c;或者在虚拟机中启动安装。 请访问原文链接&#xff1a;https://sysin.org/blog/macOS-Big-Sur/&#xff0…

【Python百日进阶-Web开发-Linux】Day236 - Win11安装Windows Subsystem for Android(WSA)

文章目录一、Win11运行安卓app前提条件二、Win11运行安卓app支持以下新特性三、Win11运行安卓app操作步骤3.1 修改定位3.2 开启VT虚拟化3.3 开启电脑的Hyper-V和虚拟机平台四、WSA下载4.1 百度网盘下载4.2 store.rg-adguard.net下载五、WSA安装&#xff08;没有成功&#xff0c…

C++初阶--继承

目录 继承的概念 继承定义 继承基类成员访问方式 基类和派生类对象的赋值转换 继承中的作用域 派生类的默认成员函数 友元关系不能继承 基类static成员 菱形继承与菱形虚拟继承 虚拟继承解决数据冗余和二义性的原理 继承和组合 继承的概念 继承是类层次的复用。 继…

Golang 泛型学习

Golang 泛型 今天来学习下Golang中泛型的基础知识。使用泛型&#xff0c;开发者可以声明一个函数来适应不同类型的参数&#xff0c;避免由于参数类型不一致而声明多个处理逻辑类似的函数。在本教程中&#xff0c;将声明两个简单的非泛型函数&#xff0c;然后在单个泛型函数中实…

这些实体店直播必备技巧,新手直接套用就能火!

随着直播的受众越来越广、门槛越来越低&#xff0c;入局服装直播的实体店越来越多。对于服装厂商来说&#xff0c;服装产业链越靠下游毛利率越高&#xff0c;品牌商和销售商利润远高于加工生产商&#xff0c;约在40-50%&#xff0c;而服装制造商的毛利率仅在15%左右。而对于本土…

JDK8 新特性之收集Stream流中的结果

目录 一&#xff1a;Stream流中的结果到集合中 二&#xff1a;Stream流中的结果到数组中 三&#xff1a;对流中数据进行聚合计算 四&#xff1a;对流中数据进行分组 五&#xff1a;对流中数据进行多级分组 六&#xff1a;对流中数据进行分区 七&#xff1a;对流中数据进行拼接…

8.Java循环高级综合练习-无限循环和跳转控制语句,逢七过,平方根,判断是否为质数,猜数字小游戏

文章目录前言一、无限循环1.这三种循环中哪一种无限循环是最常用的呢?2.注意事项:二、跳转控制语句三、逢七过四、平方根五、判断该整数是否为一个质数六、猜数字小游戏保底机制总结前言 一、无限循环 1.这三种循环中哪一种无限循环是最常用的呢? 当然是右上角的while循环啦…

【若依】若依字典管理页面中列表按钮功能的实现

0. 功能实现描述 1. 代码实现 ScStfController.java /*** 查询员工证书* param stfId* param modelMap* return*/ RequiresPermissions("sc:stf:cert") GetMapping("/cert/{stfId}") public String detail(PathVariable("stfId") Long stfId, …

结构型模式-组合模式

1.概述 对于这个图片肯定会非常熟悉&#xff0c;上图我们可以看做是一个文件系统&#xff0c;对于这样的结构我们称之为树形结构。在树形结构中可以通过调用某个方法来遍历整个树&#xff0c;当我们找到某个叶子节点后&#xff0c;就可以对叶子节点进行相关的操作。可以将这颗树…

谷粒学院——Day19【项目部署】

❤ 作者主页&#xff1a;欢迎来到我的技术博客&#x1f60e; ❀ 个人介绍&#xff1a;大家好&#xff0c;本人热衷于Java后端开发&#xff0c;欢迎来交流学习哦&#xff01;(&#xffe3;▽&#xffe3;)~* &#x1f34a; 如果文章对您有帮助&#xff0c;记得关注、点赞、收藏、…

Java中的hashCode,真的很容易弄懂

写这篇文章是因为在看hashMap源码时遇到有什么hashcode值&#xff0c;然后就去查&#xff0c;脑袋里面是有印象的&#xff0c;不就是在Object中有equals和hashcode方法嘛&#xff0c;这在学java基础的时候就遇到过&#xff0c;不过那时候无所谓&#xff0c;囫囵吞枣&#xff0c…

三、python基础语法进阶篇(黑马程序猿-python学习记录)

黑马程序猿的python学习视频&#xff1a;https://www.bilibili.com/video/BV1qW4y1a7fU/ 目录 一、文件操作 一、 文件的读取 1. 打开文件open() 2. 读取文件10个字节read(10) 3. 读取文件全部信息read() 4. 读取文件readLines() 5. 读取文件readLine() 6. for循环读取…

Nginx与LUA(7)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客。值此新春佳节&#xff0c;我给您拜年啦&#xff5e;祝您在新的一年中所求皆所愿&#xff0c;所行皆坦途&#xff0c;展宏“兔”&#xff0c;有钱“兔”&#xff0c;多喜乐&#xff0c;常安宁&#xff01;软件开发中&…

使用小程序+网页简易实现多客户端实时弹幕

此文主要通过小程序网页模拟多客户端通过轮询、WebSockets、订阅推送等方式简易实现实时弹幕。 实现流程1、服务端1.1、创建项目2.2、接口定义2、客户端2.1、小程序端2.2、web端3、实现方式3.1、轮询3.2、WebSocket3.3、订阅推送实现流程 1、服务端 1.1、创建项目 打开Visual…

【docker概念和实践 5】容器命令和案例(1)

一、说明 docker的四个要素是&#xff1a;本地的Docker-engine、网上&#xff08;本地&#xff09;的仓库、镜像images、容器&#xff1b;初学者必须了解这是个概念的关系。但是&#xff0c;真正重要的概念是容器&#xff0c;因为&#xff0c;只有掌握了容器&#xff0c;才能具…

SpringBoot整合SSM

添加pom依赖 <dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.18</version><scope>provided</scope></dependency><dependency><groupId>org.mybati…

macOS Monterey 12.6.3 (21G419) Boot ISO 原版可引导镜像

macOS Monterey 12.6&#xff0c;皆为安全更新&#xff0c;不再赘述。 macOS Monterey 12.6&#xff0c;发布于 2022 年 9 月 12 日&#xff08;北京时间今日凌晨&#xff09;&#xff0c;本次为安全更新。 今日&#xff08;2022-07-21&#xff09;凌晨&#xff0c;Apple 终于…

【Hadoop】HDFS高可用与高扩展原理分析(HA架构与Federation机制)

文章目录一、HDFS的高可用性&#xff08;HA架构&#xff09;二、HDFS的高扩展性&#xff08;Federation机制&#xff09;三、HA架构 Federation机制一、HDFS的高可用性&#xff08;HA架构&#xff09; 为保证HDFS的高可用性&#xff0c;即当NameNode节点机器出现故障而导致宕机…

【操作系统】—— Windows常用快捷键(带你快速了解)

&#x1f4dc; “作者 久绊A” 专注记录自己所整理的Java、web、sql等&#xff0c;IT技术干货、学习经验、面试资料、刷题记录&#xff0c;以及遇到的问题和解决方案&#xff0c;记录自己成长的点滴。 &#x1f341; 操作系统【带你快速了解】对于电脑来说&#xff0c;如果说…