判斷一個單鏈表是否有環,有環則返回入環節點,不然返回nulloop
1->2->3->4->5->6 ↑ ↓ 8<-7
例如上面這個鏈表就有環,入環節點是5ui
一般判斷鏈表是否有環,會採用快慢指針的方法,其實道理很簡單,就像兩我的賽跑且一我的跑得快一我的跑得慢。若是賽道是直的,那麼快人跑到終點時慢人還未到;若是賽道是環形,則快人和慢人總會相遇。
代碼實現this
function ListNode(x){ this.val = x; this.next = null; } function EntryNodeOfLoop(pHead){ if(pHead === null) return null; // 快慢指針從鏈表的頭部開始 var fast = pHead; var slow = pHead; while(fast.next !==null && fast.next.next !== null) { // 快指針每次走兩步;慢指針每次走一步 slow = slow.next; fast = fast.next.next; // 快慢指針相遇時,跳出while循環 if(slow === fast) break; } // 快指針已經到了鏈表尾部了還沒和慢指針相遇,說明沒有環 if(fast === null || fast.next === null) return null; // 後續會處理有環的狀況... }
常見的方法是:在肯定鏈表有環以後,慢指針從新指向鏈表頭,快指針留在相遇處;而後快慢指針再以每次移動1個節點的速度前進,最終他們在入環節點相遇。
爲何這麼作就能夠保證在入環節點相遇?證實一下:
如圖,設整個鏈表長度爲L,環長度爲R,且距離具備方向性,例如CB是C點到B點的距離,BC是B點到C點的距離,CB!=BC。當證實有環時,fast和slow都順時針到了B點,則此時:
slow走的距離:AC+CB
fast走的距離:AC+k*R+CB(k=0,1,2...)
因爲fast每次走2個節點,slow每次走1個節點,因此:
2(AC+CB) = AC+k*R+CB
AC+CB = k*R
AC+CB = (k-1)*R+R
AC = (k-1)*R+R-CB
AC = (k-1)*R+BC
從最終的表達式能夠看出來,AC的距離等於繞環若干圈後再加上BC的距離,也就是說慢指針從A點出發以速度1前進、快指針從B點出發以速度1前進,則慢指針到C點時,快指針也必然到了。
代碼實現:spa
function ListNode(x){ this.val = x; this.next = null; } function EntryNodeOfLoop(pHead){ if(pHead === null) return null; var fast = pHead; var slow = pHead; while(fast.next !==null && fast.next.next !== null) { slow = slow.next; fast = fast.next.next; if(slow === fast) break; } if(fast === null || fast.next === null) return null; // 有環,slow從新回到鏈表頭 slow = pHead; // slow和fast從新相遇時,相遇節點就是入環節點 while(slow !== fast) { slow = slow.next; fast = fast.next; } return slow; }