© Profit_Image/Shutterstock.com
您可能對數組有所了解,但您可以在編程中使用許多數據結構。在某些方面,鍊錶類似於數組,但存在一些關鍵差異。了解什麼是鍊錶,探索不同類型的鍊錶,並了解如何在本文中使用鍊錶。
什麼是鍊錶?
就像數組一樣,線性列表是一種線性的數據結構。但區別在於它們存儲和訪問數據的方式。數組將元素存儲在連續的內存塊中,而鍊錶包含作為節點的元素,每個節點指向列表中的下一個元素。因此,它們不是連續存儲的,並且不使用索引訪問元素。相反,指針被分配給每個節點,指示序列中的下一個節點。與動態數組一樣,可以根據需要通過添加或刪除節點來修改鍊錶。
有什麼好處?
現在我們已經詳細介紹了什麼是鍊錶,您可能想知道為什麼要想用一個。這裡有幾個優點:
除非你使用動態數組,否則數組的大小往往是固定的,而鍊錶可以根據需要增加或減少。插入或刪除元素需要恆定的時間,而不是數組其他元素需要移動的地方。鏈接列表往往更容易滿足您的內存限制,尤其是對於大量數據。這是因為內存僅用於所需的節點,但數組的連續性質意味著您可能正在支持一些操作不需要的數據。使用鍊錶更容易維護數據的持久性,因為數據可以比在數組中更容易序列化。這使得在多個程序之間或程序終止後傳輸數據變得更簡單。在你有稀疏數據的地方,即空元素,數組仍然會存儲這些數據。然而,鍊錶只存儲非空值。
有哪些不同類型的鍊錶?
您可以使用多種不同類型的鍊錶,每種類型都有自己的特點。我們將在這裡簡要介紹一下,以及如何在 Python 中實現它們。
簡單鍊錶
如您所料,這是最簡單的鍊錶類型,您只能在一個方向上遍歷鍊錶。每個指針都指向下一個節點,最後一個節點指向空或 NULL。這也稱為單鍊錶。 Python實現一個簡單鍊錶的代碼如下:
class Node: def __init__(self, data): self.data=data self.next=None head=None def add(self, data): new_node=Node(data) if self.head is None: self.head=new_Node else: current_node=self.head while current_node.next is not None: current_node=current_node.next current_node.next=new_node def remove(self, data): 如果 self.head 為 None: 返回not None: if current_node.next.data==data: current_node.next=current_node.next.next 返回 def display(self): current_node=self.head while cur rent_node 不是無: print(current_node.data) current_node=current_node.next my_list=LinkedList() my_list.add(1) my_list.add(2) my_list.add(3) my_list.display() my_list.remove(2) my_list.display
解釋
這是一個相當廣泛的代碼塊,但不太難理解。
首先,我們定義“Node”和“LinkedList” ”類。 “Node”類表示列表中的一個節點,每個節點都有一個指針(由“self.next”指示)。該列表由“LinkedList”表示,我們實現了一個頭節點,可以更輕鬆地插入或刪除元素。
“add”函數表示要添加一個新節點到列表的末尾。如果列表為空,則將頭節點設置為創建的新節點。如果它不為空,則遍歷列表直到最後一個節點並將其指向新節點。
“刪除”功能通過刪除列表中的第一個節點來工作。如果這是頭節點,則下一個節點成為頭節點。如果頭節點沒有給定的數據,則遍歷列表以找到它。一旦它找到具有給定數據的節點,它的“next”屬性被設置為此之後的節點,這將從列表中刪除該節點。
“Display”遍歷列表並打印找到的數據每個節點。
列表中的數據在末尾用“LinkedList”類定義,數據值為 1、2 和 3 的節點。節點 2 已被刪除以顯示刪除過程,並且兩個列表都被打印出來作為輸出。查看屏幕截圖以了解其工作原理。
列表中的數據在末尾定義了“LinkedList”類,數據值為1、2、3的節點。
©”TNGD”.com
Doubly Linked List
這類似於一個簡單的列表,除了遍歷可以在兩個方向上發生,向前和向後。要實現這一點,我們可以使用以下代碼:
class Node: def __init__(self, data): self.data=data self.prev=None self.next=None class DoublyLinkedList: def __init__(self): self.head=None self.tail=None def add(self,data): new_node=Node(data) 如果 self.head 是 None: self.head=new_node self.tail=new_node else: new_node.prev=self.tail self.tail.next=new_node self.tail=new_node def remove(self, data): current_node=self.head while current_node is not None: 如果 current_node.data==data: 如果 current_node.prev 不是 None: current_node.prev.next=current_node.next else: self.head=current_node.next 如果 current_node.next 不是 None: current_node.next.prev=current_node.prev否則:self.tail=current_node.prev return current_node=current_node.next def display(self): current_node=self.head while current_node is not None: print(current_node.data) current_node=current_node.next my_list=DoublyLinkedList() my_list. add(1) my_list.add(2) my_list.add(3) my_list.display() my_list.remove(3) my_list.display()
說明
很多代碼在這example 類似,但有一些不同。
我們以類似的方式定義“Node”類,但這次有對上一個節點和下一個節點的引用。類似地,“DoublyLinkedList”類表示列表,但具有同時充當列表的頭部和尾部的實例。
像以前一樣,“add”函數將一個新節點添加到列表的末尾。但如果列表為空,則頭和尾都設置為這個新節點。當列表不為空時,將新節點的“prev”屬性設置為當前尾部,將當前尾部的“next”屬性設置為新節點,並將尾部更改為新節點。
“刪除”函數刪除列表中具有給定數據的第一個節點。更新相鄰節點的“prev”和“next”屬性以避免當前節點,有效地將其從列表中刪除。如果有問題的節點是頭部或尾部,那麼這個屬性也會更新以反映這一點。
最後,“display”和“my_list”功能基本相同,但這次我們已經刪除了數據值為 3 的節點。屏幕截圖說明了代碼執行情況。
“添加”函數將一個新節點添加到列表的末尾。
©”TNGD”.com
“display”和“my_list”功能基本相同,但這次我們’刪除了數據值為 3 的節點。
©”TNGD”.com
Circular Linked Lists
另一種類型的鍊錶是循環列表。顧名思義,這是一個沒有“結束”的列表。結束節點連接回起始節點。示例如下所示:
class Node: def __init__(self, data): self.data=data self.next=None class CircularLinkedList: def __init__(self): self.head=None def add (self, data): new_node=Node(data) if self.head is None: self.head=new_node new_node.next=self.head else: current_node=self.head while current_node.next is not self.head: current_node=current_node.next current_node.next=new_node new_node.next=self.head def remove(self,data): 如果 self.head 為 None: return if self.head.data==data: current_node=self.head while current_node.next不是 self.head: current_node=current_node.next current_node.next=self.head.next self.head=self.head.next else: current_node=self.head while current_node.next is not self.head: if current_node.next.data==data: current_node.next=current_node.next.next return current_node=current_node.next def display(self): 如果 self.head 是無:返回 current_node=self.head 而 True:print(current_node.data) current_node=current_node.next 如果 current_node 是 self.head:break my_list=CircularLinkedList() my_list.add(1) my_list.add(2) my_list.add (3) my_list.display()
說明
這裡有幾個關鍵的區別。 “new_node.next=self.head”表示添加的新節點是列表中的最後一個節點,並指向列表的頭部,即第一個節點。
“ while current_node.next is not self.head” 行用於遍歷鍊錶並添加元素。因為沒有真正的結束,就像單鍊錶一樣,我們必須檢查當前節點是否是頭節點,而不是它是否簡單地非零。這是為了避免改變頭節點。
“new_node.next=self.head”代碼確保添加到列表中的節點與頭節點連接。
“remove”函數的工作原理類似,如果給定數據包含在第一個節點中,則將其刪除,下一個節點成為新的頭節點。如果頭節點不包含給定的數據,則像以前一樣遍歷列表,直到找到數據。然後將“下一個”屬性分配給該節點之後的節點,從而跳過該節點。總的來說,功能是一樣的,但是由於列表的循環性質,實現稍微複雜一些。
“顯示”功能也非常相似,除了我們必須檢查我們’從頭節點開始,一旦我們再次到達這一點就中斷遍歷。
‘new_node.next=self.head’代碼確保添加到列表的節點與頭節點相連。
©”TNGD”.com
“顯示”功能也差不多,但是我們必須從頭節點開始,再到這個點就中斷遍歷。
©”TNGD”.com
雙循環鍊錶
因為我們已經看過單鍊錶、雙鍊錶和循環鍊錶,所以它只有意義e 以雙循環鍊錶結束。滿嘴的,但不是很難以捉摸。意料之中,這是指一個循環鍊錶,我們可以向前和向後兩個方向遍歷。這種類型的列表的實現可以這樣說明:
: self.head=None def add(self, data): new_node=Node(data) 如果 self.head 是 None: new_node.next=new_node new_node.prev=new_node self.head=new_node else: last_node=self.head. prev last_node.next=new_node new_node.prev=last_node new_node.next=self.head self.head.prev=new_node def remove(self,data):如果 self.head 為 None:返回 current_node=self.head 而 current_node.data !=data: current_node=current_node.next if current_node==self.head: return if current_node==self.head: se lf.head=current_node.next current_node.prev.next=current_node.next current_node.next.prev=current_node.prev def display(self): 如果 self.head 為 None: return current_node=self.head while True: print(current_node.data) current_node=current_node.next if current_node==self.head: break my_list=DoubleCircularLinkedList() my_list.add(1) my_list.add(2) my_list.add(3) my_list.display()
說明
雙循環鍊錶和標準循環鍊錶的主要區別在於,與之前的非循環鍊錶一樣,雙循環鍊錶將每個節點指向前一個節點和下一個節點。這是通過在添加節點時更新最後一個節點和新節點的“prev”和“next”屬性來實現的。當刪除節點時,需要更新被刪除節點和相鄰節點的這些屬性。如果您要刪除頭節點,則需要更新最後一個節點的指針以反映這一點。
雙列表將每個節點指向前一個節點以及下一個節點。
©”TNGD”.com
如果要刪除頭節點, 然後需要更新最後一個節點的指針以反映這一點。
©”TNGD”.com
總結
許多類型的鍊錶都可以用作數組的替代方法,例如當您需要定期插入和刪除元素或有內存限制時。儘管數組在某些操作方面執行得更快,但鍊錶並非沒有其獨特的優勢,因此了解它們的工作原理很有用。
下一步…
鍊錶Data Structure and How to Use It FAQs(常見問題解答)
什麼是鍊錶?
鍊錶是一種數據結構,其中數據存儲在“節點”中,而不是像數組那樣連續存儲。它們可以是簡單的或循環的,其中後者涉及指向頭節點的最後一個節點。您還可以實現雙向鍊錶,其中可以向前和向後遍歷列表。
鍊錶的優點和缺點是什麼?
當您需要頻繁插入和刪除數據或有內存限制時,鍊錶可以比數組更好。但是,當您需要使用二進制搜索時,數組可能會更好,並且因為它們是連續的,所以它們對緩存更友好。雖然插入元素更快,但訪問特定元素可能會更慢,因為您必須遍歷整個列表。
有哪些不同類型的鍊錶?
鍊錶的類型包括簡單鍊錶和雙向鍊錶,循環鍊錶和雙向循環鍊錶。
如何在鍊錶中插入或刪除節點?
要插入一個節點,必須創建新節點並將該節點的“next”屬性指向列表中的下一個節點,同時將前一個節點的“next”屬性指向該節點新節點。要刪除節點,您必須遍歷列表以找到給定的數據。然後,您需要將前一個節點的“next”屬性指向要刪除的節點之前的節點,以便在列表中跳過它。
如何遍歷一個鍊錶?
要遍歷列表,您從頭節點開始並移動到序列中的每個節點,直到到達結束節點。
鍊錶操作的時間複雜度是多少?
每個操作都有自己的時間複雜度。時間複雜度為 O(1) 的操作包括在開始處插入一個元素和在開始處刪除一個元素。大多數其他操作的複雜度為 O(n)。這包括訪問元素、在末尾插入或刪除元素、在特定索引處插入或刪除元素以及搜索特定元素。這是因為必須遍歷列表,所以復雜度取決於列表的大小。
有哪些替代鍊錶的方法?
代替對於鍊錶,您可以使用數組,其中數據存儲在連續塊中。哈希表用於將鍵映射到數組中的索引,因此它們可以相互結合使用。或者,您可以使用樹,其中節點以分層格式連接。堆是一種二叉樹,是另一種選擇。最後,堆棧和隊列可以與數組或鍊錶一起使用,雖然它們不像某些選項那樣靈活,但確實提供了對元素的有效修改。
有哪些應用程序鏈接列表?
鏈接列表在您不知道列表的大小、想要在圖像和音樂處理中實現更多數據結構以及表示圖形和樹的情況下很有用。