Skip to content

Commit 3c42db4

Browse files
authored
Merge pull request #237 from WAMaker/master
[LinkedList] Add a solution to LRU Cache
2 parents 7a3e77e + c46f4f7 commit 3c42db4

File tree

3 files changed

+232
-1
lines changed

3 files changed

+232
-1
lines changed

LinkedList/LFUCache.swift

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/**
2+
* Question Link: https://leetcode.com/problems/lfu-cache/
3+
* Primary idea: Use linked list and hash map to build the cache.
4+
* Time Complexity Per Action: O(1), Space Complexity: O(K)
5+
*
6+
*/
7+
8+
class LFUCache {
9+
10+
private let capacity: Int
11+
private var nodeMap = [Int: CacheNode]()
12+
private var listMap = [Int: CacheList]()
13+
private var size = 0
14+
private var leastFrequency = 1
15+
16+
init(_ capacity: Int) {
17+
self.capacity = capacity
18+
}
19+
20+
func get(_ key: Int) -> Int {
21+
guard let node = nodeMap[key], let list = listMap[node.count] else {
22+
return -1
23+
}
24+
updateExsit(node: node)
25+
return node.val
26+
}
27+
28+
func put(_ key: Int, _ value: Int) {
29+
if capacity == 0 {
30+
return
31+
}
32+
33+
if let node = nodeMap[key], let list = listMap[node.count] {
34+
node.val = value
35+
updateExsit(node: node)
36+
} else {
37+
removeCacheIfNeeded()
38+
39+
let node = CacheNode(key, value)
40+
nodeMap[key] = node
41+
listMap(add: node)
42+
43+
size += 1
44+
leastFrequency = 1
45+
}
46+
}
47+
48+
private func updateExsit(node: CacheNode) {
49+
guard let list = listMap[node.count] else {
50+
return
51+
}
52+
list.remove(node)
53+
54+
if list.isEmpty {
55+
listMap[node.count] = nil
56+
57+
if leastFrequency == node.count {
58+
leastFrequency += 1
59+
}
60+
}
61+
62+
node.count += 1
63+
listMap(add: node)
64+
}
65+
66+
private func removeCacheIfNeeded() {
67+
guard size >= capacity, let list = listMap[leastFrequency], let key = list.removeLast()?.key else {
68+
return
69+
}
70+
size -= 1
71+
nodeMap[key] = nil
72+
if list.isEmpty {
73+
listMap[leastFrequency] = nil
74+
}
75+
}
76+
77+
private func listMap(add node: CacheNode) {
78+
let list = listMap[node.count, default: CacheList()]
79+
list.add(node)
80+
listMap[node.count] = list
81+
}
82+
83+
}
84+
85+
class CacheList {
86+
87+
private let head = CacheNode(0, 0)
88+
private let tail = CacheNode(0, 0)
89+
private var count = 0
90+
91+
var isEmpty: Bool {
92+
return count <= 0
93+
}
94+
95+
init() {
96+
head.next = tail
97+
tail.pre = head
98+
}
99+
100+
func add(_ node: CacheNode) {
101+
head.next?.pre = node
102+
node.next = head.next
103+
node.pre = head
104+
head.next = node
105+
count += 1
106+
}
107+
108+
109+
func remove(_ node: CacheNode) {
110+
node.pre?.next = node.next
111+
node.next?.pre = node.pre
112+
node.next = nil
113+
node.pre = nil
114+
count -= 1
115+
}
116+
117+
func removeLast() -> CacheNode? {
118+
guard !isEmpty, let node = tail.pre else {
119+
return nil
120+
}
121+
remove(node)
122+
return node
123+
}
124+
125+
}
126+
127+
class CacheNode {
128+
let key: Int
129+
var val: Int
130+
var pre: CacheNode?
131+
var next: CacheNode?
132+
var count = 1
133+
134+
init(_ key: Int, _ val: Int) {
135+
self.key = key
136+
self.val = val
137+
}
138+
}

