有一個單向鏈表,鏈表當中有可能出現「環」,就像下圖這樣。如何用程序判斷出這個鏈表是有環鏈表?
java
方法一:首先從頭節點開始,依次遍歷單鏈表的每個節點。每遍歷到一個新節點,就從頭節點從新遍歷新節點以前的全部節點,用新節點ID和此節點以前全部節點ID依次做比較。若是發現新節點以前的全部節點當中存在相同節點ID,則說明該節點被遍歷過兩次,鏈表有環;若是以前的全部節點當中不存在相同的節點,就繼續遍歷下一個新節點,繼續重複剛纔的操做。算法
例如這樣的鏈表:A->B->C->D->B->C->D, 當遍歷到節點D的時候,咱們須要比較的是以前的節點A、B、C,不存在相同節點。這時候要遍歷的下一個新節點是B,B以前的節點A、B、C、D中剛好也存在B,所以B出現了兩次,判斷出鏈表有環。緩存
假設從鏈表頭節點到入環點的距離是D,鏈表的環長是S。那麼算法的時間複雜度是0+1+2+3+….+(D+S-1) = (D+S-1)*(D+S)/2 , 能夠簡單地理解成 O(N*N)。而此算法沒有建立額外存儲空間,空間複雜度能夠簡單地理解成爲O(1)。3d
方法二:首先建立一個以節點ID爲鍵的HashSet集合,用來存儲曾經遍歷過的節點。而後一樣是從頭節點開始,依次遍歷單鏈表的每個節點。每遍歷到一個新節點,就用新節點和HashSet集合當中存儲的節點做比較,若是發現HashSet當中存在相同節點ID,則說明鏈表有環,若是HashSet當中不存在相同的節點ID,就把這個新節點ID存入HashSet,以後進入下一節點,繼續重複剛纔的操做。指針
這個方法在流程上和方法一相似,本質的區別是使用了HashSet做爲額外的緩存。cdn
假設從鏈表頭節點到入環點的距離是D,鏈表的環長是S。而每一次HashSet查找元素的時間複雜度是O(1), 因此整體的時間複雜度是1*(D+S)=D+S,能夠簡單理解爲O(N)。而算法的空間複雜度仍是D+S-1,能夠簡單地理解成O(N)。對象
方法三:首先建立兩個指針1和2(在java裏就是兩個對象引用),同時指向這個鏈表的頭節點。而後開始一個大循環,在循環體中,讓指針1每次向下移動一個節點,讓指針2每次向下移動兩個節點,而後比較兩個指針指向的節點是否相同。若是相同,則判斷出鏈表有環,若是不一樣,則繼續下一次循環。get
例如鏈表A->B->C->D->B->C->D,兩個指針最初都指向節點A,進入第一輪循環,指針1移動到了節點B,指針2移動到了C。第二輪循環,指針1移動到了節點C,指針2移動到了節點B。第三輪循環,指針1移動到了節點D,指針2移動到了節點D,此時兩指針指向同一節點,判斷出鏈表有環。循環
此方法也能夠用一個更生動的例子來形容:在一個環形跑道上,兩個運動員在同一地點起跑,一個運動員速度快,一個運動員速度慢。當兩人跑了一段時間,速度快的運動員必然會從速度慢的運動員身後再次追上並超過,緣由很簡單,由於跑道是環形的。遍歷
假設從鏈表頭節點到入環點的距離是D,鏈表的環長是S。那麼循環會進行S次(爲何是S次,有心的同窗能夠本身揣摩下),能夠簡單理解爲O(N)。除了兩個指針之外,沒有使用任何額外存儲空間,因此空間複雜度是O(1)。
問題一:判斷兩個單向鏈表是否相交,若是相交,求出交點。
問題二:在一個有環鏈表中,如何找出鏈表的入環點?