链表相关
翻转链表
1 2 3 4 5 6 7 8 9 10
| ListNode reverseList(ListNode head) { ListNode pre = null, cur = head; while (cur != null) { ListNode next = cur.next; cur.next = pre; pre = cur; cur = next; } return pre; }
|
快慢指针找中间节点
1 2 3 4 5 6 7 8
| ListNode getMidNode(ListNode head) { ListNode fast = head, slow = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } return slow; }
|
环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
快慢指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class Solution { public boolean hasCycle(ListNode head) { if (head == null) return false; ListNode slow = head, fast = head; while (fast != null) { slow = slow.next; fast = fast.next; if (fast == null) return false; fast = fast.next; if (slow == fast) break; } return fast != null; } }
|
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
快慢指针+找入环点
graph LR
A(slow1\nfast1) --> B(slow2\nfast3) --> C(slow3\nfast2) --> D(slow4\nfast4\n相遇)
D --> B
令起点到环入口的距离为x
,环入口到相遇点的距离为y
,环的长度为loop
,则有:
slow = x + y
fast = x + y + n * loop = 2 * slow = 2x + 2y
x + y = n * loop = slow
x = n * loop - y;
所以让起点和fast节点同时行走,相遇时的位置就是入环点
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| public class Solution { public ListNode detectCycle(ListNode head) { if (head == null) return null; ListNode res = head, slow = head, fast = head; while (fast != null) { slow = slow.next; fast = fast.next; if (fast == null) return null; fast = fast.next; if (slow == fast) break; } if (fast == null) return null; while (res != slow) { res = res.next; slow = slow.next; } return res; } }
|
Dummy节点
利用Dummy节点可以很好的获得和维护首尾节点
请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。
实现 LRUCache
类:
LRUCache(int capacity)
以 正整数 作为容量 capacity
初始化 LRU 缓存
int get(int key)
如果关键字 key
存在于缓存中,则返回关键字的值,否则返回 -1
。
void put(int key, int value)
如果关键字 key
已经存在,则变更其数据值 value
;如果不存在,则向缓存中插入该组 key-value
。如果插入操作导致关键字数量超过 capacity
,则应该 逐出 最久未使用的关键字。
函数 get
和 put
必须以 O(1)
的平均时间复杂度运行。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 输入 ["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 <= 10^5
- 最多调用
2 * 10^5
次 get
和 put
双向链表+哈希表
使用dummy head和dummy tail后,就不用考虑将Node移到首位以及将Node从尾部删除的特殊情况
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| class LRUCache { class Node { Node next, last; int key, val; } Node head = new Node(), tail = new Node(); int capacity; Map<Integer, Node> map = new HashMap<>(); public LRUCache(int capacity) { this.capacity = capacity; head.next = tail; tail.last = head; } public int get(int key) { Node cur = map.get(key); if (cur == null) return -1; update(cur); return cur.val; } public void put(int key, int value) { Node cur = map.get(key); if (cur == null) { cur = new Node(); cur.key = key; cur.val = value; moveToHead(cur); map.put(key, cur); if (map.size() > capacity) { map.remove(tail.last.key); delete(tail.last); } } else { cur.val = value; update(cur); } } public void delete(Node cur) { cur.last.next = cur.next; cur.next.last = cur.last; } public void moveToHead(Node cur) { cur.next = head.next; head.next.last = cur; cur.last = head; head.next = cur; } public void update(Node cur) { delete(cur); moveToHead(cur); } }
|