[Algorithm Series (6)]: Dictionary

table of Contents

1. Algorithm application

13. Roman Numerals to Integers

 146. LRU Caching Mechanism


The dictionary stores data in the form of Key/Value (key-value pairs); the biggest advantage of this class is that its time complexity for finding elements is close to O(1), and it is often used as a local cache of some data in actual projects. Overall efficiency.

1. Algorithm application

13. Roman Numerals to Integers

  • Title description

Roman numerals contain the following seven characters: I, V, X, L, C, D and M.

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

For example, the Roman numeral 2 is written as II, which means two parallel ones. 12 is written as XII, which means X + II. 27 is written as XXVII, which is XX + V + II.

Normally, the small numbers in Roman numerals are to the right of the large numbers. But there are special cases, for example, 4 is not written as IIII, but as IV. The number 1 is to the left of the number 5, and the number represented is equal to the number 4 obtained by subtracting the number 1 from the large number 5. Similarly, the number 9 is represented as IX. This special rule only applies to the following six situations:

  • I can be placed to the left of V (5) and X (10) to represent 4 and 9.
  • X can be placed to the left of L (50) and C (100) to represent 40 and 90. 
  • C can be placed to the left of D (500) and M (1000) to represent 400 and 900.

Given a Roman numeral, convert it to an integer. Ensure that the input is in the range of 1 to 3999.

示例 1:
输入:"III"
输出: 3

示例 2:
输入: "IV"
输出: 4

示例 3:
输入: "IX"
输出: 9

示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
  • Problem-solving ideas

The way to use the dictionary. Treat the dictionary as a storage container, key store all combinations of Roman characters, and value store the values ​​represented by the combinations. Each time one character is taken, it is judged whether there are any characters after this character. If there are, it is judged whether these two characters are in the dictionary, and the value is taken if they exist. Otherwise, just get the value according to one character.

  • C++ algorithm implementation 
 int romanToInt(string s) {
	 map<string, int> dict = { {"I", 1}, {"II" , 2}, {"IV" , 4}, {"IX" , 9}, {"X" , 10}, {"XL" , 40}, {"XC" , 90},
	 {"C" , 100}, {"CD", 400}, {"CM" , 900}, {"V" , 5},{"L" , 50}, {"D" , 500}, {"M" , 1000} };

	 int res = 0;
	 int i = 0;
	 while (i < s.size()) {
		 string tmp(1,s[i]);
		 if (i + 1 < s.size()) {
			 if (dict.find(tmp+s[i+1]) != dict.end()) {
				 res += dict[tmp + s[i + 1]];
				 i += 2;
			 }
			 else {
				 res += dict[tmp];
				 i += 1;
			 }
			 
		 }
		 else {
			 res += dict[tmp];
			 i += 1;
			
		 }
		 
	 }

	 return res;
 }

 146. LRU Caching Mechanism

  • Title description

Use the data structure you know to design and implement an LRU (least recently used) caching mechanism. It should support the following operations: Get data get and write data put.

Get data get(key)-If the key exists in the cache, get the value of the key (always a positive number), otherwise return -1.
Write data put(key, value)-If the key already exists, change its data value; if the key does not exist, insert the set of "key/value". When the cache capacity reaches the upper limit, it should delete the oldest unused data value before writing new data to make room for the new data value.

Advanced:

Can you complete these two operations in O(1) time complexity?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得关键字 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得关键字 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4
  • Problem-solving ideas

Use dictionary + list method

The computer's cache capacity is limited. If the cache is full, some content will be deleted to make room for new content. But the question is, what should be deleted? We definitely hope to delete the useless caches, and keep the useful data in the caches for future use. So, what kind of data do we judge as "useful" data?

The LRU cache elimination algorithm is a common strategy. The full name of LRU is Least Recently Used, which means that we believe that data that has been used recently should be "useful", and data that has not been used for a long time should be useless. When the memory is full, delete those that have not been used for a long time. data.

Because obviously the cache must be in order to distinguish between the most recently used and long-unused data; and we need to find out whether the key already exists in the cache; if the capacity is full, delete the last data; insert the data for each access To the head of the team.

So, what data structure meets the above conditions at the same time? Hash table lookup is fast, but the data has no fixed order; the linked list has order, insertion and deletion are fast, but lookup is slow. So combine it to form a new data structure: hash linked list.

The core data structure of the LRU cache algorithm is a combination of hash linked list, doubly linked list and hash table. The idea is very simple, that is, with the help of the hash table to give the linked list the feature of fast search: you can quickly find whether a key exists in the cache (linked list), and at the same time you can quickly delete and add nodes. Recalling the previous example, does this data structure perfectly solve the needs of LRU caching?

Think of a dictionary as a storage container. Since the dictionary is unordered, that is, the order of  dict internal storage  key is irrelevant to the order in which it is placed, so one list is needed  to assist in sorting.

  • C++ algorithm implementation
class LRUCache {
 public:
	 unordered_map<int, list<pair<int,int>>::iterator> hash_map;
	 list<pair<int,int>> head_cache;
	 int cap;

 public:
	 LRUCache(int capacity) {
		 cap = capacity;
	 }

	 int get(int key) {
		 if (hash_map.find(key) == hash_map.end()) {
			 return -1;
		 }

		 int val = hash_map[key]->second;
		 put(key, val);//利用put将数据提前

		 return val;
	 }

	 void put(int key, int value) {
		 pair<int, int> p(key, value);

		 if (hash_map.find(key) != hash_map.end()) {
			 head_cache.erase(hash_map[key]);//删除旧节点
			 head_cache.emplace_front(p);//插入到头部
			 hash_map[key] = head_cache.begin();//更新hash map
		 }
		 else {
			 if (cap == head_cache.size()) {
				 pair<int, int> last = head_cache.back();
				 head_cache.pop_back();//删除list最后一个数据
				 hash_map.erase(last.first);//删除map中的数据
			 }

			 //向头部添加数据
			 head_cache.emplace_front(p);
			 hash_map[key] = head_cache.begin();
		 }
	 }
 };

Reference link:

The Application of Dictionary Technology in Solving Algorithmic Problems

Detailed LRU algorithm

Guess you like

Origin blog.csdn.net/wxplol/article/details/108481901