链表中环的入口结点 python_链表中环的入口结点
117
推薦
編輯于 2015-08-25 11:27:14
回復(fù)(43)
287
思路:
設(shè)置快慢指針,都從鏈表頭出發(fā),快指針每次走兩步,慢指針一次走一步,假如有環(huán),一定相遇于環(huán)中某點(diǎn)(結(jié)論1)。接著讓兩個指針分別從相遇點(diǎn)和鏈表頭出發(fā),兩者都改為每次走一步,最終相遇于環(huán)入口(結(jié)論2)。以下是兩個結(jié)論證明:
兩個結(jié)論:
1、設(shè)置快慢指針,假如有環(huán),他們最后一定相遇。
2、兩個指針分別從鏈表頭和相遇點(diǎn)繼續(xù)出發(fā),每次走一步,最后一定相遇與環(huán)入口。
證明結(jié)論1:設(shè)置快慢指針fast和low,fast每次走兩步,low每次走一步。假如有環(huán),兩者一定會相遇(因?yàn)閘ow一旦進(jìn)環(huán),可看作fast在后面追趕low的過程,每次兩者都接近一步,最后一定能追上)。
證明結(jié)論2:
設(shè):
鏈表頭到環(huán)入口長度為--a
環(huán)入口到相遇點(diǎn)長度為--b
相遇點(diǎn)到環(huán)入口長度為--c
則:相遇時
快指針路程=a+(b+c)k+b ,k>=1? 其中b+c為環(huán)的長度,k為繞環(huán)的圈數(shù)(k>=1,即最少一圈,不能是0圈,不然和慢指針走的一樣長,矛盾)。
慢指針路程=a+b
快指針走的路程是慢指針的兩倍,所以:
(a+b)*2=a+(b+c)k+b
化簡可得:
a=(k-1)(b+c)+c 這個式子的意思是: 鏈表頭到環(huán)入口的距離=相遇點(diǎn)到環(huán)入口的距離+(k-1)圈環(huán)長度。其中k>=1,所以k-1>=0圈。所以兩個指針分別從鏈表頭和相遇點(diǎn)出發(fā),最后一定相遇于環(huán)入口。 C++版:
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode*fast=pHead,*low=pHead;
while(fast&&fast->next){
fast=fast->next->next;
low=low->next;
if(fast==low)
break;
}
if(!fast||!fast->next)return NULL;
low=pHead;//low從鏈表頭出發(fā)
while(fast!=low){//fast從相遇點(diǎn)出發(fā)
fast=fast->next;
low=low->next;
}
return low;
}
}; java版:
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast=pHead;
ListNode low=pHead;
while(fast!=null&&fast.next!=null){
fast=fast.next.next;
low=low.next;
if(fast==low)
break;
}
if(fast==null||fast.next==null)
return null;
low=pHead;
while(fast!=low){
fast=fast.next;
low=low.next;
}
return low;
}
}
編輯于 2019-12-04 12:29:55
回復(fù)(46)
336
【轉(zhuǎn)】
http://kekecv.com/2016/06/08/Linked-List-Cycle-%E5%88%A4%E6%96%AD%E9%93%BE%E8%A1%A8%E6%98%AF%E5%90%A6%E6%9C%89%E7%8E%AF%EF%BC%8C%E5%A6%82%E6%9E%9C%E6%9C%89%E7%8E%AF%EF%BC%8C%E6%89%BE%E5%88%B0%E7%8E%AF%E7%9A%84%E5%85%A5%E5%8F%A3/
假設(shè)x為環(huán)前面的路程(黑色路程),a為環(huán)入口到相遇點(diǎn)的路程(藍(lán)色路程,假設(shè)順時針走), c為環(huán)的長度(藍(lán)色+橙色路程)
當(dāng)快慢指針相遇的時候:
此時慢指針走的路程為Sslow =
x + m * c + a
快指針走的路程為Sfast = x + n * c + a
2 Sslow =
Sfast
2 * ( x + m*c + a ) = (x + n *c + a)
從而可以推導(dǎo)出:
x = (n
- 2 * m )*c - a
= (n - 2 *m -1 )*c + c - a
即環(huán)前面的路程 =
數(shù)個環(huán)的長度(為可能為0) + c - a
什么是c - a?這是相遇點(diǎn)后,環(huán)后面部分的路程。(橙色路程)
所以,我們可以讓一個指針從起點(diǎn)A開始走,讓一個指針從相遇點(diǎn)B開始繼續(xù)往后走,
2個指針?biāo)俣纫粯?#xff0c;那么,當(dāng)從原點(diǎn)的指針走到環(huán)入口點(diǎn)的時候(此時剛好走了x)
從相遇點(diǎn)開始走的那個指針也一定剛好到達(dá)環(huán)入口點(diǎn)。
所以2者會相遇,且恰好相遇在環(huán)的入口點(diǎn)。
最后,判斷是否有環(huán),且找環(huán)的算法復(fù)雜度為:
時間復(fù)雜度:O(n)
空間復(fù)雜度:O(1)
下面的是斷鏈法。不過題目要求不允許修改鏈表時就尷尬了。
編輯于 2017-03-11 10:47:33
回復(fù)(67)
379
第一步,找環(huán)中相匯點(diǎn)。分別用p1,p2指向鏈表頭部,p1每次走一步,p2每次走二步,直到p1==p2找到在環(huán)中的相匯點(diǎn)。
第二步,找環(huán)的入口。接上步,當(dāng)p1==p2時,p2所經(jīng)過節(jié)點(diǎn)數(shù)為2x,p1所經(jīng)過節(jié)點(diǎn)數(shù)為x,設(shè)環(huán)中有n個節(jié)點(diǎn),p2比p1多走一圈有2x=n+x;
n=x;可以看出p1實(shí)際走了一個環(huán)的步數(shù),再讓p2指向鏈表頭部,p1位置不變,p1,p2每次走一步直到p1==p2;
此時p1指向環(huán)的入口。
public class Solution {
ListNode EntryNodeOfLoop(ListNode pHead){
if(pHead == null || pHead.next == null)
return null;
ListNode p1 = pHead;
ListNode p2 = pHead;
while(p2 != null && p2.next != null ){
p1 = p1.next;
p2 = p2.next.next;
if(p1 == p2){
p2 = pHead;
while(p1 != p2){
p1 = p1.next;
p2 = p2.next;
}
if(p1 == p2)
return p1;
}
}
return null;
}
}
發(fā)表于 2015-09-15 15:23:04
回復(fù)(65)
65
HashSet set = new HashSet();
while (pHead != null) {
if (!set.add(pHead)) {
return pHead;
}
pHead = pHead.next;
}
returnnull;
發(fā)表于 2015-10-07 12:45:55
回復(fù)(34)
32
//左神講的
//先說個定理:兩個指針一個fast、一個slow同時從一個鏈表的頭部出發(fā)
//fast一次走2步,slow一次走一步,如果該鏈表有環(huán),兩個指針必然在環(huán)內(nèi)相遇
//此時只需要把其中的一個指針重新指向鏈表頭部,另一個不變(還在環(huán)內(nèi)),
//這次兩個指針一次走一步,相遇的地方就是入口節(jié)點(diǎn)。
//這個定理可以自己去網(wǎng)上看看證明。
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
ListNode fast = pHead;
ListNode slow = pHead;
while(fast != null && fast.next !=null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow)
break;
}
if(fast == null || fast.next == null)
return null;
fast = pHead;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
發(fā)表于 2017-08-17 11:29:22
回復(fù)(13)
114
/*
時間復(fù)雜度為O(n),兩個指針,一個在前面,另一個緊鄰著這個指針,在后面。
兩個指針同時向前移動,每移動一次,前面的指針的next指向NULL。
也就是說:訪問過的節(jié)點(diǎn)都斷開,最后到達(dá)的那個節(jié)點(diǎn)一定是尾節(jié)點(diǎn)的下一個,
也就是循環(huán)的第一個。
這時候已經(jīng)是第二次訪問循環(huán)的第一節(jié)點(diǎn)了,第一次訪問的時候我們已經(jīng)讓它指向了NULL,
所以到這結(jié)束。
*/
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if (!pHead->next)
return NULL;
ListNode* previous = pHead;
ListNode* front = pHead ->next;
while (front)
{
previous->next = NULL;
previous = front;
front = front->next;
}
return previous;
}
};
編輯于 2016-01-26 16:36:01
回復(fù)(39)
15
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
set s;
ListNode* node = pHead;
while(node!=NULL){
if(s.insert(node).second)
node = node->next;
else
return node;
}
return node;
}
};
我這里用到了STL中的set,set有一個特性就是不能插入相同元素,這樣只需遍歷原List一次就可以判斷出有沒有環(huán),還有環(huán)的入口地址。s.insert(node).second這里在插入的同時也判斷了插入是否成功,如果不成功表明set中已經(jīng)有該元素了,該元素就是環(huán)的入口元素。
發(fā)表于 2016-07-23 18:20:48
回復(fù)(7)
30
python solution: # -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
slow,fast=pHead,pHead
while fast and fast.next:
slow=slow.next
fast=fast.next.next
if slow==fast:
slow2=pHead
while slow!=slow2:
slow=slow.next
slow2=slow2.next
return slow
發(fā)表于 2017-10-07 19:25:27
回復(fù)(14)
24
思路:leetcode上也有這道題,具體思想是,兩個指針fast和slow,fast以slow兩倍速度前進(jìn),
如果沒有環(huán),那么fast和slow不會相遇此時返回null;如果有環(huán),那fast和slow肯定會再次相遇
相遇的時候,fast剛好比slow多走了一圈環(huán)的長度。
用圖來描述下,當(dāng)fast與slow相遇時,fast走過的距離為a + b + c + b,而slow走過的距離為
a + b,因?yàn)閒ast是slow速度的兩倍,則有a+b+c+b = 2*(a+b),登出a=c;此時slow節(jié)點(diǎn)所處X處
到環(huán)起點(diǎn)Y處的距離a和X節(jié)點(diǎn)到Y(jié)處距離c其實(shí)是相等的,此時第三個指針p從x處,以和slow指針
相同的速度前進(jìn),當(dāng)它兩相遇時,即為環(huán)的起點(diǎn)Y處!
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
ListNode fast = pHead;
ListNode slow = pHead;
while(fast !=null && fast.next !=null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
ListNode p = pHead;
while( p != slow) {
p = p.next;
slow = slow.next;
}
return p;
}
}
return null;
}
}
編輯于 2017-05-18 13:02:17
回復(fù)(9)
13
要尋找環(huán)的入口節(jié)點(diǎn),遍歷節(jié)點(diǎn)的時候,遇到的第一個重復(fù)節(jié)點(diǎn)肯定入環(huán)節(jié)點(diǎn),所以定義一個Set,
添加失敗時 即返回入口節(jié)點(diǎn)
import java.util.*;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead)
{
if(pHead==null)
return null;
ListNode pNode=pHead;
HashSet pSet = new HashSet();
while(pNode!=null){
if(!pSet.add(pNode))
return pNode;
pNode=pNode.next;
}
return null;
}
}
發(fā)表于 2016-06-18 10:34:08
回復(fù)(6)
8
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
linkls = []
while pHead:
if pHead in linkls:
return pHead
linkls.append(pHead)
pHead = pHead.next
return None
發(fā)表于 2018-07-05 19:59:29
回復(fù)(13)
5
先貼代碼,再解釋; public ListNode EntryNodeOfLoop(ListNode pHead)
{
if (pHead == null || pHead.next == null)
return null;
//step1 發(fā)現(xiàn)環(huán)
ListNode walk = pHead, run = pHead;
do {
walk = walk.next;
run = run.next.next;
} while (walk != run);
//step2 找到了環(huán),并且run正在環(huán)內(nèi)
//另起一個quickWalk從pHead開始走,quickWalk和run再次相遇的地點(diǎn)就是環(huán)的入口
ListNode quickWalk = pHead;
while (quickWalk != run) {
run = run.next.next;
quickWalk = quickWalk.next.next;
}
return quickWalk;
}
}第一步發(fā)現(xiàn)環(huán),并找到了walk指針和run指針相遇的地點(diǎn)
第二步另起一個quickWalk指針,和run指針保持同步,倆指針相遇點(diǎn)即為入口;
解釋:
如圖,每個距離的含義: S:環(huán)入口到walk和run相遇點(diǎn)的距離
L:walk和run相遇點(diǎn)到入口的距離
P:頭結(jié)點(diǎn)到環(huán)入口的距離
設(shè)walk的速度是v,則run的速度是2v;
設(shè)相遇花費(fèi)了t時間,walk走過的路程為Swalk
根據(jù)題意有: 2vt - vt = vt,
又相遇的時候:vt = k(S+L)--①成立;
且 Swalk = vt = P + m(S+L) + S--②成立;
由①②可得 k(S+L) = P + m(S+L) + S
變形為:P = (k-m-1)(S+L) + L。
設(shè) q = (k-m-1),最終得到 P = q(S+L) + L。
因此分別從相遇地點(diǎn)、頭結(jié)點(diǎn)同時以相同的速度走的倆指針,最終相遇地點(diǎn)一定是環(huán)的入口。
PS:最后兩個同步指針的速度應(yīng)該是越快越好,因此,采用另起一個quickWalk指針和run同步,而不是和walk同步。
編輯于 2017-12-18 15:45:35
回復(fù)(1)
4
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if(pHead == null || pHead.next == null) return null;
ListNode slow = pHead;
ListNode fast = pHead;
while (fast != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) break;
}
fast = pHead;
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
發(fā)表于 2017-04-26 15:38:04
回復(fù)(0)
6
'''
解法:
第一步,先找到環(huán)中的一個節(jié)點(diǎn)(找到之后就可以計算出環(huán)中的節(jié)點(diǎn)數(shù)目)
讓一個指針fast走快一點(diǎn),一個指針slow走慢一點(diǎn);當(dāng)fast與slow相遇時,即fast比slow多一圈,此時相遇的節(jié)點(diǎn)肯定是環(huán)內(nèi)的節(jié)點(diǎn)
第二步,根據(jù)環(huán)中的節(jié)點(diǎn)來計算環(huán)中節(jié)點(diǎn)總數(shù)
從此節(jié)點(diǎn)遍歷到此節(jié)點(diǎn)結(jié)束,即可得到節(jié)點(diǎn)總數(shù)
第三步,知道了環(huán)內(nèi)節(jié)點(diǎn)總數(shù),來找到環(huán)入口
先讓一個指針p1從根節(jié)點(diǎn)開始往后走m步,然后再讓一個節(jié)點(diǎn)p2指向頭結(jié)點(diǎn);
然后讓p1和p2同時往后移動,當(dāng)p1與p2相交時,此時的點(diǎn)就是環(huán)的入口節(jié)點(diǎn)
'''
class Solution:
def EntryNodeOfLoop(self, pHead):
# write code here
meet_node = self.MeetNode(pHead)
if meet_node == None:
return None
# 得到環(huán)中的節(jié)點(diǎn)個數(shù)
loop_nodes = 1
p1 = meet_node
while p1.next != meet_node:
loop_nodes += 1
p1 = p1.next
# 目前已經(jīng)得到了環(huán)中節(jié)點(diǎn)個數(shù)m,和環(huán)中個一個節(jié)點(diǎn)meetnode,如何找到環(huán)的入口?
# 一個指針p1從根節(jié)點(diǎn)開始往后走m步,然后再讓一個節(jié)點(diǎn)p2指向頭結(jié)點(diǎn),p1和p2同時往后移動,當(dāng)p1與p2相交時,此時的點(diǎn)就是環(huán)的入口節(jié)點(diǎn)
p1 = pHead
for i in range(loop_nodes):
p1 = p1.next
p2 = pHead
while p1 != p2:
p1 = p1.next
p2 = p2.next
return p1
def MeetNode(self, pHead):
if pHead == None:
return None
slow = pHead.next
if slow == None:
return None
fast = slow.next
while slow != None and fast != None:
if slow == fast:
return slow
slow = slow.next
fast = fast.next
if slow != fast:
fast = fast.next
return None
編輯于 2018-05-19 14:52:05
回復(fù)(3)
6
//時間復(fù)雜度:O(n),用map對訪問過的節(jié)點(diǎn)做標(biāo)記,這樣如果訪問一個節(jié)點(diǎn)兩次,表明找到了環(huán)入口,否則沒有環(huán)。 表示map好好用啊~~
class EntryNodeOfLoop
{
public:
ListNode* EntryNodeOfLoop_Solution(ListNode* pHead)
{
if(pHead==nullptr) return nullptr;
ListNode *ptmp=pHead;
map mlist; //用map關(guān)聯(lián)各個鏈表指針和對應(yīng)的映射值
while(ptmp!=nullptr && mlist[ptmp]!=1)
{
mlist[ptmp]=1;//訪問過該鏈表節(jié)點(diǎn),就將實(shí)值改為1
ptmp=ptmp->next;
}
if(ptmp==nullptr) return nullptr; //沒有環(huán)
else return ptmp; //入口節(jié)點(diǎn)
}
};
發(fā)表于 2017-05-22 22:29:30
回復(fù)(3)
6
劍指offer 上面提到了一種很巧妙的方法:
1. 知道環(huán)的長度 len
2. 讓一個結(jié)點(diǎn)先走 len 然后讓另一個 和先走的那個一起走,兩者必然會相遇。
問題歸結(jié)為,計算len.
整體流程:
1. 用快慢指針求相遇的結(jié)點(diǎn)(如果不存在相遇的結(jié)點(diǎn),返回null 不用后面的計算)
2. ?求len
3. 求入口節(jié)點(diǎn)。
我們這里使用HashMap , 效率比 ArrayList 效率高點(diǎn)。
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
import java.util.HashMap;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead){
ListNode node = pHead;
HashMap map = new HashMap<>();
while(node!=null){
if(map.containsKey(node)){
return node;
}else{
map.put(node,true);
node = node.next;
}
}
return null;
}
}
發(fā)表于 2016-06-01 16:31:14
回復(fù)(3)
2
class?Solution:
def?EntryNodeOfLoop(self,?pHead):
a?=?[]
cur?=?pHead
while?cur:
if?cur?not?in?a:
a.append(cur)
cur?=?cur.next
else:
return?cur
return
發(fā)表于 2020-02-29 09:54:31
回復(fù)(0)
2
# 這樣把經(jīng)過的節(jié)點(diǎn)記錄下來就可以了呀 class Solution:
def EntryNodeOfLoop(self, pHead):
l = []
p = pHead
res = None
while p:
if p in l:
res = p
break
l.append(p)
p = p.next
return res
編輯于 2019-07-18 09:08:16
回復(fù)(1)
2
package entryNodeOfLoop;
import java.util.HashSet;
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) {
if (pHead == null) {
return null;
}
ListNode p = pHead;
HashSet set = new HashSet<>();
while (set.add(p)) {
if (p.next != null) {
p = p.next;
} else {
return null;
}
}
return p;
}
}
發(fā)表于 2018-03-10 21:29:48
回復(fù)(0)
2
//快慢指針。題目中明明說了有環(huán)。可是為何還要判斷下?
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
ListNode *slow = pHead;
ListNode *fast = pHead;
do{
if(fast == NULL || fast->next==NULL)
return NULL;
fast = fast->next->next;
slow = slow->next;
}while(slow != fast);
slow = pHead;
while(slow != fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
};
發(fā)表于 2017-06-07 14:48:40
回復(fù)(4)
總結(jié)
以上是生活随笔為你收集整理的链表中环的入口结点 python_链表中环的入口结点的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 划痕麻点检测程序_精密外观检测机设计
- 下一篇: 单文档应用程序弹出新对话框_简介——文档