從零開始學演算法:6.連結串列與分塊
作者: tiankonguse | 更新日期:
效率更高的刪除與插入資料結構
在公眾號中回覆“ACM模板”你將免費獲得我大學耗時四年整理的《ACM演算法模板》。
一、背景
大家好,我是tiankonguse。
由於某些原因,經常有人想學習演算法但之前又沒有相關經驗,不知道從何做起。
我思考了許久,計劃寫一個系列來分享如何入門學習演算法。
之前已經分享了《 ofollow,noindex" target="_blank">認識演算法 》、《 瞭解套路長啥樣 》、《 排序演算法 》、《 二分查詢 》、《 萬能陣列 》,這是第六篇《連結串列》。
一、基礎知識
上篇文章《 萬能陣列 》在最後,遺留了一個問題:我們在中間刪除或插入資料的時候,平均複雜度是O(n)。
另外,在空間用完重新分配時,也會有O(n)的調整時間,這些操作有時候不能接受。
於是我們就想,有沒有更快的方法來插入或刪除。
答案自然就是連結串列了。
使用陣列之所以會面臨插入或刪除效能低的問題,就是因為陣列的空間是定長連續的。
所以對應的解決方案自然就是非連續的資料結構了,最簡單的就是連結串列。
二、單向連結串列
連結串列每個節點都包含兩部分:資料、其他節點的位置資訊。
對於位置資訊一般使用地址指標標示,後面也會介紹其他的形式。
最常見的連結串列如下圖,有一個數據和一個位置資訊,我們稱為單向連結串列。
我們使用這種方式,我們就再也不怕空間不夠用了。
看下面的程式碼,可以發現,插入複雜度是O(1)的了,不過最後追增資料的複雜度是O(n)。
當然我們可以通過把最後一個節點儲存起來,做到插入O(1),這裡我就不實現了。
不過這裡有個問題就是插入或刪除資料時,需要維護兩個關係。
三、雙向連結串列
上一小節的名字是單向連結串列,自然可以想到雙向連結串列,也就是儲存兩個位置資訊。
對於雙向連結串列,有個好處是可以逆向遍歷資料。
不過增加節點和刪除節點的時候,需要維護四個關係。
四、迴圈連結串列
對於單向連結串列,我們可以發現 連結串列的尾部的位置資訊浪費了。
對於雙向連結串列,則是表頭的ptr資訊和表尾的next資訊浪費了。
如果我們利用起來的話,表尾指向表頭,表頭指向表尾,那就是迴圈列表了。
對於迴圈列表,我們就可以直接得到表尾,然後快速從隊尾插入資料。
五、下標連結串列
第一小節提到過,連結串列的每個節點都包含兩部分:資料、其他節點的位置資訊。
而對於位置資訊,我們其實可以不使用指標的,在ACM中我們都是使用陣列下標來標識的。
具體來看,就是預先生成一個節點陣列,然後用陣列的下標代表節點的指標。
這樣有個好處,以運算元組的形式才做連結串列。
六、塊狀連結串列
對於連結串列,雖然在中間插入刪除有天然優勢,那自然也有對應的弱勢。
聊表下標定位效率低下,需要從連結串列頭部一個一個的迴圈才能找到指定下標的節點。
五六年前,在大學ACM比賽的時候,經常使用一種折中的方法,就是塊狀連結串列。
這個算是結合了陣列和連結串列,然後操作複雜度也可以接受。
具體分析,陣列是連續的記憶體,所以可以O(1)定位,但是插入刪除是O(n)。
連結串列是非連續記憶體,所以可以O(1)插入和刪除,但是定位要O(n)。
算是兩種性質相反的資料結構,結合之和可以做到O(sqrt(n))的複雜度。
而ACM比賽的時候,資料往往是十萬百萬級別,開放一下不過幾百幾千,也就變得可以接受了。
具體實現也很簡單,就是把陣列當做資料節點,然後使用指標連起來就行了。
實際操作中,只需要維護陣列節點當前使用資料的個數以及下個數組節點的位置即可。
在插入資料時,需要判斷資料的個數是否超過陣列大小,超過了就需要分裂成兩個陣列節點。
在刪除數時,可以判斷一下當前陣列和下個數組的總個數是否小於指定值,小於了就合併。
下標定位的時候,每次可以跳過一個數組,所以速度就加快了。
查詢時迴圈陣列找到節點,然後使用下標具體定位陣列即可。
插入的時候,先找到對應的陣列節點。
如果陣列節點恰好滿了,需要拆分為兩個,資料分兩半,然後確定應該插入到那個陣列。
然後使用插入邏輯插入到具體的數組裡(整體後移)。
刪除就簡單了,直接先刪除,然後判斷是否需要合併,需要就合併。
分塊連結串列的實際場景很多,如文字編輯器、雙向佇列(申請新記憶體時,不需要copy全量資料)等。
七、最後
這篇文章介紹了單向連結串列、雙向連結串列、迴圈連結串列,下標儲存時連結串列、分塊連結串列,幾乎覆蓋了所有的連結串列資料結構。
對於普通的序列,我們使用分塊連結串列來優化了,實現方式的成本與時間複雜度O(sqrt(n))都可以接受了。
而對於有序的序列,我們有另一種優化方式,複雜度可以優化到O(log(n)),下篇文章分享給大家。
注:如果可以隨手快速敲出一個平衡樹,或者變種平衡樹,這些其實小眾的資料結構其實都不需要了。
本文首發於公眾號:天空的程式碼世界,微信號:tiankonguse-code。
推薦閱讀:
今天長按識別上面的二維碼,在公眾號中回覆“ ACM模板 ”,你將免費獲得我大學耗時四年整理的《ACM演算法模板》。
回覆“ 演算法的世界 ”,或點選 閱讀原文 加入“tiankonguse的朋友們”,已有三百多個小夥伴加入。