單項鍊表查詢 相交或成環的幾個演算法

2021-08-03 10:06:52 字數 2102 閱讀 2059

這篇文章介紹了單項鍊表上有關相交和成環的幾個問題和演算法。

你也可以參考有關單項鍊表的另外幾篇文章:

單項鍊表實現的棧

反轉單項鍊表的四種演算法

給定兩個單向鍊錶,判斷鍊錶是否相交(有共同的節點)。

最直接的解法,就是對其中乙個鍊錶(鍊錶一)進行遍歷,在遍歷到每乙個節點時,都在另乙個鍊錶(鍊錶二)中進行遍歷,看是否有重複的節點,假設兩個鍊錶的長度分別是 n 和 m,那麼最壞的漸進時間複雜度為 o (n * m)。

凡是檢索節點均可能進行改進。演算法一中存在對鍊錶二的多次掃瞄檢索,可以採取以下思路進行簡化:

注意:這裡所謂的「改進」有乙個前提:節點的值不能重複。也就是說,重複節點值必須能夠代表同乙個節點。

演算法二的思路基於以下兩個原理:

於是,我們給出這樣的判斷:兩個鍊錶相交,當且僅當兩個鍊錶的尾元素相同:

假設兩個鍊錶相交,很容易證明兩個鍊錶的尾元素是相同的。

逆反命題:兩個鍊錶的尾元素相同時,兩個鍊錶一定相交。(注意:相同的節點不僅僅要求值相同,而是「同乙個」,也就是指標位址相同。)

演算法思路如下:

遍歷鍊錶一,找出尾節點指標。

遍歷鍊錶二,找出尾節點指標。

如果兩個尾節點指標不為 nullptr 且相等,則兩個鍊錶相交。

這個問題是上面問題的擴充套件。通過觀察兩個相交鍊錶的形狀(y字形),我們可以看到,如果從尾部看,從尾部到第乙個交點的這部分節點是「對齊」的,而前面的長度不同。我們讓前面也「右對齊」,這樣比較是否相交節點的時候,只需要對比一次而不是遍歷另乙個節點。具體方法如下:

找出兩個鍊錶的長度 len1 和 len2. 假設 len1 > len2.

讓長的鍊錶先遍歷 len1 - len2 次。

兩個鍊錶一起遍歷,每次都判斷遍歷到的是否是同乙個節點。

給定乙個單項鍊表,判斷鍊錶中是否存在環。

遍歷單項鍊表,並對已經遍歷到的節點進行標記。每次遍歷的時候,從標記過的集合裡查詢,如果找到相同節點,說明鍊錶有環。時間複雜度為 o (n^2)。當然,也可以通過改進查詢演算法減少時間複雜度,但不會降低到 o (n)。

使用兩個指標同時遍歷鍊錶。假設這兩個指標分別叫快指標 pt***st 和慢指標 ptrslow。

慢指標使用通常的方式遍歷:每次遍歷時有

ptrslow = ptrslow -> next;

快指標則以兩倍的速度遍歷:

pt***st = pt***st -> next;

設想:如果這個鍊錶沒有環,就如同一條直直的公路,快的一方永遠不可能被慢的趕上。

但是,有環的時候,這種情況就不一樣了:

因為快的指標進入環中並且不停迴圈,快指標和慢指標將很快相遇。

這種方法的時間複雜度為 o(n)。

查詢鍊錶中指定的元素:

找到鍊錶中倒數第 k 個元素。

找到鍊錶的中間元素。

我們先從直觀上分析一下:要找到倒數第 k 個元素,必然要知道鍊錶的長度,所以至少需要 o(n) 的時間。在不借助其他空間的情況下,此時我們無法反過來知道倒數第 k 個元素,因此必須重新遍歷,所以至少需要遍歷 2n - k 次。這種演算法已經是很優秀的演算法了,已經很難在不借助空間的情況下減少遍歷了。

這個演算法只是更「優雅」,其實沒有變化:使用兩個指標 ptrfirst 和 ptrsecond。

對於第乙個問題:讓兩個指標有 k 步的遍歷步距差值,這樣當第乙個指標指向最後時,第二個指標正好指向倒數第 k 個。其實遍歷的次數和演算法一相同。網上有一些文章誤認為這種方式更高效,其實沒有太本質的變化,只有常數的變化。

具體來說,就是先讓 ptrfirst 遍歷了 k 個節點後,兩個指標再開始一起遍歷節點。

同樣道理,對於第二個問題:讓 ptrfisrt 以兩倍的速度遍歷(參見本文上面的「快指標」和「慢指標」部分),當第乙個指標遍歷到最後乙個節點的時候,第二個指標正好遍歷到中點。

思考題:

1、如何找到鍊錶 1/3 位置的節點?

2、如何找到單鏈表中環的起點和長度?(tip:可以通過將單鏈表增加一維標記位,通過遍歷將標記位 域填充;如果發現有節點已經被填充該域,則說明有環;此時,這個點就是環的起點。時間複雜度 o(n),空間複雜度 o(n)。)

建立單項鍊表,然後實現單項鍊表逆序

建立乙個任意數目的單項鍊表,每項的位置作為自己的初始資料 返回鏈頭 node initlink int num return head 輸出單項鍊表的全部資料 void display node head node curnode head while curnode.next null syste...

單項鍊表反轉

遍歷,將當前節點的下乙個節點快取後更改當前節點指標 public static node reverse node head node pre head node cur head.getnextnode node next while null cur 將原鍊錶的頭節點的下乙個節點置為null,再...

建立單項鍊表

鍊錶是動態分配儲存空間的鏈式儲存結構,其中包括乙個 頭指標 變數,頭指標中存放乙個位址,該位址指向乙個元素。鍊錶中每乙個元素稱為 節點 每個節點都由兩部分組成,即儲存資料元素的資料域和儲存直接後繼儲存位置的指標域。指標域中儲存的即是鍊錶的下乙個節點的位置,是乙個指標。多個節點構成乙個鍊錶。inclu...