【資料結構與演算法01】- 陣列、連結串列對比及應用
阿新 • • 發佈:2018-12-13
1. 陣列和連結串列的區別
- 1.1 底層儲存結構
- 陣列需要一塊連續的記憶體空間進行儲存
- 連結串列通過“指標”將一組零散的記憶體塊串聯起來使用
- 1.2 效能
- 連結串列和陣列的(增刪查)時間複雜度正好相反
- 陣列使用連續的記憶體空間,可以藉助快取機制提高效率;連結串列不連續,所以無法藉助快取機制
- 陣列大小固定,當需要申請更大的空間,需要拷貝資料,很耗時;而連結串列純天然的支援動態擴容
- 相對來說,連結串列比較耗記憶體,因為需要記錄節點指標,記憶體消耗翻倍
【注】和陣列相比,連結串列更適合插入、刪除操作頻繁的場景,查詢的時間複雜度較高。不過,在具體的軟體開發中,要對陣列和連結串列的各種效能進行對比,綜合考慮再決定使用兩者中的哪一個。
2. 基於單鏈表的LRU快取
2.1 什麼是快取?
快取是一種提高資料讀取性效能的技術,在硬體設計、軟體開發中都有著廣泛的應用,比如常見的CPU快取、資料庫快取、瀏覽器快取等。
2.2 快取淘汰策略有哪些?
Q:快取的特點是什麼? A:快取的大小是有限的,當快取被用滿時,哪些資料應該被清理出去,哪些資料應該被保留下來。這時就需要用到快取淘汰策略。
常見的3種快取淘汰策略:先進先出策略(FIFO,First In, First Out),最少使用策略(LFU,Least Frequently Used),最近最少使用策略(LRU,Least Recently Used)
2.3 單鏈表實現LRU快取淘汰策略
當訪問的資料沒有儲存在快取的連結串列中時,直接將資料插入連結串列表頭,時間複雜度為O(1); 當訪問的資料存在於儲存的快取的連結串列中,將該資料對應的節點,移動到連結串列表頭,時間複雜度為O(n); 如果快取被佔滿,則從連結串列尾部的資料開始清理,時間複雜度為O(1)。
3. 判斷單鏈表是否為迴文連結串列
3.1 定位連結串列中間節點
Q:在不知道連結串列長度或者節點個數的情況,如何定位連結串列中間節點呢? A:我們可以通過快慢指標進行定位。慢指標每次前進一步,快指標每次前進兩步。
private <T> Node<T> shootingMiddleNode(Node< T> node) {
if (node == null || node.next == null) {
return node;
}
Node<T> slow = node;
Node<T> fast = node;
while (slow != null && fast.next != null && fast.next.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
3.2 從中間節點下一個節點開始進行翻轉連結串列
private <T> Node<T> reverseList(Node<T> root) {
Node<T> pre = null;
Node<T> next = null;
while (root != null) {
next = root.next;
root.next = pre;
pre = root;
root = next;
}
return pre;
}
3.3 比對是否為迴文
public <T> boolean isPalindrome(Node<T> root) {
if (root == null || root.next == null) {
return true;
}
Node<T> middleNode = shootingMiddleNode(root);
middleNode.next = reverseList(middleNode.next);
Node<T> first = root;
Node<T> second = middleNode.next;
while (first != null && second != null && Objects.equals(first.data, second.data)) {
first = first.next;
second = second.next;
}
return second == null;
}
完整程式碼實現可以點選這裡檢視 當然,這裡通過雙向連結串列進行實現會簡單很多,畢竟存在前驅和後繼指標。
【注】除了單鏈表外,還有演化出來的雙向連結串列,迴圈單鏈表,迴圈雙向連結串列。