鏈表模板
鏈表結構
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
虛擬頭節點
虛擬頭節點用於是鏈表每一個節點都具有前一個節點,結構統一,可以規避特殊情況,使得鏈表頭節點也可以當作普通節點處理。
ListNode dummy = new ListNode(-1);
dummy.next = head;
/**********/
return dummy.next;
獲取鏈表倒數第k個節點
ListNode dummy = new ListNode(-1);
dummy.next = head; //虛擬頭節點
ListNode l = dummy; //l是慢指針,指向虛擬頭節點
ListNode r = dummy; //r是快指針,指向虛擬頭節點
for (int i = 0; i < k; i++) {
r = r.next; //快指針移動k次
}
while (r != null) {
l = l.next;
r = r.next;
}
// l所指向的節點就是鏈表倒數第k個節點
鏈表節點交換
ListNode tmp = pos.next;
pos.next = cur;
cur.next = tmp;
pre.next = pos;
鏈表反轉
整個鏈表反轉
🌈 法一:使用虛擬頭節點
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode pre = dummy;
ListNode cur = dummy.next;
while(cur != null) {
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
dummy.next.next = null;
dummy.next = pre;
return dummy.next;
}
🌈 法二:不使用虛擬頭節點(最快的方式)
public ListNode reverseList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode pre = null;
ListNode cur = head;
while(cur != null) {
ListNode t = cur.next;
cur.next = pre;
pre = cur;
cur = t;
}
return pre;
}
鏈表局部反轉
翻轉鏈表從left到right范圍內的節點
比如:翻轉第2到第4個節點之間的鏈表
public ListNode reverseBetween(ListNode head, int left, int right) {
int length = right - left + 1; //一共要反轉length個節點
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode hh = dummy; //hh指向目標反轉范圍的前一個節點
while(left-- > 1) {
hh = hh.next;
}
ListNode a = hh.next, b = a.next;
while(length-- > 1) { //一共要反轉length個節點, 中間就有 length - 1 個 next 指針需要翻轉
ListNode tmp = b.next;
b.next = a;
a = b;
b = tmp;
}
hh.next.next = b;
hh.next = a;
return dummy.next;
}
雙向鏈表
🌈 節點:
class Node{
int k,v;
Node l,r;
Node(int key, int value){
this.k = key;
this.v = value;
}
}
🌈 虛擬頭尾節點
🌈 雙向鏈表節點的插入
target.r = node.r;
node.r.l = target;
target.l = node;
node.r = target
🌈 雙向鏈表節點的刪除
node.r.l = node.l;
node.l.r = node.r;
例題
兩數相加
給你兩個 非空 的鏈表,表示兩個非負的整數。它們每位數字都是按照 逆序 的方式存儲的,並且每個節點只能存儲 一位 數字。
請你將兩個數相加,並以相同形式返回一個表示和的鏈表。
你可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。
示例 1:
輸入:l1 = [2,4,3], l2 = [5,6,4]
輸出:[7,0,8]
解釋:342 + 465 = 807.
示例 2:
輸入:l1 = [0], l2 = [0]
輸出:[0]
示例 3:
輸入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
輸出:[8,9,9,9,0,0,0,1]
提示:
每個鏈表中的節點數在范圍 [1, 100] 內
0 <= Node.val <= 9
題目數據保證列表表示的數字不含前導零
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
if (l1 == null) return l2;
if (l2 == null) return l1;
ListNode head = new ListNode(0); //虛擬頭節點
ListNode tail = head;
int addition = 0; //記錄進位
while (l1 != null && l2 != null) {
int num = l1.val + l2.val + addition;
if (num >= 10) { //兩數和大於0,進位為1;否則進位為0
addition = 1;
num -= 10;
} else
addition = 0;
tail.next = new ListNode(num); //尾插法
tail = tail.next;
l1 = l1.next;
l2 = l2.next;
}
while (l1 != null) { //再處理l1和l2中長的鏈表
int num = l1.val + addition;
if (num >= 10) {
addition = 1;
num -= 10;
} else addition = 0;
tail.next = new ListNode(num);
tail = tail.next;
l1 = l1.next;
}
while (l2 != null) {
int num = l2.val + addition;
if (num >= 10) {
addition = 1;
num -= 10;
} else addition = 0;
tail.next = new ListNode(num);
tail = tail.next;
l2 = l2.next;
}
if (addition == 1) { //注意如果最后一次運算仍然有進位(1+9)
//需要再創建一個節點保存進位
tail.next = new ListNode(1);
tail = tail.next;
}
return head.next;
}
}
刪除鏈表的倒數第 N 個結點
給你一個鏈表,刪除鏈表的倒數第 n 個結點,並且返回鏈表的頭結點。
進階:你能嘗試使用一趟掃描實現嗎?
示例 1:
輸入:head = [1,2,3,4,5], n = 2
輸出:[1,2,3,5]
示例 2:
輸入:head = [1], n = 1
輸出:[]
示例 3:
輸入:head = [1,2], n = 1
輸出:[1]
提示:
鏈表中結點的數目為 sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null) return head;
ListNode h = new ListNode(0); //虛擬頭節點
h.next = head;
ListNode l = h; //l指向目標節點的前一個節點
ListNode r = h; //r是快指針
for (int i = 0; i < n; i++) {
r = r.next;
}
while (r.next != null) {
l = l.next;
r = r.next;
}
if (l.next.next == null) l.next = null;
else {
l.next = l.next.next;
}
return h.next;
}
}
合並兩個有序鏈表
LeetCode 21.https://leetcode-cn.com/problems/merge-two-sorted-lists/
將兩個升序鏈表合並為一個新的 升序 鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。
示例 1:
輸入:l1 = [1,2,4], l2 = [1,3,4]
輸出:[1,1,2,3,4,4]
示例 2:
輸入:l1 = [], l2 = []
輸出:[]
示例 3:
輸入:l1 = [], l2 = [0]
輸出:[0]
提示:
兩個鏈表的節點數目范圍是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非遞減順序 排列
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode h = new ListNode(0); //虛擬頭節點
ListNode tail = h;
while (l1 != null && l2 != null) {
if (l1.val < l2.val) {
tail.next = l1; //尾插法
tail = tail.next;
l1 = l1.next;
} else {
tail.next = l2;
tail = tail.next;
l2 = l2.next;
}
}
if (l1 != null) tail.next = l1; //處理l1和l2中長的剩余鏈表
if (l2 != null) tail.next = l2;
return h.next;
}
}
合並K個升序鏈表
給你一個鏈表數組,每個鏈表都已經按升序排列。
請你將所有鏈表合並到一個升序鏈表中,返回合並后的鏈表。
示例 1:
輸入:lists = [[1,4,5],[1,3,4],[2,6]]
輸出:[1,1,2,3,4,4,5,6]
解釋:鏈表數組如下:
[
1->4->5,
1->3->4,
2->6
]
將它們合並到一個有序鏈表中得到。
1->1->2->3->4->4->5->6
示例 2:
輸入:lists = []
輸出:[]
示例 3:
輸入:lists = [[]]
輸出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的總和不超過 10^4
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> q = new PriorityQueue<>((a, b) -> a.val - b.val);
for (ListNode node : lists) { //第一次一股腦把所有鏈表的頭節點塞進優先隊列
if (node != null) q.add(node);
}
ListNode h = new ListNode(0); //虛擬頭節點
ListNode tail = h;
while (!q.isEmpty()) {
ListNode node = q.poll(); //每次彈出最小的節點
tail.next = node; //尾插法
tail = tail.next;
if (node.next != null) q.add(node.next); //彈出節點的后繼節點放入隊列
}
return h.next;
}
}
兩兩交換鏈表中的節點
給定一個鏈表,兩兩交換其中相鄰的節點,並返回交換后的鏈表。
你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
示例 1:
輸入:head = [1,2,3,4]
輸出:[2,1,4,3]
示例 2:
輸入:head = []
輸出:[]
示例 3:
輸入:head = [1]
輸出:[1]
提示:
鏈表中節點的數目在范圍 [0, 100] 內
0 <= Node.val <= 100
進階:你能在不修改鏈表節點值的情況下解決這個問題嗎?(也就是說,僅修改節點本身。)
class Solution {
public ListNode swapPairs(ListNode head) {
if(head==null || head.next==null) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode cur = dummy;
while(cur.next != null && cur.next.next != null) {
ListNode a = cur.next;
ListNode b = a.next;
cur.next = b;
a.next = b.next;
b.next = a;
cur = a;
}
return dummy.next;
}
}
K 個一組翻轉鏈表
給你一個鏈表,每 k 個節點一組進行翻轉,請你返回翻轉后的鏈表。
k 是一個正整數,它的值小於或等於鏈表的長度。
如果節點總數不是 k 的整數倍,那么請將最后剩余的節點保持原有順序。
進階:
你可以設計一個只使用常數額外空間的算法來解決此問題嗎?
你不能只是單純的改變節點內部的值,而是需要實際進行節點交換。
示例 1:
輸入:head = [1,2,3,4,5], k = 2
輸出:[2,1,4,3,5]
示例 2:
輸入:head = [1,2,3,4,5], k = 3
輸出:[3,2,1,4,5]
示例 3:
輸入:head = [1,2,3,4,5], k = 1
輸出:[1,2,3,4,5]
示例 4:
輸入:head = [1], k = 1
輸出:[1]
提示:
列表中節點的數量在范圍 sz 內
1 <= sz <= 5000
0 <= Node.val <= 1000
1 <= k <= sz
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(-1); //虛擬頭節點
dummy.next = head;
ListNode cur = dummy; //cur指向反轉鏈表前一個節點
while (cur != null) {
reverse(cur, k);
int u = k;
while (u-- > 0 && cur != null) {
cur = cur.next;
}
}
return dummy.next;
}
public void reverse(ListNode root, int k) {
int u = k;
ListNode cur = root;
while (u-- > 0 && cur != null) cur = cur.next;
if (cur == null) return;
ListNode a = root.next;
ListNode b = a.next;
// 當需要翻轉 k 個節點時,中間就有 k - 1 個 next 指針需要翻轉
while (k-- > 1) {
ListNode tmp = b.next;
b.next = a;
a = b;
b = tmp;
}
root.next.next = b;
root.next = a;
}
}
旋轉鏈表
給你一個鏈表的頭節點 head ,旋轉鏈表,將鏈表每個節點向右移動 k 個位置。
示例 1:
輸入:head = [1,2,3,4,5], k = 2
輸出:[4,5,1,2,3]
示例 2:
輸入:head = [0,1,2], k = 4
輸出:[2,0,1]
提示:
鏈表中節點的數目在范圍 [0, 500] 內
-100 <= Node.val <= 100
0 <= k <= 2 * 109
思路:
-
注意到k的數據極大,要對k取模,避免不必要的循環。
-
遍歷鏈表,首尾相連,形成環形鏈表。
-
在length - k的位置斷開鏈表,返回下一個節點即可。
class Solution {
public ListNode rotateRight(ListNode head, int k) {
if (head == null || head.next == null) return head;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode a = head;
ListNode b = a.next;
int length = 1;
while (b != null) {
a = a.next;
b = b.next;
length++;
}
a.next = head;
k = k % length;
int cnt = length - k;
while (cnt-- > 0) dummy = dummy.next;
ListNode ans = dummy.next;
dummy.next = null;
return ans;
}
}
刪除排序鏈表中的重復元素
存在一個按升序排列的鏈表,給你這個鏈表的頭節點 head ,請你刪除鏈表中所有存在數字重復情況的節點,只保留原始鏈表中 沒有重復出現 的數字。
返回同樣按升序排列的結果鏈表。
示例 1:
輸入:head = [1,2,3,3,4,4,5]
輸出:[1,2,5]
示例 2:
輸入:head = [1,1,1,2,3]
輸出:[2,3]
提示:
鏈表中節點數目在范圍 [0, 300] 內
-100 <= Node.val <= 100
題目數據保證鏈表已經按升序排列
class Solution {
public ListNode deleteDuplicates(ListNode head) {
ListNode dummy = new ListNode();
ListNode tail = dummy;
while (head != null) {
// 進入循環時,確保了 head 不會與上一節點相同
if (head.next == null || head.val != head.next.val) {
tail.next = head;
tail = head;
}
// 如果 head 與下一節點相同,跳過相同節點
while (head.next != null && head.val == head.next.val) head = head.next;
head = head.next;
}
tail.next = null;
return dummy.next;
}
}
存在一個按升序排列的鏈表,給你這個鏈表的頭節點 head ,請你刪除所有重復的元素,使每個元素 只出現一次 。
返回同樣按升序排列的結果鏈表。
示例 1:
輸入:head = [1,1,2]
輸出:[1,2]
示例 2:
輸入:head = [1,1,2,3,3]
輸出:[1,2,3]
提示:
鏈表中節點數目在范圍 [0, 300] 內
-100 <= Node.val <= 100
題目數據保證鏈表已經按升序排列
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if(head==null||head.next==null) return head;
ListNode index = head;
while(index.next!=null){
if(index.val==index.next.val){
index.next = index.next.next;
}else{
index = index.next;
}
}
return head;
}
}
相交鏈表
給你兩個單鏈表的頭節點 headA 和 headB ,請你找出並返回兩個單鏈表相交的起始節點。如果兩個鏈表沒有交點,返回 null 。
圖示兩個鏈表在節點 c1 開始相交:
題目數據 保證 整個鏈式結構中不存在環。
注意,函數返回結果后,鏈表必須 保持其原始結構 。
示例 1:
輸入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
輸出:Intersected at '8'
解釋:相交節點的值為 8 (注意,如果兩個鏈表相交則不能為 0)。
從各自的表頭開始算起,鏈表 A 為 [4,1,8,4,5],鏈表 B 為 [5,0,1,8,4,5]。
在 A 中,相交節點前有 2 個節點;在 B 中,相交節點前有 3 個節點。
示例 2:
輸入:intersectVal = 2, listA = [0,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
輸出:Intersected at '2'
解釋:相交節點的值為 2 (注意,如果兩個鏈表相交則不能為 0)。
從各自的表頭開始算起,鏈表 A 為 [0,9,1,2,4],鏈表 B 為 [3,2,4]。
在 A 中,相交節點前有 3 個節點;在 B 中,相交節點前有 1 個節點。
示例 3:
輸入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
輸出:null
解釋:從各自的表頭開始算起,鏈表 A 為 [2,6,4],鏈表 B 為 [1,5]。
由於這兩個鏈表不相交,所以 intersectVal 必須為 0,而 skipA 和 skipB 可以是任意值。
這兩個鏈表不相交,因此返回 null 。
提示:
listA 中節點數目為 m
listB 中節點數目為 n
0 <= m, n <= 3 * 104
1 <= Node.val <= 105
0 <= skipA <= m
0 <= skipB <= n
如果 listA 和 listB 沒有交點,intersectVal 為 0
如果 listA 和 listB 有交點,intersectVal == listA[skipA + 1] == listB[skipB + 1]
思路:
- 差值解法:先對兩條鏈表掃描一遍,取得兩者長度,然后讓長的鏈表先走「兩者的長度差值」,然后再同時走,遇到第一個節點即是答案。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA == headB) return headA;
ListNode a = headA;
ListNode b = headB;
int lenA = 0;
int lenB = 0;
while(a != null) {
a = a.next;
lenA++;
}
while(b != null) {
b = b.next;
lenB++;
}
if(lenB > lenA) {
int l = lenB - lenA;
while(l-- > 0) {
headB = headB.next;
}
}
else{
int l = lenA - lenB;
while(l-- > 0) {
headA = headA.next;
}
}
while(headA != null && headB != null) {
if(headA == headB) return headA;
headA = headA.next;
headB = headB.next;
}
return null;
}
}
環形鏈表
給定一個鏈表,判斷鏈表中是否有環。
如果鏈表中有某個節點,可以通過連續跟蹤 next 指針再次到達,則鏈表中存在環。 為了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。注意:pos 不作為參數進行傳遞,僅僅是為了標識鏈表的實際情況。
如果鏈表中存在環,則返回 true 。 否則,返回 false 。
進階:
你能用 O(1)(即,常量)內存解決此問題嗎?
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:true
解釋:鏈表中有一個環,其尾部連接到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0
輸出:true
解釋:鏈表中有一個環,其尾部連接到第一個節點。
示例 3:
輸入:head = [1], pos = -1
輸出:false
解釋:鏈表中沒有環。
提示:
鏈表中節點的數目范圍是 [0, 104]
-105 <= Node.val <= 105
pos 為 -1 或者鏈表中的一個 有效索引 。
快慢指針法:
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null) return false;
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) return true;
}
return false;
}
}
給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 null。
為了表示給定鏈表中的環,我們使用整數 pos 來表示鏈表尾連接到鏈表中的位置(索引從 0 開始)。 如果 pos 是 -1,則在該鏈表中沒有環。注意,pos 僅僅是用於標識環的情況,並不會作為參數傳遞到函數中。
說明:不允許修改給定的鏈表。
進階:
你是否可以使用 O(1) 空間解決此題?
示例 1:
輸入:head = [3,2,0,-4], pos = 1
輸出:返回索引為 1 的鏈表節點
解釋:鏈表中有一個環,其尾部連接到第二個節點。
示例 2:
輸入:head = [1,2], pos = 0
輸出:返回索引為 0 的鏈表節點
解釋:鏈表中有一個環,其尾部連接到第一個節點。
示例 3:
輸入:head = [1], pos = -1
輸出:返回 null
解釋:鏈表中沒有環。
提示:
鏈表中節點的數目范圍在范圍 [0, 104] 內
-105 <= Node.val <= 105
pos 的值為 -1 或者鏈表中的一個有效索引
public class Solution {
public ListNode detectCycle(ListNode head) {
if(head == null || head.next == null) return null;
ListNode fast = head;
ListNode slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if(fast == slow) {
break;
}
}
if(fast == null || fast.next == null) return null;
fast = head;
while(fast != slow) {
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
奇偶鏈表
給定一個單鏈表,把所有的奇數節點和偶數節點分別排在一起。請注意,這里的奇數節點和偶數節點指的是節點編號的奇偶性,而不是節點的值的奇偶性。
請嘗試使用原地算法完成。你的算法的空間復雜度應為 O(1),時間復雜度應為 O(nodes),nodes 為節點總數。
示例 1:
輸入: 1->2->3->4->5->NULL
輸出: 1->3->5->2->4->NULL
示例 2:
輸入: 2->1->3->5->6->4->7->NULL
輸出: 2->3->6->7->1->5->4->NULL
說明:
應當保持奇數節點和偶數節點的相對順序。
鏈表的第一個節點視為奇數節點,第二個節點視為偶數節點,以此類推。
class Solution {
public ListNode oddEvenList(ListNode head) {
if(head == null || head.next == null) return head;
ListNode evenHead = head.next;
ListNode odd = head;
ListNode even = evenHead;
while(even != null && even.next != null) {
odd.next = odd.next.next;
odd = even.next;
even.next = even.next.next;
even = odd.next;
}
odd.next = evenHead;
return head;
}
}
回文鏈表
給你一個單鏈表的頭節點 head ,請你判斷該鏈表是否為回文鏈表。如果是,返回 true ;否則,返回 false 。
示例 1:
輸入:head = [1,2,2,1]
輸出:true
示例 2:
輸入:head = [1,2]
輸出:false
提示:
鏈表中節點數目在范圍[1, 105] 內
0 <= Node.val <= 9
進階:你能否用 O(n) 時間復雜度和 O(1) 空間復雜度解決此題?
思路:后半段鏈表反轉,一個指針指開頭,一個指針指中間,雙指針掃描
class Solution {
public boolean isPalindrome(ListNode head) {
if(head.next == null) return true;
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode l = dummy;
ListNode r = dummy;
while(r != null && r.next != null) {
l = l.next;
r = r.next.next;
}
ListNode a = l.next;
ListNode b = a.next;
while(b != null) {
ListNode tmp = b.next;
b.next = a;
a = b;
b = tmp;
}
l.next.next = b;
l.next = a;
ListNode right = l.next;
ListNode left = dummy.next;
while(right != null) {
if(right.val != left.val) return false;
left = left.next;
right = right.next;
}
return true;
}
}
LRU 緩存機制
運用你所掌握的數據結構,設計和實現一個 LRU (最近最少使用) 緩存機制 。
實現 LRUCache 類:
LRUCache(int capacity) 以正整數作為容量 capacity 初始化 LRU 緩存
int get(int key) 如果關鍵字 key 存在於緩存中,則返回關鍵字的值,否則返回 -1 。
void put(int key, int value) 如果關鍵字已經存在,則變更其數據值;如果關鍵字不存在,則插入該組「關鍵字-值」。當緩存容量達到上限時,它應該在寫入新數據之前刪除最久未使用的數據值,從而為新的數據值留出空間。
進階:你是否可以在 O(1) 時間復雜度內完成這兩種操作?
示例:
輸入
["LRUCache", "put", "put", "get", "put", "get", "put", "get", "get", "get"]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]
輸出
[null, null, null, 1, null, -1, null, -1, 3, 4]
解釋
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 緩存是 {1=1}
lRUCache.put(2, 2); // 緩存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 該操作會使得關鍵字 2 作廢,緩存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 該操作會使得關鍵字 1 作廢,緩存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4
提示:
1 <= capacity <= 3000
0 <= key <= 10000
0 <= value <= 105
最多調用 2 * 105 次 get 和 put
class LRUCache {
Map<Integer,Node> map;
int n;
Node head;
Node tail;
class Node{
int k,v;
Node l,r;
Node(int key, int value){
this.k = key;
this.v = value;
}
}
public LRUCache(int capacity) {
n = capacity;
map = new HashMap<>();
head = new Node(-1, -1);
tail = new Node(-1, -1);
head.r = tail;
tail.l = head;
}
public int get(int key) {
if(map.containsKey(key)) {
Node node = map.get(key);
refresh(node);
return node.v;
}
return -1;
}
public void put(int key, int value) {
Node node = null;
if(map.containsKey(key)) {
node = map.get(key);
node.v = value;
}else {
if(n == map.size()) {
Node del = tail.l;
map.remove(del.k);
delete(del);
}
node = new Node(key, value);
map.put(key, node);
}
refresh(node);
}
public void refresh(Node node) {
delete(node);
node.r = head.r;
node.l = head;
head.r.l = node;
head.r = node;
}
public void delete(Node node) {
if(node.l != null) {
Node tmp = node.r;
node.l.r = node.r;
tmp.l = node.l;
}
}
}