[Serie de algoritmos (6)]: Diccionario

Tabla de contenido

1. Aplicación de algoritmos

13. Convertir números romanos en enteros

 146. Mecanismo de almacenamiento en caché de LRU


El diccionario almacena datos en forma de clave / valor (pares clave-valor); la mayor ventaja de esta clase es que su complejidad de tiempo para encontrar elementos es cercana a O (1), y a menudo se usa como un caché local de algunos datos en proyectos reales Eficiencia global.

1. Aplicación de algoritmos

13. Convertir números romanos en enteros

  • Descripción del Título

Los números romanos contienen los siguientes siete caracteres: I, V, X, L, C, D y M.

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

Por ejemplo, el número romano 2 se escribe II, que significa dos paralelos. 12 se escribe XII, que significa X + II. 27 se escribe XXVII, que es XX + V + II.

Normalmente, los números pequeños en números romanos están a la derecha de los números grandes. Pero hay casos especiales, por ejemplo, 4 no se escribe como IIII, sino como IV. El número 1 está a la izquierda del número 5, y el número representado es igual al número 4 obtenido al restar el número 1 del número grande 5. Del mismo modo, el número 9 se representa como IX. Esta regla especial solo se aplica a las siguientes seis situaciones:

  • Puedo colocarme a la izquierda de V (5) y X (10) para representar 4 y 9.
  • X se puede colocar a la izquierda de L (50) y C (100) para representar 40 y 90. 
  • C se puede colocar a la izquierda de D (500) y M (1000) para representar 400 y 900.

Dado un número romano, conviértalo en un número entero. Asegúrese de que la entrada esté en el rango de 1 a 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.
  • Ideas para resolver problemas

La forma de utilizar el diccionario. Trate el diccionario como un contenedor de almacenamiento, key almacene todas las combinaciones de caracteres romanos y value almacene los valores representados por las combinaciones. Cada vez que se toma un carácter, se juzga si hay algún carácter después de este. Si los hay, se juzga si estos dos caracteres están en el diccionario y se toma el valor si existen. De lo contrario, obtenga el valor de acuerdo con un carácter.

  • Implementación del algoritmo C ++ 
 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. Mecanismo de almacenamiento en caché de LRU

  • Descripción del Título

Utilice la estructura de datos que conoce para diseñar e implementar un mecanismo de caché LRU (menos utilizado recientemente). Debe admitir las siguientes operaciones: Obtener datos, obtener y escribir datos, poner.

Obtener datos obtener (clave) -Si la clave existe en el caché, obtenga el valor de la clave (siempre un número positivo); de lo contrario, devuelva -1.
Escribir datos poner (clave, valor) -Si la clave ya existe, cambie su valor de datos, si la clave no existe, inserte el conjunto de "clave / valor". Cuando la capacidad de la caché alcanza el límite superior, debe eliminar el valor de datos no utilizado más antiguo antes de escribir nuevos datos para dejar espacio para el nuevo valor de datos.

Avanzado:

¿Puede completar estas dos operaciones en O (1) complejidad de tiempo?

示例:

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
  • Ideas para resolver problemas

Usar diccionario + método de lista

La capacidad de la memoria caché de la computadora es limitada. Si la memoria caché está llena, se eliminará parte del contenido para dejar espacio para contenido nuevo. Pero la pregunta es, ¿qué debería eliminarse? Definitivamente esperamos eliminar los cachés inútiles y mantener los datos útiles en los cachés para uso futuro. Entonces, ¿qué tipo de datos juzgamos como datos "útiles"?

El algoritmo de eliminación de caché LRU es una estrategia común. El nombre completo de LRU es Menos utilizado recientemente, lo que significa que creemos que los datos que se han utilizado recientemente deberían ser "útiles", y los datos que no se han utilizado durante mucho tiempo deberían ser inútiles. Cuando la memoria esté llena, elimine los que no se han utilizado durante mucho tiempo .datos.

Porque obviamente la caché debe ser para poder distinguir entre los datos usados ​​más recientemente y los datos no usados ​​durante mucho tiempo; y necesitamos averiguar si la clave ya existe en la caché; si la capacidad está llena, borre los últimos datos; inserte los datos por cada acceso al jefe del equipo.

Entonces, ¿qué estructura de datos cumple las condiciones anteriores al mismo tiempo? La búsqueda de tablas hash es rápida, pero los datos no tienen un orden fijo; la lista vinculada tiene orden, la inserción y eliminación son rápidas, pero la búsqueda es lenta. Así que combínelos para formar una nueva estructura de datos: lista enlazada hash.

La estructura de datos central del algoritmo de caché LRU es una combinación de lista enlazada hash, lista doblemente enlazada y tabla hash. La idea es muy simple, es decir, con la ayuda de la tabla hash para darle a la lista vinculada la función de búsqueda rápida: puede encontrar rápidamente si existe una clave en la caché (lista vinculada) y, al mismo tiempo, puede eliminar y agregar nodos rápidamente. Recordando el ejemplo anterior, ¿esta estructura de datos resuelve perfectamente las necesidades de almacenamiento en caché de LRU?

Piense en un diccionario como un contenedor de almacenamiento, dado que el diccionario no está ordenado, es decir, el orden del  dict almacenamiento interno  key es irrelevante para el orden en el que está colocado, por lo que list se necesita uno  para ayudar en la clasificación.

  • Implementación del algoritmo C ++
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();
		 }
	 }
 };

Link de referencia:

La aplicación de la tecnología de diccionario para resolver problemas algorítmicos

Algoritmo LRU detallado

Supongo que te gusta

Origin blog.csdn.net/wxplol/article/details/108481901
Recomendado
Clasificación