Это 23-й день моего участия в Gengwen Challenge.Подробности о мероприятии:Обновить вызов
1. Описание темы
146. Механизм кэширования LRU
Используя свои знания о структурах данных, спроектируйте и внедрите
LRU(Наименее недавно использовавшийся) механизм кэширования. выполнитьLRUCacheсвоего рода:
LRUCache(int capacity)Инициализировать с положительным целым числом в качестве емкости емкостиLRUтайник int get(int key) Возвращает значение ключа, если ключ существует в кэше, иначе -1. void put(int key, int value) Если ключ уже существует, измените его значение данных, если ключ не существует, вставьте набор "ключ-значение". Когда емкость кэша достигает верхнего предела, он должен удалить самые старые неиспользуемые значения данных перед записью новых данных, чтобы освободить место для новых значений данных.Для продвинутых: можете ли вы выполнить обе операции с временной сложностью O(1)?
2. Анализ мыслей
первая мысль
- Когда я впервые увидел этот вопрос, я не думал об этом и думал, что буду использовать очередь, потому что очередь FIFO может удалить данные в конце, но если вы тщательно обдумаете этот вопрос, его нужно устранить.Наименее недавно использованныйДанные, если только последние данные, то очереди легко реализовать. В сочетании с частотой использования это связано с частым перемещением данных. Ясно, что очередь не может быть завершена.
- Итак, существуют ли последовательно добавляемые данные, которые перемещают данные вперед к одному концу каждый раз, когда они извлекаются? Ответ - да!
LinkedHashMap -
LinkedHashMapНезнакомые друзья могут просто понять это какHashMap. На рисунке ниже показаноHashMapструктура хранения
- Для вышеперечисленных элементов я сделал анимацию, чтобы продемонстрировать весь процесс! ! !
- и
LinkedHashMapЭто просто дополнительный связанный список для соединения элементов в нем.
- Вот почему
LinkedHashMapхранятся по порядку. ноLinkedHahsMapНельзя ли отсортировать по частоте использования? Всем известно, что он в порядке сложения! ! !
*LinkedHashMap* Модернизация
- оригинальный
LinkedHashMapУложиться в ситуацию действительно невозможно, но если мы немного посмотрим на исходный код, то обнаружим, что он будет выполняться после put.afterNodeInsertionСюда. Это тожеHashMapуехатьLinkedHashMapДелай расширение!
-
removeNodeзаключается в том, чтобы поставить передние данные. Чтобы войти в этот метод, вам нужноremoveEldestEntryсудить.LinkedHashMapЗначение по умолчанию — false, поэтому нам просто нужно переопределить его. Но как сохранить значение в конце при получении значения? Если мы внимательно посмотрим на исходный код, то обнаружим, что вgetЕсть такой методafterNodeAccess. Его роль состоит в том, чтобы сместить элемент отставания от значения. как раз для насLRUстратегические особенности
- Подводить итоги! мы используем
LinkedHashMapРеализовать стратегию LRU очень просто!
реализовать это самостоятельно
-
Но смысл этого вопроса в том, чтобы изучить, как мы реализуем это сами, а не гениально модифицировать существующие инструменты! Однако, столкнувшись
LinkedHashMapНельзя отрицать, что трансформация очень случайна! Попробуем сделать это сами! -
Прежде всего, нам нужно определить необходимость использования хэша в сочетании со связанным списком для достижения цели. Хэш, который мы используем естественно
HashMapЦель хранения данных — облегчить позиционирование данных. Чтобы найти данные, необходимо использовать связанный список, чтобы переместить данные в конец связанного списка значений в режиме реального времени. Чтобы облегчить нашу работу со связанным списком, связанный список здесь должен быть двойным связным списком!
единица связанного списка
- Сначала мы определяем внутренний класс! Базовая единица для связанных списков. В нем хранятся ключ и значение, чтобы упростить поиск узла по содержимому, хранящемуся в хэше!
preNode,nextNodeУкажите на передний и задний узлы соответственно
- Инициализируйте емкость и размер связанного списка в построителе и инициализируйте граничный узел, чтобы облегчить нам сдвиг и удаление в рабочем узле.
- Возвращает -1, если он не был добавлен при получении данных, и перемещает узел, соответствующий данным, в конец связанного списка для добавленных данных.
- При добавлении в первый раз нам нужно сохранить размер связанного списка и проверить, нужно ли нам удалять данные, Если это не первое добавление, нам нужно только обновить значение и положение соответствующий узел в связанном списке.
| имя метода | эффект |
|---|---|
| addToTail | Добавьте узел в конец списка значений |
| moveToTail | Переместить узел, который уже существует в связанном списке, в конец связанного списка |
| removeHeadNode | Удалите первый узел в связанном списке, обратите внимание на первый узел после граничного узла |
4. Резюме
- Хотя время выполнения и потребление памяти немного великоваты! Но я просто не оптимизирую.
- Этот вопрос в основном усложняется при перемещении связанного списка. Нам нужно поддерживать порядок между ними в порядке добавления и частоте использования. Пока этот порядок поддерживается, нет проблем!