一位小夥伴來問一道谷歌的筆試題,關於單鏈表操做的,問到底有多少種解決方案,今天咱們就來聊聊。算法
題目的大體意思是:函數
假設存在一個無序單鏈表,將重複結點去除後,並保原順序。去重前:
1→3→1→5→5→7
去重後:1→3→5→7
性能
順序刪除
經過雙重循環直接在鏈表上執行刪除操做。外層循環用一個指針從第一個結點開始遍歷整個鏈表,而後內層循環用另一個指針遍歷其他結點,將與外層循環遍歷到的指針所指結點的數據域相同的結點刪除,以下圖所示。spa
假設外層循環從outerCur開始遍歷,當內層循環指針innerCur遍歷到上圖實線所示的位置(outerCur.data==innerCur.data
)時,此時須要把innerCur指向的結點刪除。3d
具體步驟以下:指針
-
用tmp記錄待刪除的結點的地址。code
-
爲了可以在刪除tmp結點後繼續遍歷鏈表中其他的結點,使innerCur指針指向它的後繼結點:
innerCur=innerCur.next
。blog -
從鏈表中刪除tmp結點。遞歸
實現代碼以下:性能分析
運行結果:
算法性能分析
因爲這種方法採用雙重循環對鏈表進行遍歷,所以,時間複雜度爲O(N^2)
。其中,N爲鏈表的長度。在遍歷鏈表的過程當中,使用了常量個額外的指針變量來保存當前遍歷的結點、前驅結點和被刪除的結點,所以,空間複雜度爲O(1)
。
遞歸法
主要思路爲:對於結點cur,首先遞歸地刪除以cur.next爲首的子鏈表中重複的結點,接着從以cur.next爲首的子鏈表中找出與cur有着相同數據域的結點並刪除。
實現代碼以下:
算法性能分析
這種方法與方法一相似,從本質上而言,因爲這種方法須要對鏈表進行雙重遍歷,所以,時間複雜度爲O(N^2)。其中,N爲鏈表的長度。因爲遞歸法會增長許多額外的函數調用,所以,從理論上講,該方法效率比前面的方法低。
空間換時間
一般狀況下,爲了下降時間複雜度,每每在條件容許的狀況下,經過使用輔助空間實現。
具體而言,主要思路以下。
-
創建一個HashSet,HashSet中的內容爲已經遍歷過的結點內容,並將其初始化爲空。
-
從頭開始遍歷鏈表中的因此結點,存在如下兩種可能性:
-
若是結點內容已經在HashSet中,則刪除此結點,繼續向後遍歷。
-
若是結點內容不在HashSet中,則保留此結點,將此結點內容添加到HashSet中,繼續向後遍歷。
「引伸:如何從有序鏈表中移除重複項?」
如鏈表:1,三、五、五、七、七、八、9
去重後:1,三、五、七、八、9
分析與解答
上述介紹的方法也適用於鏈表有序的狀況,可是因爲以上方法沒有充分利用到鏈表有序這個條件,所以,算法的性能確定不是最優的。本題中,因爲鏈表具備有序性,所以,不須要對鏈表進行兩次遍歷。因此,有以下思路:用cur 指向鏈表第一個結點,此時須要分爲如下兩種狀況討論。
-
若是cur.data==cur.next.data,那麼刪除cur.next結點。
-
若是cur.data!=cur.next.data,那麼cur=cur.next,繼續遍歷其他結點。
總結
對於無序單鏈表中,想要刪除其中重複的結點(多個重複結點保留一個)。刪除辦法有按照順序刪除、使用遞歸方式刪除以及能夠使用空間換時間(HashSet中元素的惟一性)。
點贊越多,bug越少~