142. Linked List Cycle II(环形链表2,找到环的入口点并且推理验证)

Given a linked list, return the node where the cycle begins. If there is no cycle, return null.

Note: Do not modify the linked list.

Follow up:
Can you solve it without using extra space?

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

说明:不允许修改给定的链表。

进阶:
你是否可以不用额外空间解决此题?

 

/**
 * Definition for singly-linked list.
 * class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */
public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null)  return null;
        ListNode runner = head;
        ListNode walker = head;
        while (runner.next != null && runner.next.next != null) {
            walker = walker.next;
            runner = runner.next.next;
            if (walker == runner) {
                ListNode walker2 = head;
                while (walker != walker2) {
                    walker = walker.next;
                    walker2 = walker2.next;
                }
                return walker; // 考虑只有一个结点自环,不能在第二个while里面判断
            }
        }
        return null;
    }
}

原因分析:

问题:为什么快慢指针在相遇时再设置一个指针walker2从头开始慢走,然后第二个指针walker在环里慢走,能够再次相遇,并且再次相遇结点就是入口结点呢?

下面我们来证明这一点:

当它遇到慢速指针walker时,快速指针runner可能会运行几个圆而不是一个圆。

假设快速指针runner运行m个圆圈,并在它们相遇时慢速指针walker运行n个圆圈。然后我们可以得到如下结论:

runner走的距离  =   walker走的距离 * 2         (因为walker每次一步,runner每次2步,距离肯定是2倍)

a + m *(b + c)+ b =  2 * (a + n *(b + c)+ b) 

因此我们化简可以得到: mb + mc =  a + (2n + 1)b +2nc

有两种可能性:

待定系数法,以b为准

m = 2n+1  -------①

m = a/c+2n   -------②

由①②得出

a=c

推出a=c成立所以在快慢指针相遇点再设置一个指针walker2从头开始慢走,第二个指针walker在环里慢走,一定能够相遇,并且再次相遇就是入口点。

待定系数法,以c为准

m = 2n                     -------------①

m = a/b + 2n + 1       ------------②

由①②得出

a = -b

距离不可能是负数,所以这种情况不成立!也不是我们所关心的结论!

综上所述,只有a=c成立!

那么,我们只需要在快慢指针相遇点再次设置一个指针从头开始走,在环里的慢指针只走一轮就一定可以和从头到环的入口点的指针相遇,并且相遇点是环形链表的入口结点。于是就有了如下部分的代码:

            if (walker == runner) {
                ListNode walker2 = head;
                while (walker != walker2) {
                    walker = walker.next;
                    walker2 = walker2.next;
                }
                return walker; // 考虑只有一个结点自环,不能在第二个while里面判断
            }

 

 

=========================Talk is cheap, show me the code========================

 

 

 

 

转载于:https://www.cnblogs.com/lcy0515/p/10807844.html

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个双向链表的Java代码: ```java public class DoublyLinkedList { private Node head; private Node tail; public DoublyLinkedList() { this.head = null; this.tail = null; } public void insertFirst(int data) { Node newNode = new Node(data); if (isEmpty()) { tail = newNode; } else { head.setPrev(newNode); } newNode.setNext(head); head = newNode; head.setPrev(tail); tail.setNext(head); } public void insertLast(int data) { Node newNode = new Node(data); if (isEmpty()) { head = newNode; } else { tail.setNext(newNode); newNode.setPrev(tail); } tail = newNode; tail.setNext(head); head.setPrev(tail); } public void deleteFirst() { if (isEmpty()) { throw new NoSuchElementException(); } if (head == tail) { head = null; tail = null; } else { head = head.getNext(); head.setPrev(tail); tail.setNext(head); } } public void deleteLast() { if (isEmpty()) { throw new NoSuchElementException(); } if (head == tail) { head = null; tail = null; } else { tail = tail.getPrev(); tail.setNext(head); head.setPrev(tail); } } public boolean isEmpty() { return head == null; } public void printList() { if (isEmpty()) { System.out.println("List is empty"); } else { Node current = head; do { System.out.print(current.getData() + " "); current = current.getNext(); } while (current != head); System.out.println(); } } private class Node { private int data; private Node next; private Node prev; public Node(int data) { this.data = data; this.next = null; this.prev = null; } public int getData() { return data; } public void setData(int data) { this.data = data; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } public Node getPrev() { return prev; } public void setPrev(Node prev) { this.prev = prev; } } } ``` 这个DoublyLinkedList类包含了双向链表的基本操作,如在链表开头和末尾插入节、删除链表开头和末尾的节、判断链表是否为空以及打印链表中的节。注意,在双向链表中,头节的prev指向尾节,尾节的next指向头节。 现在我们可以创建一个DoublyLinkedList对象并使用它的方法来操作链表。例如: ```java // 创建一个DoublyLinkedList对象 DoublyLinkedList my_list = new DoublyLinkedList(); // 在链表末尾添加节 my_list.insertLast(1); my_list.insertLast(2); my_list.insertLast(3); // 在链表开头添加节 my_list.insertFirst(0); // 打印链表中的所有节 my_list.printList(); // 删除链表中的一个 my_list.deleteLast(); // 再次打印链表中的所有节 my_list.printList(); ``` 这样,我们就可以创建、操作和打印一个DoublyLinkedList对象了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值