【數據結構與算法】鏈表模板及例題


鏈表模板

鏈表結構

  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個節點

鏈表節點交換

image

image

ListNode tmp = pos.next;
pos.next = cur;
cur.next = tmp;
pre.next = pos;

鏈表反轉

整個鏈表反轉

image
🌈 法一:使用虛擬頭節點

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個節點之間的鏈表

image

    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;

例題

兩數相加

LeetCode 2.兩數相加

image

給你兩個 非空 的鏈表,表示兩個非負的整數。它們每位數字都是按照 逆序 的方式存儲的,並且每個節點只能存儲 一位 數字。

請你將兩個數相加,並以相同形式返回一個表示和的鏈表。

你可以假設除了數字 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 個結點

LeetCode 19.刪除鏈表的倒數第 N 個結點

image

給你一個鏈表,刪除鏈表的倒數第 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/

image

將兩個升序鏈表合並為一個新的 升序 鏈表並返回。新鏈表是通過拼接給定的兩個鏈表的所有節點組成的。 

 

示例 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個升序鏈表

LeetCode 23.合並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;
    }
}

兩兩交換鏈表中的節點

LeetCode 24.兩兩交換鏈表中的節點

image

給定一個鏈表,兩兩交換其中相鄰的節點,並返回交換后的鏈表。

你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。

 

示例 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 個一組翻轉鏈表

LeetCode 25.K 個一組翻轉鏈表

image

image

給你一個鏈表,每 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;
    }
}

旋轉鏈表

LeetCode 61.旋轉鏈表
image

image

給你一個鏈表的頭節點 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

image
思路:

  • 注意到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;
    }
}

刪除排序鏈表中的重復元素

LeetCode 82.刪除鏈表中的重復元素
image

存在一個按升序排列的鏈表,給你這個鏈表的頭節點 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;
    }
}

LeetCode 83. 刪除鏈表中的重復元素
image

存在一個按升序排列的鏈表,給你這個鏈表的頭節點 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;
    }
}

相交鏈表

LeetCode 160. 相交鏈表
image

image

給你兩個單鏈表的頭節點 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;
    }
}

環形鏈表

LeetCode 141.環形鏈表

image

	給定一個鏈表,判斷鏈表中是否有環。

如果鏈表中有某個節點,可以通過連續跟蹤 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;
    }
}

LeetCode 142. 環形鏈表 II

給定一個鏈表,返回鏈表開始入環的第一個節點。 如果鏈表無環,則返回 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;
    }
}

奇偶鏈表

LeetCode 328.奇偶鏈表

給定一個單鏈表,把所有的奇數節點和偶數節點分別排在一起。請注意,這里的奇數節點和偶數節點指的是節點編號的奇偶性,而不是節點的值的奇偶性。

請嘗試使用原地算法完成。你的算法的空間復雜度應為 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;
    }
}

回文鏈表

LeetCode 234. 回文鏈表

給你一個單鏈表的頭節點 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 緩存機制

LeetCode 146.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;
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM