3737
3838 假如淘汰逻辑要做到 O(1) 复杂度, 我们可以引入一个链表, 每次 touch 一个值时, 就删掉它重新 push_back, 而当达到容量要驱逐时, 则 pop_front
3939
40- Rust 的链表不支持根据引用删除任意元素,这个工程实现还是有点挑战的, 晚点再做
40+ Rust 的链表不支持根据引用删除任意元素,也没有 LinkedHashMap,需要自己实现一个
4141 */
4242use std:: collections:: HashMap ;
43- use std:: collections:: LinkedList ;
44- struct LRUCache {
45- cache : HashMap < i32 , i32 > ,
46- vec : Vec < i32 > ,
47- cap : i32 ,
43+ use std:: ptr;
44+ use std:: mem;
45+
46+ // Entry is either a map entry and a link-list node
47+ pub struct LRUEntry {
48+ key : i32 ,
49+ val : i32 ,
50+ prev : * mut LRUEntry ,
51+ next : * mut LRUEntry ,
4852}
4953
50- /**
51- * `&self` means the method takes an immutable reference.
52- * If you need a mutable reference, change it to `&mut self` instead.
53- */
54- impl LRUCache {
54+ impl LRUEntry {
55+ pub fn new ( key : i32 , val : i32 ) -> Self {
56+ LRUEntry {
57+ key : key,
58+ val : val,
59+ prev : ptr:: null_mut ( ) ,
60+ next : ptr:: null_mut ( ) ,
61+ }
62+ }
63+ }
64+
65+ pub struct LRUCache {
66+ map : HashMap < i32 , Box < LRUEntry > > ,
67+ cap : usize ,
5568
56- fn new ( capacity : i32 ) -> Self {
57- LRUCache {
58- cache : HashMap :: with_capacity ( capacity as usize , ) ,
59- vec : Vec :: with_capacity ( capacity as usize ) ,
69+ // head and tail is dummy node of the double-linked-list
70+ head : * mut LRUEntry ,
71+ tail : * mut LRUEntry ,
72+ }
73+
74+ impl LRUCache {
75+ pub fn new ( capacity : i32 ) -> Self {
76+ let capacity = capacity as usize ;
77+ let map = HashMap :: with_capacity ( capacity) ;
78+ let cache = LRUCache {
79+ map : map,
6080 cap : capacity,
81+ head : unsafe { Box :: into_raw ( Box :: new ( mem:: uninitialized :: < LRUEntry > ( ) ) ) } ,
82+ tail : unsafe { Box :: into_raw ( Box :: new ( mem:: uninitialized :: < LRUEntry > ( ) ) ) } ,
83+ } ;
84+ unsafe {
85+ ( * cache. head ) . next = cache. tail ;
86+ ( * cache. tail ) . prev = cache. head ;
6187 }
88+
89+ cache
6290 }
6391
64- fn get ( & mut self , key : i32 ) -> i32 {
65- let cache = HashMap :: new ( ) ;
66- let list = Vec :: new ( ) ;
92+ pub fn get ( & mut self , key : i32 ) -> i32 {
93+ let ( ptr, val) = match self . map . get_mut ( & key) {
94+ None => ( None , None ) ,
95+ Some ( entry) => {
96+ let ptr: * mut LRUEntry = & mut * * entry;
97+ ( Some ( ptr) , Some ( unsafe { ( * entry) . val } ) )
98+ }
99+ } ;
100+
101+ if let Some ( ptr) = ptr {
102+ self . detach ( ptr) ;
103+ self . push ( ptr) ;
104+ }
105+ val. unwrap_or ( -1 )
67106 }
68107
69- fn put ( & mut self , key : i32 , value : i32 ) {
70-
108+ pub fn put ( & mut self , key : i32 , value : i32 ) {
109+ let ptr = self . map . get_mut ( & key) . map ( |entry| {
110+ let ptr: * mut LRUEntry = & mut * * entry;
111+ ptr
112+ } ) ;
113+
114+ match ptr {
115+ Some ( ptr) => {
116+ // key already exist, update it
117+ unsafe { ( * ptr) . val = value } ;
118+ self . detach ( ptr) ;
119+ self . push ( ptr) ;
120+ }
121+ None => {
122+ // insert new key, cache is full, evict
123+ if self . map . len ( ) == self . cap {
124+ let mut old_entry = self . pop ( ) . unwrap ( ) ;
125+ old_entry. key = key;
126+ old_entry. val = value;
127+ self . push ( & mut * old_entry) ;
128+ self . map . insert ( key, old_entry) ;
129+ } else {
130+ let mut new_entry = Box :: new ( LRUEntry :: new ( key, value) ) ;
131+ self . push ( & mut * new_entry) ;
132+ self . map . insert ( key, new_entry) ;
133+ }
134+ }
135+ }
136+ }
137+
138+ // pop() remove the entry from map, detach the entry from head of linked-list, and return it
139+ fn pop ( & mut self ) -> Option < Box < LRUEntry > > {
140+ let next;
141+ unsafe { next = ( * self . head ) . next }
142+ // list is empty
143+ if next == self . tail {
144+ return None
145+ }
146+ let key = unsafe { ( * next) . key } ;
147+ let mut old_entry = self . map . remove ( & key) . unwrap ( ) ;
148+ self . detach ( & mut * old_entry) ;
149+ Some ( old_entry)
150+ }
151+
152+ // push() pushs an entry to the tail of linked-list
153+ fn push ( & mut self , entry : * mut LRUEntry ) {
154+ unsafe {
155+ // prev <-> tail
156+ // prev <-> entry <-> tail
157+ ( * entry) . prev = ( * self . tail ) . prev ;
158+ ( * entry) . next = self . tail ;
159+ ( * self . tail ) . prev = entry;
160+ ( * ( * entry) . prev ) . next = entry;
161+ }
162+ }
163+
164+ // detach() remove an entry from the linked-list
165+ fn detach ( & mut self , entry : * mut LRUEntry ) {
166+ unsafe {
167+ // prev <-> entry <-> next
168+ // prev <-> next
169+ ( * ( * entry) . prev ) . next = ( * entry) . next ;
170+ ( * ( * entry) . next ) . prev = ( * entry) . prev ;
171+ }
71172 }
72173}
73174
@@ -79,5 +180,20 @@ mod tests {
79180
80181 #[ test]
81182 fn test_146 ( ) {
183+
184+ println ! ( "init cache" ) ;
185+ let mut lru_cache = LRUCache :: new ( 2 ) ;
186+ lru_cache. put ( 1 , 1 ) ;
187+ lru_cache. put ( 2 , 2 ) ;
188+ println ! ( "return 1" ) ;
189+ assert_eq ! ( lru_cache. get( 1 ) , 1 ) ; // returns 1
190+ println ! ( "evict key 2" ) ;
191+ lru_cache. put ( 3 , 3 ) ; // evicts key 2
192+ println ! ( "return -1" ) ;
193+ assert_eq ! ( lru_cache. get( 2 ) , -1 ) ; // returns -1 (not found)
194+ lru_cache. put ( 4 , 4 ) ; // evicts key 1
195+ assert_eq ! ( lru_cache. get( 1 ) , -1 ) ; // returns -1 (not found)
196+ assert_eq ! ( lru_cache. get( 3 ) , 3 ) ; // returns 3
197+ assert_eq ! ( lru_cache. get( 4 ) , 4 ) ; // returns 4
82198 }
83199}
0 commit comments