生活随笔
收集整理的這篇文章主要介紹了
链表相关算法汇总(详细)
小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
鏈表基礎(chǔ)知識(shí)
鏈表是一種物理存儲(chǔ)單元上非連續(xù)、非順序的存儲(chǔ)結(jié)構(gòu),數(shù)據(jù)元素的邏輯順序是通過(guò)鏈表中的指針鏈接次序?qū)崿F(xiàn)的。鏈表由一系列結(jié)點(diǎn)(鏈表中每一個(gè)元素稱(chēng)為結(jié)點(diǎn))組成,結(jié)點(diǎn)可以在運(yùn)行時(shí)動(dòng)態(tài)生成。每個(gè)結(jié)點(diǎn)包括兩個(gè)部分:一個(gè)是存儲(chǔ)數(shù)據(jù)元素的數(shù)據(jù)域,另一個(gè)是存儲(chǔ)下一個(gè)結(jié)點(diǎn)地址的指針域。 相比于線性表順序結(jié)構(gòu),操作復(fù)雜。由于不必須按順序存儲(chǔ),鏈表在插入的時(shí)候可以達(dá)到O(1)的復(fù)雜度,比另一種線性表順序表快得多,但是查找一個(gè)節(jié)點(diǎn)或者訪問(wèn)特定編號(hào)的節(jié)點(diǎn)則需要O(n)的時(shí)間,而線性表和順序表相應(yīng)的時(shí)間復(fù)雜度分別是O(logn)和O(1)。
使用鏈表結(jié)構(gòu)可以克服數(shù)組鏈表需要預(yù)先知道數(shù)據(jù)大小的缺點(diǎn),鏈表結(jié)構(gòu)可以充分利用計(jì)算機(jī)內(nèi)存空間,實(shí)現(xiàn)靈活的內(nèi)存動(dòng)態(tài)管理。但是鏈表失去了數(shù)組隨機(jī)讀取的優(yōu)點(diǎn),同時(shí)鏈表由于增加了結(jié)點(diǎn)的指針域,空間開(kāi)銷(xiāo)比較大。鏈表最明顯的好處就是,常規(guī)數(shù)組排列關(guān)聯(lián)項(xiàng)目的方式可能不同于這些數(shù)據(jù)項(xiàng)目在記憶體或磁盤(pán)上順序,數(shù)據(jù)的存取往往要在不同的排列順序中轉(zhuǎn)換。鏈表允許插入和移除表上任意位置上的節(jié)點(diǎn),但是不允許隨機(jī)存取。鏈表有很多種不同的類(lèi)型:單向鏈表,雙向鏈表以及循環(huán)鏈表。
需要注意的是,在Java中沒(méi)有指針的概念,而類(lèi)似指針的功能都是通過(guò)引用來(lái)實(shí)現(xiàn)的,為了便于理解,我們?nèi)匀皇褂弥羔?#xff08;可以認(rèn)為引用與指針是類(lèi)似的)來(lái)進(jìn)行描述,而在實(shí)現(xiàn)的代碼中,都是通過(guò)引用來(lái)建立結(jié)點(diǎn)之間的關(guān)系。
鏈表的分類(lèi)
單向鏈表
單向鏈表(單鏈表)是鏈表的一種,其特點(diǎn)是鏈表的鏈接方向是單向的,對(duì)鏈表的訪問(wèn)要通過(guò)順序讀取從頭部開(kāi)始;
單鏈表節(jié)點(diǎn):
class ListNode {int val
;ListNode next
;ListNode(int x
) { val
= x
; }
}
雙向鏈表
雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個(gè)數(shù)據(jù)結(jié)點(diǎn)中都有兩個(gè)指針,分別指向直接后繼和直接前驅(qū)。所以,從雙向鏈表中的任意一個(gè)結(jié)點(diǎn)開(kāi)始,都可以很方便地訪問(wèn)它的前驅(qū)結(jié)點(diǎn)和后繼結(jié)點(diǎn)。
雙向鏈表節(jié)點(diǎn):
class ListNode {int value
;ListNode prev
= null
;ListNode next
= null
;ListNode(int x
) {value
= x
;}
}
循環(huán)鏈表
循環(huán)鏈表是另一種形式的鏈?zhǔn)酱尜A結(jié)構(gòu)。它的特點(diǎn)是表中最后一個(gè)結(jié)點(diǎn)的指針域指向頭結(jié)點(diǎn),整個(gè)鏈表形成一個(gè)環(huán)。
循環(huán)鏈表可以是單向也可以是雙向,節(jié)點(diǎn)因可以是雙向節(jié)點(diǎn)也可以是單向節(jié)點(diǎn)
鏈表基本操作
237. 刪除鏈表中的節(jié)點(diǎn)
public void deleteNode(ListNode node
) {node
.val
= node
.next
.val
;node
.next
= node
.next
.next
;
}
面試題18. 刪除鏈表的節(jié)點(diǎn)
- 通過(guò)記錄遍歷節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn),一旦匹配上,前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)相連自然就刪掉目的節(jié)點(diǎn)
public ListNode
deleteNode(ListNode head
, int val
) {if (head
.val
== val
) return head
.next
;ListNode pre
= head
, suf
= head
.next
;while (suf
!= null
&& suf
.val
!= val
) {pre
= suf
;suf
= suf
.next
;}if (suf
!= null
) pre
.next
= suf
.next
;return head
;}
206. 反轉(zhuǎn)鏈表
迭代模板:
- 只要將鏈表中兩兩之間的指向調(diào)換,就可以達(dá)到目的
- 注意需要一個(gè)指針指向下一個(gè)值不然在換的過(guò)程中會(huì)丟失進(jìn)度
public ListNode
reverseList(ListNode head
) {ListNode pre
= null
;ListNode cur
= head
;while (cur
!= null
) {ListNode tmp
= cur
.next
;cur
.next
= pre
;pre
= cur
;cur
= tmp
;}return pre
;}
遞歸模板:
- 因?yàn)槭沁f歸,最先操作的一定是最后一個(gè)節(jié)點(diǎn),因此遞歸的返回判斷就是最后一個(gè)節(jié)點(diǎn) head.next == null
- 我們需要將最后一個(gè)節(jié)點(diǎn)可前一個(gè)節(jié)點(diǎn)調(diào)轉(zhuǎn)指向
public ListNode
reverseList(ListNode head
) {if (head
== null
|| head
.next
== null
) return head
;ListNode pre
= reverseList(head
.next
);head
.next
.next
= head
;head
.next
= null
;return pre
;}
25. K 個(gè)一組翻轉(zhuǎn)鏈表
- K個(gè)一組,每組內(nèi)部通過(guò)上面的復(fù)轉(zhuǎn)鏈表做
- 通過(guò)遞歸分組拼接
class Solution {public static ListNode
reverseKGroup(ListNode head
, int k
) {if (head
== null
) return null
;ListNode cur
= head
;int count
= 0;while (cur
!= null
&& count
!= k
) {cur
= cur
.next
;count
++;}if (count
== k
) {cur
= reverseKGroup(cur
, k
);while (count
!= 0) {count
--;ListNode tmp
= head
.next
;head
.next
= cur
;cur
= head
;head
= tmp
;}head
= cur
;}return head
;}
}
21. 合并兩個(gè)有序鏈表
迭代模板:
public static ListNode
mergeTwoLists(ListNode l1
, ListNode l2
){ListNode head
= new ListNode(-1);ListNode tmp
=head
;while(l1
!=null
&&l2
!=null
){if (l1
.val
<l2
.val
){tmp
.next
= l1
;l1
= l1
.next
;}else{tmp
.next
= l2
;l2
= l2
.next
;}tmp
= tmp
.next
;}tmp
.next
= l1
==null
?l2
:l1
;return head
.next
;}
遞歸模板:
public ListNode
mergeTwoLists(ListNode l1
, ListNode l2
) {if (l1
== null
) {return l2
;}else if (l2
== null
) {return l1
;}else if (l1
.val
< l2
.val
) {l1
.next
= mergeTwoLists(l1
.next
, l2
);return l1
;}else {l2
.next
= mergeTwoLists(l1
, l2
.next
);return l2
;}}
23. 合并K個(gè)排序鏈表
- 使用分治的思想,通過(guò)上面的合并兩個(gè)鏈表,將多個(gè)鏈表合并
class Solution {public ListNode
mergeKLists(ListNode
[] lists
) {int len
= lists
.length
;if (len
==0) return null
;return split(lists
,0,len
-1);}private ListNode
split(ListNode
[] lists
, int start
, int end
) {if (start
== end
) {return lists
[start
];}else if(end
-start
==1){return merge2Lists(lists
[start
], lists
[end
]);}else if (end
-start
>1){int mid
= (end
+start
)>>1;ListNode left
= split(lists
, start
, mid
);ListNode right
= split(lists
, mid
+1, end
);return merge2Lists(left
, right
);}return null
;}private ListNode
merge2Lists(ListNode n1
, ListNode n2
) {ListNode head
= new ListNode(-1);ListNode tmp
= head
;while (n1
!=null
&&n2
!=null
){if (n1
.val
>= n2
.val
) {tmp
.next
= n2
;n2
= n2
.next
;}else{tmp
.next
= n1
;n1
= n1
.next
;}tmp
=tmp
.next
;}tmp
.next
= n1
!= null
?n1
:n2
;return head
.next
;}
}
雙向鏈表應(yīng)用
146. LRU緩存機(jī)制
- 通過(guò)雙向鏈表進(jìn)行緩存
- 添加:在頭部,保持頭部是最新訪問(wèn)的,尾部是最久未使用的
- 操作某個(gè)node的話,將該node刷新的head
- 如果超出緩存長(zhǎng)度,則刪掉末尾那個(gè),再添加新的
public class LRUCache {private HashMap
<Integer, Node> cache
;private Integer cap
;private Node tail
= new Node(-1, -1);private Node head
= new Node(-1, 1);public LRUCache(int capacity
) {cache
= new HashMap<Integer, Node>(capacity
);cap
= capacity
;this.head
.suf
= tail
;this.tail
.pre
= head
;}public int get(int key
) {if (!cache
.containsKey(key
)){return -1;}else{Node node
= cache
.get(key
);refresh(node
);return node
.value
;}}public void put(int key
, int value
) {if (cache
.containsKey(key
)){Node node
= cache
.get(key
);node
.value
= value
;refresh(node
);}else {if(cache
.size() == cap
){cache
.remove(tail
.pre
.key
);Node removedPreNode
= tail
.pre
.pre
;removedPreNode
.suf
= tail
;tail
.pre
= removedPreNode
;}Node addNode
= new Node(key
,value
);cache
.put(key
, addNode
);head
.suf
.pre
= addNode
;addNode
.suf
= head
.suf
;head
.suf
= addNode
;addNode
.pre
= head
;}}public void refresh(Node node
){Node pre
= node
.pre
, suf
= node
.suf
;pre
.suf
= suf
;suf
.pre
= pre
;head
.suf
.pre
= node
;node
.suf
= head
.suf
;head
.suf
= node
;node
.pre
= head
;}
}class Node {int key
;int value
;Node pre
= null
;Node suf
= null
;public Node() {}public Node(int key
, int value
) {this.key
= key
;this.value
= value
;}
}
460. LFU緩存
- 相對(duì)于LRU多了訪問(wèn)次數(shù)的存儲(chǔ)
- 通過(guò)訪問(wèn)次數(shù)進(jìn)行重新排序
public class LFUCache {private HashMap
<Integer, Node> cache
;private Integer cap
;private Node tail
= new Node(-1, -1, Integer
.MIN_VALUE
);private Node head
= new Node(1, 1, Integer
.MAX_VALUE
);public LFUCache(int capacity
) {cache
= new HashMap<Integer, Node>(capacity
);cap
= capacity
;this.head
.suf
= tail
;this.tail
.pre
= head
;}public int get(int key
) {if (cap
<= 0 || !cache
.containsKey(key
)){return -1;}else{Node node
= cache
.get(key
);node
.freq
++;node
.refresh();return node
.value
;}}public void put(int key
, int value
) {if (cap
> 0){if (cache
.containsKey(key
)){Node node
= cache
.get(key
);node
.value
= value
;node
.freq
++ ;node
.refresh();}else {if(cache
.size() == cap
){cache
.remove(tail
.pre
.key
);Node removedPreNode
= tail
.pre
.pre
;removedPreNode
.suf
= tail
;tail
.pre
= removedPreNode
;}Node addNode
= new Node(key
,value
);cache
.put(key
, addNode
);addNode
.pre
= tail
.pre
;addNode
.suf
= tail
;tail
.pre
.suf
= addNode
;tail
.pre
= addNode
;addNode
.refresh();}}else{System
.out
.println("capacity smaller then 1");}}
}class Node {int key
;int value
;int freq
= 0;Node pre
= null
;Node suf
= null
;public Node() {}public Node(int key
, int value
) {this.key
= key
;this.value
= value
;}public Node(int key
, int value
, int freq
) {this.key
= key
;this.value
= value
;this.freq
= freq
;}public void refresh(){Node preNode
= this.pre
, sufNode
= this.suf
;if (preNode
.freq
<= this.freq
) {preNode
.suf
= sufNode
;sufNode
.pre
= preNode
;while (preNode
.freq
<= this.freq
) {preNode
= preNode
.pre
;}sufNode
= preNode
.suf
;preNode
.suf
= sufNode
.pre
= this ;this.pre
= preNode
;this.suf
= sufNode
;}}
}
持續(xù)更新中。。。。。。。。。。。
總結(jié)
以上是生活随笔為你收集整理的链表相关算法汇总(详细)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。