@jayce 數據結構本身是用於數據的存儲,其核心在於保存數據。
掌握一個數據解構,應該重點把握兩個方面:
- 該數據結構的特點有哪些?
- 該數據結構的增刪查是如何進行的?有哪些點值得注意?
- 該數據結構有哪些擴展形式,其特點和增刪查又是如何進行的?
本文創建原名: 第1章.[附加]如何纔算掌握了一個數據解構
總結
1. 鏈表
1.1 特點
- 數據線性排列
- 增刪很方便,查詢耗時
- 每個數據項有一個指針指向下一個數據內存地址,在內存中,無需連續內存空間,可以分散存儲
- 也正是因爲分散存儲,所以查詢時需要順着指針逐個遍歷
- 時間複雜度: 查詢爲 O(n), 增加和刪除與n無關,所以爲 O(1)
1.2 增刪查
1.2.1 查
查詢,從首個元素開始,依據其指針指向的下一個元素,去遍歷。
1.2.2 增
只需要改變插入位置前後的指針指向即可:
"blue" --> "Yellow" --> "Red"
# 在第二個位置插入 “Green”
"blue" --> "Green"
"Green" --> "Yellow"
# ==>
"blue" --> "Green" --> "Yellow" --> "Red"
1.2.3 刪
將待刪除項的前一個元素指針指向待刪除項的後一個元素即可:
"blue" --> "Green" --> "Yellow" --> "Red"
# 刪除 Yellow, 直接將 "Green" 指向 "Red" 即可, "Yellow" 無需處理,如果需要再次用到其地址,直接覆蓋
"Green" --> "Red"
# ==>
"blue" --> "Green" --> "Red"
1.3 鏈表的擴展
處理基本鏈表, 還有兩種較爲常見的擴展鏈表,分別是 環形鏈表/循環鏈表 和 雙向鏈表
循環鏈表,適合保存數量固定的最新數據
雙向鏈表的則可以前後雙向遍歷, (雙向鏈表存在兩個缺點:1. 指針數的增加會導致存儲空間需求增加;2.添加和刪除數據時需要改變更多指針指向。 )
2. 數組
2.1 特點
- 數據呈線性排列,內存空間也是順序的
- 數據的訪問十分簡單,數據的添加和刪除比較耗時
- 時間複雜度:數據訪問爲 O(1), 增刪爲 O(n)
- 數組相比較鏈表:數據訪問比鏈表快,但是數據的增刪都比鏈表慢
2.2 增刪查
2.2.1 查
數據的訪問,直接按照索引訪問
2.2.2 增
["a","b","c"]
要想在 "a"
和 “b”
之間插入一個數據,
- 在數組末尾增加需要的存儲空間:
["a","b","c", - ]
- 然後自後往前向後移動,直到指定插入位置:
["a","b", - ,"c"] --> ["a", - ,"b","c"]
- 最後在指定位置插入目標數據:
["a","x","b","c"]
2.2.3 刪
- 首先刪除掉目標數據項:
["a", - ,"b","c"]
- 從刪除位置開始,將後面的數據逐個像前移動:
["a","b", - ,"c"] --> ["a","b","c", - ]
- 最後,刪除掉多餘的空間:
["a","b","c"]
3. 棧(桶)
3.1 特點
- 數據呈線性排列
- 後進先出
- 棧中的數據增刪只能在一端進行, 增加叫做 入棧操作, 刪除叫做 出棧操作
3.2 增刪查
3.2.1 查
棧很少討論如何查詢
3.2.2 增
在最後一個元素後追加元素
3.2.3 刪
刪除最後一個元素
4. 隊列
4.1 特點
- 隊列的數據也是呈線性排列的
- 隊列和數據有些相似,但是隊列是一個雙開口的管子, 數據的增加叫做入隊,數據的刪除叫做出隊
- 先進先出
4.2 增刪查
4.2.1 查
隊列也基本不討論如何查詢
4.2.2 增
隊列的數據增加,從隊列的頭部操作 (入隊)
4.2.3 刪
隊列的數據刪除, 從隊列的尾部操作(出隊)
5. 哈希表
5.1 特點
-
哈希表以數組 + 鏈表爲存儲容器
-
數據的訪問和存儲都需要通過 哈希函數(Hash) 進行計算,算出 數組的鍵 ,然後進行 mod運算
#運算規則: "Joe" --hash--> 4928 --> mod 5 --> 3 # Joe 數據將被存儲到索引值爲 3 的數組位上。
更多的
#item Hash mod 5 "Joe" 4828 3 "Sue" 7291 1 "Dan" 1539 4 "Nell" 6276 1 "Ally" 9143 3 "Bob" 5278 3
相同的鍵位 我們稱之爲 “衝突”, 這時候我們通過鏈表,將這些位置相同的數據項放在一起。
[ , "Sue" --> "Nell", , "Joe" --> "Ally" --> "Bob", "Dan" ]
5.2 增刪查
5.2.1 查
計算 Hash 值,然後進行 Mod 運算,然後對數據對應的索引位上的鏈表進行線性查找。
例如要查詢 “Ally” , "Allay" ==> Hash函數 > 9143
mod 5 ==> 3
, 然後對數組[3],進行鏈表的線性查找。找到 “Allay” 數據項。
5.2.2 增刪
哈希表結構的數據, 增刪都符合,先通過 Hash 函數計算出 Hash 值,然後進行 Mod 運算找到數組的索引位。 從而找到了數組中對應位的鏈表 。 接着,增刪操作均符合 鏈表的增刪操作特性。
關於哈希表的補充:
在 哈希表 中,我們可以利用 哈希函數 快速訪問到數組中的目標數據。 如果發生 哈希衝突 , 就使用 鏈表 進行存儲。如果使用數組的空間太小,使用哈希表的時候就容易發生衝突,線性查找的使用頻率也會更加高; 反過來,如果數組的空間太大, 就會出現很多空箱子,造成內存的浪費。 因此給數組設定合適的空間非常重要。
在存儲數據的過程中,如果發生衝突,通常有兩種方式去解決:
鏈地址法(同一位置的衝突對象組織在一起):可以利用鏈表在已有數據的後面插入新的數據來解決衝突。 這種方法被稱爲 “鏈地址法”。也就是上面 「特點」 中那樣
開放定址法(換個位置):一旦產生了衝突(該地址已經有其他的元素),就按某種規則去尋找另一空地址。
若發生了第 \(i\) 次衝突,試探的下一個地址將會增加 \(d_i\), 基本公式是\((1\leq i \leq TableSize)\):
\[h_i(key) = (h(key)+ d_i) \pmod {TableSize} \]\(d_i\) 決定了不同的解決衝突方案:
- 線性探測:\(d_i = i\)
- 平方探測:$ d_i = \pm i^2$
- 雙散列:\(d_i = i\times h_2(key)\)
6. 堆
6.1 特點
-
堆是一種圖的樹形結構,被用於實現 “優先隊列(priority queues)”
優先隊列是一種數據結構,可以自由地添加數據,但是取出數據時要從最小值開始按照順序取出。
-
堆的樹形結構中,各個頂點被稱之爲 “結點”, 數據就存儲於這些結點
-
堆中的每個結點最多有兩個子結點
-
結點的排列順序爲 從上到下, 同一行爲從左到右。
-
子結點必定大於父結點
-
新增結點 一般放於最下面一行靠左的位置,如沒有多餘空間,就往下另起一行,加在最左端
-
時間複雜度:取出最小值的時間複雜度爲 \(O(1)\), 此外,取出數據後,堆需要重排,假設數據量爲n, 根據堆的形狀特點可知樹的高度爲 \(log_2n\), 時間複雜度即爲 \(O(logn)\).
6.2 增刪查
6.2.1 增
- 先將數據添加至最後一排靠左的位置,沒有空間則新增一排
- 判斷是否滿足子節點大於父節點,不滿足則和父節點互換位置,直到滿足大於父節點,否則,保持不動。
6.2.2 取
- 優先隊列取出數據的時候,取出的是最小值,也就是頂端結點。
- 取出後,其他位置需要頂替根結點的位置,把結點順序末尾的那個結點放置在根結點
- 然後和子節點比較,是否滿足子節點都大於根結點,不滿足則和較小的那個結點互換,依次往下,知道滿足基本條件(所有父結點小於任意根結點)
頂端結點被取出之後,堆的結構需要被重排
子結點必定大於父結點
7. 二叉樹
二叉樹即二叉查找樹(也叫做二叉搜索樹,二叉排序樹)。 這種數據結構採用了圖的樹形結構,只是數據組織規則有所不同。
7.1 特點
-
每個結點最多有兩個子結點
-
每個結點值大於其左子樹上任意一個結點值
-
每個結點的值均小於其右子樹上的任意一個結點值
-
所以最小值要從頂端開始,往其左下的末端尋找,最大值要從頂端開始,往其右下的末端尋找。
-
時間複雜度: 最大值查找時間複雜度爲 O(1), 其他值,則取決於樹的形狀和高度, 如果結點樹爲 n, 且樹的形狀較爲均衡,比較大小和移動的次數最多就是 \(log_2n\) , 因此時間複雜度也就是 \(O(log_n)\), 但是如果樹的形狀朝單側縱向延伸,樹就會變得很高,此時時間複雜度也就變成了\(O(n)\)。
7.2 增刪查
7.2.1 增
二叉樹的增加,從頂部開始
接着, 1 < 9, 接着往左移到 9 所在結點,然後,1 < 3 大,所以接着往左移,填補空位:
同理,如果插入 4
4 < 15 ==>左移
4 < 9 ==>左移
4 > 3 ==>右移
4 < 8, 其8 無其他子結點,則作爲其子結點
7.2.2 刪
如果需要刪除的結點沒有子結點,那麼直接刪掉該結點即可:
如果需要刪除的結點只有一個子結點,那麼刪除掉目標結點後,然後把子結點移到被刪除結點的位置即可。
如果被刪除結點有兩個結點,那麼先刪除該結點,然後在該結點左子樹中尋找最大結點,移動到被刪除結點位置。
7.2.1 查找
二叉樹的查找,類似於二叉樹的結點增加,也是從頂部開始,將根結點和目標結點比較,如果目標結點小於根結點,則向左移,否則右移動,依照此規則往下遍歷,直到找到目標元素。
7.3 二叉樹的擴展
有很多以二叉樹爲基礎擴展的數據結構,比如 “平衡而茶查找樹”, 這種數據結構可以修正形狀不均衡的樹,讓其始終保持均衡狀態,以提高查找效率。
此外,結點樹並不是必須爲兩個結點,可以擴展爲 m 個結點。像這種子結點樹可以自由設定,並且形狀均衡的樹,我們稱作 B 樹。