python演算法-006從無序連結串列中移除重複項(HashSet空間換時間)
你的問題主要在於讀書不多而想的太多。——楊絳
這句話說的真是太對了,我一定多讀書!!!
題目:給定一個無序連結串列,例如:head->1->2>1-->3->3->5->7->6>7->8,刪除其中的重複項,將其變成head->1->2->5->7->6->8。
今天的題目與昨天的題目是相同的,昨天我們用的順序刪除法,成功的完成了這個任務。但是其採用雙重迴圈來遍歷連結串列,時間複雜度為O(N^2)。 通常情況下,為了降低時間複雜度,往往再條件允許的情況下,通過使用輔助空間來實現。 具體如下:
題目要求去除重複項,我們很容易想到Python自己的一種資料型別——集合set。集合具有無序性、 唯一性 、確定性。無序性:集合中的元素的地位相同,沒有順序。 唯一性:也叫互異性,集合元素兩兩各不相同,每個元素只能出現一次。 確定性:給定一個集合,任給一個元素,該元素或者屬於或者不屬於該集合,二者必居其一,不允許有模稜兩可的情況出現。
我們利用集合的唯一性就可以解決——“當前節點是否重複出現過”這一問題了。 我們先建立一個HashSet集合,HashSet用來儲存已經遍歷過的節點的內容。先將其初始化為空。然後從頭開始遍歷連結串列,然後判斷當前節點的內容是否在HashSet中。如果在,則把當前節點刪除;如果不在,保留當前節點,並將當前節點的內容新增到HashSet中,遍歷下一個節點,直到連結串列為空。
這種方法的時間複雜度為O(n),因為只需要遍歷一次連結串列。下面用程式碼實現:
def RemoveDup(head): """ 空間換時間 無頭節點 """ #先判斷連結串列是否為空或者 if head.next is None: return head Hashset=set()#儲存已經遍歷過的內容 Hashset.clear()#初始化集合 pre=head#指向當前節點的前驅節點,用於刪除當前節點 cur=head.next#用於遍歷連結串列,指向當前節點 #遍歷連結串列 while cur is not None: #如果當前節點的值不在HashSet中,則將其存到HashSet中 if cur.data not in Hashset: Hashset.add(cur.data) #遍歷下一個節點 cur=cur.next pre=pre.next else: #如果當前節點的值在HashSet中,則刪除節點 pre.next=cur.next cur=cur.next #返回處理完成的連結串列 return head
至於如何刪除當前節點,這個問題我在昨天的文章裡寫的很清楚了這裡就不在贅述—— python演算法-005從無序連結串列中移除重複項(順序刪除法) 。
與之前一樣先構造一個無序連結串列(昨天也講了,可以看看前面的連結),用於檢驗我們的演算法:
#先引入random庫 import random #依然是先定義節點類 class LNode(object): def __init__(self, arg): self.data = arg self.next = None #這裡!前幾篇的create我寫錯了,少了個e, #這裡改用construct 構造的意思 def construstLink(x): i = 1 head = LNode(None) tmp = None cur = head while i <= x: #這裡與之前不同,先生成一個隨機數,作為節點值 n = random.randint(0, 9) tmp = LNode(n) cur.next = tmp cur = tmp i += 1 return head
主程式:
if __name__ == '__main__': #構造連結串列 head=construstLink(10) #列印處理之前的連結串列 print("BeforeReverse:") cur = head.next while cur != None: print(cur.data) cur = cur.next #呼叫演算法,處理連結串列 head = RemoveDup(head) # 列印處理之後的連結串列 print("\nAfterReverse:") cur = head.next while cur != None: print(cur.data) cur = cur.next
下面的是執行結果:

成功的去除了3個3
你也可以多試幾次,我這演算法是對的。
這是全部的程式碼:
import random class LNode(object): """docstring for LNode""" def __init__(self, arg): self.data = arg self.next = None #這裡!前幾篇的create我寫錯了,少了個e, #這裡改用construct 構造的意思 def construstLink(x): i = 1 head = LNode(None) tmp = None cur = head while i <= x: #這裡與之前不同,先生成一個隨機數,作為節點值 n = random.randint(0, 9) tmp = LNode(n) cur.next = tmp cur = tmp i += 1 return head """ 題目描述: 將Head->1->1->3->3->5->7->7->8變 為head->1->2->5->7->8 方法:空間換時間,利用集合的無序性、唯一性 """ def RemoveDup(head): """ 空間換時間 無頭節點 """ #先判斷連結串列是否為空或者 if head.next is None: return head Hashset=set()#儲存已經遍歷過的內容 Hashset.clear()#初始化集合 pre=head#指向當前節點的前驅節點,用於刪除當前節點 cur=head.next#用於遍歷連結串列,指向當前節點 #遍歷連結串列 while cur is not None: #如果當前節點的值不在HashSet中,則將其存到HashSet中 if cur.data not in Hashset: Hashset.add(cur.data) #遍歷下一個節點 cur=cur.next pre=pre.next else: #如果當前節點的值在HashSet中,則刪除節點 pre.next=cur.next cur=cur.next #返回處理完成的連結串列 return head if __name__ == '__main__': #構造連結串列 head=construstLink(10) #列印處理之前的連結串列 print("BeforeRemoveDup:") cur = head.next while cur != None: print(cur.data) cur = cur.next #呼叫演算法,處理連結串列 head = RemoveDup(head) # 列印處理之後的連結串列 print("\nAfterRemoveDup:") cur = head.next while cur != None: print(cur.data) cur = cur.next
今天的演算法就這樣,明天講一講如何用遞迴來實現這種操作,大家可以先試一試。我目前也是在學習階段,還有存在很多問題,望各位不吝賜教。我寫這類文章,一是為了能夠更好的理解我學的東西,畢竟自己能講出來,也是很不錯了;二呢,是為了與大家分享我的學習成果,一起學習一起進步,一起走向人生巔峰(哈哈哈,當然光學這個,那是連工作都不一定能找到);還有一點就是:有一件能夠堅持的事情也是很幸福的!
當然大家有什麼需要都可以來找我,簡書號、微信公眾號:Dkider 私信、簡信,都可以找到我。
更多的問題以及文章原始碼見 github 。
要人知道自己有個祕密,而不讓人知道是個什麼祕密,等他們問,要他們猜,這是人性的虛榮。——錢鍾書《圍城》