"Leetcode": the design and implementation of LRU cache_ cache elimination algorithm LRU

Source: Internet
Author: User
Tags time 0

The memory-culling algorithm is a more important algorithm and is often asked: what would you do if you were to design an LRU algorithm? Keep your access as efficient as possible. Then, according to this algorithm problem, LRU is a simple introduction.

Title Address: https://oj.leetcode.com/problems/lru-cache/


1. What is the LRU algorithm? Why use LRU?

LRU is the abbreviation for least recently used, which is the least recently used meaning. We know that in the memory space is limited, then when the memory is full of data, and the data need to be accessed is not in memory, it is necessary to eliminate the least used data in memory, to make room for the newly added data. LRU is often used to retire data in the cache server, which requires high data access speed, so an efficient algorithm is designed.


2. The requirements of the topic are as follows:

Design and implement a data structure for Least recently Used (LRU) cache. It should support the following operations: get and set .

get (key)  -get the value ('ll always be positive) of the key if the key ex Ists in the cache, otherwise return-1.
set (key, value)  -set or insert the value if the key is not already present . When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

Implement an LRU algorithm that implements get and set operations.

get (Key)--if key is present in the cache, returns the value of key corresponding to 1.

Set (Key,value)--if key is not in the cache, insert or set value. When the cache reaches the storage space, remove the least recently used element before inserting a new value.

3. Algorithm design:

Note that it is important to be efficient, efficient, and efficient. The time complexity for insertion and deletion as far as possible is O (1). So how to design data structures to achieve? Don't worry, first say two relatively inefficient implementations.

3.1: Pure Map Implementation (timeout)

With a pure map implementation, key is key,value as a struct, the inside is the value and time (that is, when the distance is 0, the less the key, then the smaller).

Set (key,value): If the length is less than capacity, then put in, time 0, while all the other keys corresponding to the duration minus 1, if equal to capacity, then to find the smallest time corresponding key and then delete. Get (key): if obtained, then the time value corresponding to the key is set to 0, the other key corresponding to the time value minus 1. Timeout: the algorithm in O (1) (Accurate is O (Logn), with Unordered_map can do O (1)) time to get the key value exists, at Get (key), convenient, but also to all time minus 1, This is a time-consuming operation.
Show the Code:
Class lrucache{private:typedef struct value{int val;int times;} Tvalue;map<int,tvalue> mkv;map<int,tvalue>:: Iterator it;int Capacity;public:lrucache (int capacity) { This->capacity = capacity;} int get (int key) {if (Is_exist (key)) {mkv[key].times = 0;it = Mkv.begin (); while (It!=mkv.end ()) {if (It->first = = key) {It ++;continue;} (It->second). Times-= 1;it++;} return mkv[key].val;} else{return-1;}} void set (int key, int value) {if (Is_exist (key)) return; int mint,mink;if (Mkv.size () ==capacity) {it = Mkv.begin (); mint = ( It->second). Times;mink = It->first;it++;while (It!=mkv.end ()) {if ((It->second). Times < mint) {mint = (it- >second). Times;mink = It->first;} it++;} Mkv.erase (mink);} Mkv[key].val = Value;mkv[key].times = 0;it = Mkv.begin (); while (It!=mkv.end ()) {if (It->first = = key) {it++;continue;} It->second.times-= 1;it++;}} BOOL Is_exist (int key) {return (Mkv.find (key)! = Mkv.end ());} void print () {it = Mkv.begin (); while (It!=mkv.end ()) {cout<< "key=" &LT;&Lt;it->first<< ", value=" <<it->second.val<< ", times=" <<it->second.times<< endl;it++;} cout<<endl;}};

3.2: implemented with single-linked list (single-linked list) get (int key):{1. If key is present in the cache, then the corresponding value value is returned. 1.1 and take this <key,value> pair down to the first node of the list. 2. If key does not exist in the cache, then return-1} Set (int key,int value){1. If the key value already exists in the list, then return;2. If the total length of the linked list is not capacity, insert the new node into the table header3. If the total length of the linked list equals capacity, then the last node is deleted and the new node is inserted into the table header} Timeout : The order of the list and is important order, the tail node must be least recently used node. It is time-consuming to determine if key exists to traverse through, and it is also time-consuming to find the tail node to traverse through. Show the code:
Class Lrucache{private:int capacity;int capacity_cur;typedef struct listnode{int key;int value;struct ListNode* next; struct listnode* pre; ListNode (int k, int v): Key (k), Value (v), Next (null), pre (null) {}}listnode; ListNode *head;typedef struct Value{int val; ListNode *p; Value (int _v,listnode *pp): Val (_v), P (PP) {}value () {}//if This constructor are not defined,there would be wrong}tvalue;map& Lt Int,tvalue > mkv;map< int,tvalue >:: Iterator it;public:lrucache (int capacity) {this->capacity = capacity; Head = new ListNode (0,0); capacity_cur = 0;} int get (int key) {if (Is_exist (key)) {//get down the loc nodelistnode *loc = Mkv[key].p;loc->pre->next = Loc->ne Xt;loc->next->pre = Loc->pre;//insert into the front of the Headloc->next = Head->next;loc->pre = Head;h Ead->next = Loc;loc->next->pre = Loc;return mkv[key].val;} else{return-1;}} void set (int key, int value) {if (Is_exist (key)) {mkv[key].val = value; ListNode *q = Mkv[key].p;q->value = value;return;} ListNode *tmp = new ListNode (key,value), if (capacity_cur<capacity) {if (head->next==null)//the list is empty{head- >next = tmp; head->pre = Tmp;tmp->next = Head;tmp->pre = Head;tvalue TV (value,tmp); Mkv[key] = Tv;++capacity_cu R;} Else//insert the TMP into the front of the List{tmp->next = Head->next;tmp->pre = Head;head->next->pre = t Mp;head->next = Tmp;tvalue TV (value,tmp); Mkv[key] = Tv;++capacity_cur;}} Else{//get rid of the LRU nodelistnode *tail = Head->pre;head->pre = Tail->pre;tail->pre->next = HEAD;MKV.E Rase (Tail->key);d elete Tail;//insert into the new Nodetmp->next = Head->next;tmp->pre = head;head->next- >pre = Tmp;head->next = Tmp;tvalue TV (value,tmp); Mkv[key] = TV;}} BOOL Is_exist (int key) {return (Mkv.find (key)! = Mkv.end ());} void print () {ListNode *p = head->next;while (p!=head) {cout<< "key =" <<p->key<< "Value =" < <p->value<<endl;p = P->next;} Cout<<endl;}}; 


So how can I get and set operations in the time of O (1)? In fact, for the second implementation, we only need to be able to get the position of the element in the linked list in the time complexity of O (1). So we use the data structure of map and list to operate. Map is used for the location of elements, list lists to maintain the content and order of elements.
3.3: Bidirectional linked list +MAP implementation ( space Change time ) Two-way list: store key,value structure, and can find the tail node in O (1) Time , and then quickly delete the tail node. Map (can also use unordered_map): store Key,value and key in the linked list of pointers, the purpose is to quickly determine the existence of key, followed by the existence of the case can quickly find the key corresponding to the pointer in the list, Quickly remove it and plug it into the tail.
AC Code:
Class Lrucache{private:int capacity;int capacity_cur;typedef struct listnode{int key;int value;struct ListNode* next; struct listnode* pre; ListNode (int k, int v): Key (k), Value (v), Next (null), pre (null) {}}listnode; ListNode *head;typedef struct Value{int val; ListNode *p; Value (int _v,listnode *pp): Val (_v), P (PP) {}value () {}//if This constructor are not defined,there would be wrong}tvalue;map& Lt Int,tvalue > mkv;map< int,tvalue >:: Iterator it;public:lrucache (int capacity) {this->capacity = capacity; Head = new ListNode (0,0); capacity_cur = 0;} int get (int key) {if (Is_exist (key)) {//get down the loc nodelistnode *loc = Mkv[key].p;loc->pre->next = Loc->ne Xt;loc->next->pre = Loc->pre;//insert into the front of the Headloc->next = Head->next;loc->pre = Head;h Ead->next = Loc;loc->next->pre = Loc;return mkv[key].val;} else{return-1;}} void set (int key, int value) {if (Is_exist (key)) {mkv[key].val = value; ListNode *q = Mkv[key].p;q->value = value;return;} ListNode *tmp = new ListNode (key,value), if (capacity_cur<capacity) {if (head->next==null)//the list is empty{head- >next = tmp; head->pre = Tmp;tmp->next = Head;tmp->pre = Head;tvalue TV (value,tmp); Mkv[key] = Tv;++capacity_cu R;} Else//insert the TMP into the front of the List{tmp->next = Head->next;tmp->pre = Head;head->next->pre = t Mp;head->next = Tmp;tvalue TV (value,tmp); Mkv[key] = Tv;++capacity_cur;}} Else{//get rid of the LRU nodelistnode *tail = Head->pre;head->pre = Tail->pre;tail->pre->next = HEAD;MKV.E Rase (Tail->key);d elete Tail;//insert into the new Nodetmp->next = Head->next;tmp->pre = head;head->next- >pre = Tmp;head->next = Tmp;tvalue TV (value,tmp); Mkv[key] = TV;}} BOOL Is_exist (int key) {return (Mkv.find (key)! = Mkv.end ());} void print () {ListNode *p = head->next;while (p!=head) {cout<< "key =" <<p->key<< "Value =" < <p->value<<endl;p = P->next;} Cout<<endl;}}; 

Complete test Code:
#include <iostream> #include <vector> #include <map>using namespace Std;class lrucache{private:int Capacity;int capacity_cur;typedef struct listnode{int key;int value;struct listnode* next;struct ListNode* pre; ListNode (int k, int v): Key (k), Value (v), Next (null), pre (null) {}}listnode; ListNode *head;typedef struct Value{int val; ListNode *p; Value (int _v,listnode *pp): Val (_v), P (PP) {}value () {}//if This constructor are not defined,there would be wrong}tvalue;map& Lt Int,tvalue > mkv;map< int,tvalue >:: Iterator it;public:lrucache (int capacity) {this->capacity = capacity; Head = new ListNode (0,0); capacity_cur = 0;} int get (int key) {if (Is_exist (key)) {//get down the loc nodelistnode *loc = Mkv[key].p;loc->pre->next = Loc->ne Xt;loc->next->pre = Loc->pre;//insert into the front of the Headloc->next = Head->next;loc->pre = Head;h Ead->next = Loc;loc->next->pre = Loc;return mkv[key].val;} else{return-1;}} void set (int key, int value) {if (  Is_exist (key) {//change the value in map and the listmkv[key].val = value; ListNode *q = Mkv[key].p;q->value = value;//get the node and insert into the head of the Listq->pre->next = Q-&gt ; next;q->next->pre = Q->pre;q->next = Head->next;q->pre = Head;head->next->pre = q;head-> Next = Q;return;} ListNode *tmp = new ListNode (key,value), if (capacity_cur<capacity) {if (head->next==null)//the list is empty{head- >next = tmp; head->pre = Tmp;tmp->next = Head;tmp->pre = Head;tvalue TV (value,tmp); Mkv[key] = Tv;++capacity_cu R;} Else//insert the TMP into the front of the List{tmp->next = Head->next;tmp->pre = Head;head->next->pre = t Mp;head->next = Tmp;tvalue TV (value,tmp); Mkv[key] = Tv;++capacity_cur;}} Else{//get rid of the LRU nodelistnode *tail = Head->pre;head->pre = Tail->pre;tail->pre->next = HEAD;MKV.E Rase (Tail->key);d elete Tail;//insert into the new Nodetmp->next = Head->next;tmp->pre = Head; head->next->pre = Tmp;head->next = Tmp;tvalue TV (value,tmp); Mkv[key] = TV;}} BOOL Is_exist (int key) {return (Mkv.find (key)! = Mkv.end ());} void print () {ListNode *p = head->next;while (p!=head) {cout<< "key =" <<p->key<< "Value =" < <p->value<<endl;p = P->next;} cout<<endl;}}; int main () {/*lrucache LRU (3); Lru.set (1,10); Lru.print (); Lru.set (2,20); Lru.print (); Lru.set (3,30); Lru.print (); cout << "Get key =" <<1<< ", Value =" <<lru.get (1) <<endl;lru.print (); Lru.set (4,40); Lru.print ( );cout<< "Get key =" <<3<< ", Value =" <<lru.get (3) <<endl;lru.print (); Lru.set (5,50); Lru.print (); LRUCache lru1 (2); Lru1.set (2,1); Lru1.print (); Lru1.set (2,2); Lru1.print ();cout<< "Get key =" <<2<< ", Value = "<<lru1.get (2) <<endl;lru1.set (); Lru1.print (); Lru1.set (4,1); Lru1.print ();cout<<" get Key = "<<2<<", Value = "<<lru1.get (2) <<endl;*/lrucache lru1 (2); Lru1.set (2, 1); Lru1.print (); Lru1.set (Lru1.print); Lru1.set (2,3); Lru1.print (); Lru1.set (4,1); Lru1.print ();cout<< "Get key =" <<1<< ", Value =" <<lru1.get (1) <<endl;cout<< "Get key =" <<2<< ", Value = "<<lru1.get (2) <<endl;return 0;}

The bottleneck of the speed of this problem is:1). If key is found quickly (in the time of O (1));2) If the sequence of keys is maintained to conform to the LRU conditions (with the linked list, insert delete);3). How to quickly find the tail node, that is, the LRU node, and then replace, the new node is inserted into the tail (bidirectional scale, pointing to the tail node of the pointer);because the LRU is in-memory to eliminate the data, so the speed requirements are very high, all get,set operations as far as possible in O (1) time to complete.
Hints:
1) It is possible to have the same key but different value into the queue, so if you have the same key, then value is replaced with a new value, then insert the head node! 2) The map in the AC code can be changed to unordered_map, and the efficiency should be higher.

Annotated Source: http://blog.csdn.net/lavorange/article/details/41852921



"Leetcode": the design and implementation of LRU cache_ cache elimination algorithm LRU

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.