LinkedList/LRUCache.swift

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/**
2+
* Question Link: https://leetcode.com/problems/lru-cache/
3+
* Primary idea: Use linked list and hash map to build the cache.
4+
* Time Complexity Per Action: O(1), Space Complexity: O(K)
5+
*
6+
*/
7+
8+
class LRUCache {
9+
10+
private let capacity: Int
11+
private var count = 0
12+
13+
private let head = LRUCacheNode(0, 0)
14+
private let tail = LRUCacheNode(0, 0)
15+
16+
private var dict = [Int: LRUCacheNode]()
17+
18+
init(_ capacity: Int) {
19+
self.capacity = capacity
20+
21+
head.next = tail
22+
tail.pre = head
23+
}
24+
25+
func get(_ key: Int) -> Int {
26+
if let node = dict[key] {
27+
remove(key)
28+
insert(node)
29+
return node.val
30+
}
31+
return -1
32+
}
33+
34+
func put(_ key: Int, _ value: Int) {
35+
if let node = dict[key] {
36+
node.val = value
37+
remove(key)
38+
insert(node)
39+
return
40+
}
41+
42+
let node = LRUCacheNode(key, value)
43+
dict[key] = node
44+
if count == capacity, let tailKey = tail.pre?.key {
45+
remove(tailKey)
46+
}
47+
insert(node)
48+
}
49+
50+
private func insert(_ node: LRUCacheNode) {
51+
dict[node.key] = node
52+
53+
node.next = head.next
54+
head.next?.pre = node
55+
node.pre = head
56+
head.next = node
57+
58+
count += 1
59+
}
60+
61+
private func remove(_ key: Int) {
62+
guard count > 0, let node = dict[key] else {
63+
return
64+
}
65+
dict[key] = nil
66+
67+
node.pre?.next = node.next
68+
node.next?.pre = node.pre
69+
node.pre = nil
70+
node.next = nil
71+
72+
count -= 1
73+
}
74+
75+
}
76+
77+
fileprivate class LRUCacheNode {
78+
79+
let key: Int
80+
var val: Int
81+
82+
var pre: LRUCacheNode?
83+
var next: LRUCacheNode?
84+
85+
init(_ key: Int, _ val: Int) {
86+
self.key = key
87+
self.val = val
88+
}
89+
90+
}

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,8 @@
142142
[Merge Two Sorted Lists](https://leetcode.com/problems/merge-two-sorted-lists/)| [Swift](./LinkedList/MergeTwoSortedLists.swift)| Easy| O(n)| O(1)|
143143
[Merge k Sorted Lists](https://leetcode.com/problems/merge-k-sorted-lists/)| [Swift](./LinkedList/MergeKSortedLists.swift)| Hard| O(mlogn)| O(1)|
144144
[Partition List](https://leetcode.com/problems/partition-list/)| [Swift](./LinkedList/PartitionList.swift)| Medium| O(n)| O(1)|
145+
[LRU Cache](https://leetcode.com/problems/lru-cache/) | [Swift](./LinkedList/LRUCache.swift) | Hard| O(1)| O(1)|
146+
[LFU Cache](https://leetcode.com/problems/lfu-cache/) | [Swift](./LinkedList/LFUCache.swift) | Hard| O(1)| O(1)|
145147

146148
## Stack
147149
| Title | Solution | Difficulty | Time | Space |
@@ -476,6 +478,7 @@
476478
## Problem Status
477479
| Solution | Number | Title | Difficulty |
478480
| -------- | ------ | ----- | ---------- |
481+
| [Swift](./LinkedList/LFUCache.swift) | 460 | [LFU Cache](https://oj.leetcode.com/problems/lfu-cache/) | Hard |
479482
| [Swift](./DFS/CombinationSumIV.swift) | 377 | [Combination Sum IV](https://leetcode.com/problems/combination-sum-iv/) | Medium
480483
| | 376 | [Wiggle Subsequence](https://leetcode.com/problems/wiggle-subsequence/) | Medium
481484
| [Swift](./DP/GuessNumberHigherOrLowerII.swift) | 375 | [Guess Number Higher or Lower II](https://leetcode.com/problems/guess-number-higher-or-lower-ii/) | Medium
@@ -690,7 +693,7 @@
690693
| | 149 | [Max Points on a Line](https://oj.leetcode.com/problems/max-points-on-a-line/) | Hard |
691694
| | 148 | [Sort List](https://oj.leetcode.com/problems/sort-list/) | Medium |
692695
| | 147 | [Insertion Sort List](https://oj.leetcode.com/problems/insertion-sort-list/) | Medium |
693-
| | 146 | [LRU Cache](https://oj.leetcode.com/problems/lru-cache/) | Hard |
696+
| [Swift](./LinkedList/LRUCache.swift) | 146 | [LRU Cache](https://oj.leetcode.com/problems/lru-cache/) | Hard |
694697
| [Swift](./Stack/PostorderTraversal.swift) | 145 | [Binary Tree Postorder Traversal](https://oj.leetcode.com/problems/binary-tree-postorder-traversal/) | Hard |
695698
| [Swift](./Stack/PreorderTraversal.swift) | 144 | [Binary Tree Preorder Traversal](https://oj.leetcode.com/problems/binary-tree-preorder-traversal/) | Medium |
696699
| [Swift](./LinkedList/ReorderList.swift) | 143 | [Reorder List](https://oj.leetcode.com/problems/reorder-list/) | Medium |

0 commit comments

Comments
 (0)