單鏈表有環求長度及相交問題

2021-06-17 20:30:44 字數 2968 閱讀 9110

單鏈表的節點只有乙個指標指向下乙個節點,兩個單鏈表相交的話就會導致兩個單鏈表的尾節點是相同的,所以只需要比較尾節點是否相同就可以知道兩個單鏈表是否相交。但是,這樣是否就完全沒有問題了呢?

其實不然,單鏈表有可能沒有尾節點!為什麼?因為有可能存在環!如果存在環的話上面那個辦法就行不通了!考慮到因為環的存在,原來單鏈表的尾節點消失了,既然因為環沒有尾節點了,那我們就創造乙個尾節點!

假設兩個單鏈表分別為a和b,第一步,遍歷a,使用兩個指標,乙個指標每一步移動乙個節點,另乙個指標每一步移動兩個節點(當然這裡的乙個節點和兩個節點可以替換為別的數目,只要保證兩個數的最大公約數為1就可以了,因為這個可以保證兩個指標在環中一定會相遇)。使用兩個指標遍歷鍊錶a,如果某一步兩個指標相等,那麼說明存在環,就將這一點作為a的尾節點,如果不存在環,我們最終也能獲得a的尾節點。第二步,遍歷鍊錶b,並將每乙個節點和a的尾節點相比較,如果存在相同的節點,那麼說明兩個鍊錶相交,否則不相交。使用這種方法我們最終能在o(n+m)(n為a的長度,m為b的長度)的時間複雜度內解決問題。

如果知道鍊錶a有環,那麼怎麼求出a的環的長度呢?這個問題其實非常簡單!上文中已經描述了找到鍊錶a的環中的乙個節點,也就是那個尾節點,只要繞環一周便可以計算出環的 長度。

如果鍊錶有環,怎麼求環的第乙個節點呢?我在查閱資料的時候發現有這樣乙個定理:對於有環的單鏈表a,使用乙個每次走乙個節點的指標p和乙個每次走兩個節點的指標k遍歷鍊錶a,當指標p和k第一次碰撞時,此時用乙個每次走乙個節點的指標h從單鏈表頭開始遍歷,當指標p和h相遇時一定位於環的第乙個節點!使用數學方法可以很容易來證明這個定理。設煉表的節點為o1,o2,o3,……,on,i1,i2,……,ik,……,im其中o1是鍊錶的第乙個節點,on是環的第乙個節點,ik是指標p和k發生碰撞的節點(指標p和k發生碰撞的節點是唯一的),im的next指標指向on。當指標p和k第一次相遇時,此時指標p走過了n+k個節點,而指標k走過了2n+2k個節點,可以得到這樣乙個等式:n+k = n+((n+2k) mod (m+1)),化簡可得:0 = (n+k) mod (m+1)!這個化簡後的等式說明,指標p走過n個節點會處於on節點,而指標h開始指向煉表頭,它走過n個節點也會位於on節點,雖然不知到n究竟是多少,但是指標p和h同步走一定會相交於on,而且相交的第乙個位置就是on。

有了上一段的分析,鍊錶的長度就容易得到了!有環的鍊錶,其長度可以分為兩部分來求,一部分是環外,一部是環!如果將無環鏈表看作是環長度為0的鍊錶,則可以得到乙個統一的求解鍊錶長度的**,見行93到125,其中,行99到106計算得到ik,行107到115計算環的長度,行116到122計算環外節點數目。

如果單鏈表不存在環,求解交點有兩種比較好的方法,第一種方法是將鍊錶尾節點指向其中乙個鍊錶的頭節點,這樣求交點的問題就轉換成了求環的第乙個節點問題,第二種方法效率更高一點,首先求出鍊錶a和b的長度la和lb,假設la大於lb,將每次移動乙個節點的指標pa指向鍊錶a的第la-lb+1個節點,將每次移動乙個節點的指標pb指向鍊錶b的第乙個節點,然後同步移動指標pa和pb,兩個指標相同時所指向的節點便是a和b的交點。如果單鏈表存在環的話,求解交點就麻煩一點,這裡提乙個比較簡單的辦法,對於鍊錶a/b,取環上的第乙個節點,將該節點指向鍊錶b/a的頭節點,這樣得到乙個新的有環鏈表,分別求解環的第乙個節點,可以得到兩個節點,如果兩個節點相同,說明鍊錶a和b的交點在環外,否則這兩個鍊錶在環外沒有交點,這兩個節點分別是鍊錶a和b在環上的第乙個節點!

下面貼上相關**!

#include using namespace std;

struct list ;

struct list *insert(struct list *head, int v)

else

return head;

}void set_next_node(struct list *node, struct list *next)

}struct list *find(struct list *head, size_t idx)

return head;

}struct list *first_node(struct list *head1, struct list *head2)

tail1 = cursor1;

next1 = cursor1->next;

cursor1 = cursor1->next;

} while (cursor1 != cursor2);

cursor2 = head1;

do while (cursor1 != cursor2);

if (cursor1)

tail1->next = head2;

cursor1 = head1, cursor2 = head1;

do cursor1 = cursor1->next;

} while (cursor1 != cursor2);

do if (cursor1)

} while (cursor1 != cursor2);

cursor2 = head1;

do while (cursor1 != cursor2);

tail1->next = next1;

return cursor1;

}size_t length(struct list *head)

cursor1 = cursor1->next;

} while (cursor1 != cursor2);

do if (cursor1)

} while (cursor1 != cursor2);

cursor2 = head;

do while (cursor1 != cursor2);

} return l;

}void print_length(struct list *head, size_t l)

head = head->next;

} cout << endl;

}void print(struct list *head)

int main()

有環單鏈表相交判斷

如何判斷兩個有環單鏈表是否相交?相交的話返回第乙個相交的節點,不想交的話返回空。如果兩個鍊錶長度分別為n和m,請做到時間複雜度o n m 額外空間複雜度o 1 給定兩個鍊錶的頭結點head1和head2 注意,另外兩個引數adjust0和adjust1用於調整資料,與本題求解無關 請返回乙個bool...

單鏈表相交與環問題

程式設計之美 裡面有一篇是講如何判斷兩鍊錶是否相交,讀後覺得原文太過囉嗦。於是,筆者總結了一下,此類問題可以擴充套件為兩大類,分別是 1 單鏈表與環問題 2 單鏈表相交與環問題 本文 給定兩單鏈表a b,只給出兩頭指標。請問 1 如何判斷兩單鏈表 無環 是否相交?有兩種可取的辦法 1 人為構環,將鍊...

求單鏈表長度

include include typedef struct linknode node,linklist linklist表示結構體指標 linklist createlist end int n 尾插法建立鍊錶 void showlist linklist l 輸出鍊錶內容 int linkle...