資料結構之 鍊錶

2021-09-10 10:55:30 字數 1609 閱讀 1579

單鏈表

鍊錶通過將一組零散的記憶體塊串聯到一起,其中我們把記憶體塊稱為鍊錶的「結點」。為了將所有的結點串起來,每個鍊錶的結點除了儲存資料之外,還需要記錄鏈上的下乙個結點的位址。把這個記錄下乙個結點位址的指標叫做後繼指標next

和陣列相反,插入刪除對於鍊錶來說非常高效,因為鍊錶本來儲存空間就不是連續的。但是,想要隨機訪問第k個元素,就沒有陣列高效了。他不能像陣列那樣,根據首位址和小標,定址就能計算出對應記憶體位址。而是需要根據指標的乙個結點乙個結點地依次遍歷,直到找到響應的節點。

可以想象成煉表是幼兒園放學回家的小朋友的隊伍,隊伍中的每個人只知道自己的後面那個人是誰,所以當需要直到第k個人是誰,就要乙個乙個往下數,需要o(n)時間複雜度

迴圈鍊錶

迴圈鍊錶是一種特殊的單鏈表。單鏈表的尾結點指標指向空位址,表示最後的結點。而迴圈鍊錶的尾結點指標是指向鍊錶的頭結點。就像乙個環一樣首尾相連。

和單鏈表相比,迴圈鍊錶的優點是從鏈尾到鏈頭比較方便。當要處理資料具有喚醒結構的時候,迴圈鍊錶比較適合。

雙向鍊錶

單向鍊錶只有乙個方向,節點只有乙個後繼指標next指向後面的結點。而雙向鍊錶支援兩個方向,每個結點不止有乙個後續指標next指向後面的結點,還有乙個前驅指標prev指向前面的結點。

雙向鍊錶需要額外兩個空間來儲存後繼節點和前驅結點,占用更多記憶體空間。但是支援雙向遍歷,靈活性更強。

他支援o(1)時間複雜度的情況下找到前驅結點,所以對於插入刪除等操作要比單鏈表高效。

為什麼?鍊錶刪除操作

無外乎兩種情況。

對於第一種情況,不管是單鏈表還是雙向鍊錶,為了查詢到值等於給定值的結點,都要從頭開始一惡搞乙個遍歷對比,直到找到值等於給定值的結點,然後在刪除。為o(n)複雜度。

對於第二種情況,已經找到了要刪除的結點,但是刪除某個結點q需要直到其前驅結點,所以為了找到前驅結點,還是需要從頭開始遍歷鍊錶。直到p->next=q,說明p是q的前驅節點。

但是對於雙向鍊錶來說,這種情況就好多了,以為雙向鍊錶的結點已經儲存了前驅結點的指標,不需要像單鏈表那樣遍歷,所以,對於第二種情況,單鏈表需要o(n)的,而雙向鍊錶之一笑傲o(1)。

同理,鍊錶在某個指定節點前面插入乙個結點,雙向鍊錶可以在o(1)時間複雜度搞定。

對於乙個有序鍊錶,雙向鍊錶的按值查詢的效率也要比單鏈表高一些。因為可以記錄上次查詢的位置p,每次查詢時,根據要查詢的值與p的大小關係,決定是往前還是往後找,所以平均只需要查詢一半的資料。

實際上,有個更重要的知識,那就是用空間換時間的設計思想。當記憶體空間充足時,如果我們更加追求**速度,可以選擇空間複雜度相對較高,時間複雜度相對很低的演算法。相反,要是**在手機上跑,就要反過來用時間換空間。

陣列 vs 鍊錶

他們是兩種截然不同的記憶體組織方式。所以插入刪除,隨機訪問的時間複雜度正好相反。

陣列的缺點是大小固定,一經宣告就要占用整塊連續記憶體空間。如果宣告的陣列過大,系統可能沒有足夠的連續記憶體空間分配給他。導致記憶體不足。如果宣告的陣列過小,則可能出現不夠用的情況。只能在申請乙個更大的記憶體。

而鍊錶本身沒有大小的限制,天然地支援動態擴容。

資料結構 表之煉表

頭插法建立 尾插法建立 顯示 銷毀 include include using namespace std typedef int elemtype typedef struct lnode linklist void createlinklistf linklist l,elemtype a,in...

資料結構之鍊錶

頭結點 第乙個有效結點之前的那個結點 頭結點並不存有效資料 加頭結點的目的主要是為了方便對鍊錶的操作 頭指標 指向頭結點的指標變數 尾指標 指向尾節點的指標變數 如果希望通過乙個函式對鍊錶進行處理,只需要乙個引數 頭指標 首先要定義乙個單鏈表儲存結構 然後建立乙個空表,即初始化,我寫的這個提前設定好...

資料結構之鍊錶

鍊錶是一種基本的資料結構型別,它由乙個個結點組成。每乙個結點包括乙個資料的儲存和乙個指向下乙個結點的引用。在這個定義中,結點是乙個可能含有任意型別資料的抽象實體,它所包含的指向結點的應用顯示了它在構造鍊錶之中的作用。和遞迴程式一樣,遞迴資料結構的概念一開始也令人費解,但其實它的簡潔性賦予了它巨大的價...