目次
この記事は、Datawhaleチームの調査計画の21番目のLeetCodeが選択したトピックグループTask12調査ノートです。
初心者の方は少し時間がかかり、多くの解決策は詳細に分析されておらず、将来改訂される可能性があります。ご容赦ください。
Datawhale学習ドキュメント:
https : //github.com/datawhalechina/team-learning-program/tree/master/LeetCodeTencent
(1.24:早期警告、ブロックコピーの問題の解決策。データ構造が再びドラッグバックしています)
146LRUキャッシングメカニズム
ソース:LeetCode
リンク:https ://leetcode-cn.com/problems/lru-cache
知っているデータ構造を使用して、LRU(最近使用されていない)キャッシュメカニズムを設計および実装します。
LRUCacheクラスを実装します。
LRUCache(int capacity)容量容量として正の整数を使用してLRUキャッシュを初期化します。intget(int
key)キーワードkeyがキャッシュに存在する場合は、キーワードの値を返します。存在しない場合は、-1を返します。
void put(int key、int value)キーワードがすでに存在する場合は、そのデータ値を変更します。キーワードが存在しない場合は、「keyword-value」のセットを挿入します。キャッシュ容量が上限に達すると、新しいデータを書き込む前に最も古い未使用のデータ値を削除して、新しいデータ値のためのスペースを確保する必要があります。
上級:
これらの2つの操作をO(1)時間計算量で完了できますか?
例:
輸出入
[“ LRUCache”、“ put”、“ put”、“ get”、“ put”、“ get”、“ put”、“ get”、“ get”、“ get”]
[[2]、[1、 1]、[2、2]、[1]、[3、3]、[2]、[4、4]、[1]、[3]、[4]]
エクスポート
[null、null、null、1 、null、-1、null、-1、3、4]
説明
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1、1); //キャッシュは{1 = 1}
lRUCache.put(2、2); //キャッシュは{1 = 1、2 = 2}
lRUCache .get(1); // 1を
返すlRUCache.put(3、3); //この操作はキーワード2を無効にし、キャッシュは{1 = 1、3 = 3}
lRUCache.get(2); // Return -1(見つかりません)
lRUCache.put(4、4); //この操作はキーワード1を無効にし、キャッシュは{4 =
4、3 = 3} lRUCache.get(1); //-1を返します(見つかりません)
lRUCache.get(3); // 3を返します
lRUCache.get(4); // 4を返します
促す:
1 <=容量<= 3000
0 <=キー<= 3000
0 <=値<= 10 ^ 4
最大3 * 10 ^ 4呼び出しの取得と書き込み
アイデア
この問題解決策を推奨するhttps://leetcode-cn.com/problems/lru-cache/solution/lru-ce-lue-xiang-jie-he-shi-xian-by-labuladong/
LRUアルゴリズムを使用すると、実際にデータ構造を設計できます。最初にキャッシュの最大容量として容量パラメーターを受け取り、次に2つのAPIを実装します。1つはキーと値のペアを格納するput(key、val)メソッドです。もう1つはget(key)メソッドで、キーに対応するvalを取得し、キーが存在しない場合は-1を返します。
getメソッドとputメソッドは両方ともO(1)時間計算量でなければならないことに注意してください
putメソッドとgetメソッドの時間計算量をO(1)にするために、キャッシュデータ構造に必要な条件(高速検索、高速挿入、高速削除、および順序付け)を要約できます。
明らかに、最近使用されたデータと長期間使用されていないデータを区別するためにキャッシュを順序付ける必要があるため、キーがキャッシュにすでに存在するかどうかを確認する必要があります。容量がいっぱいの場合は、最後のデータを削除し、それぞれのデータを挿入します。チームの責任者へのアクセス。
ハッシュテーブルのルックアップは高速ですが、データの順序は固定されていません。リンクリストには順序があり、挿入と削除は高速ですが、ルックアップは低速です。それを組み合わせて、上記の条件を満たす新しいデータ構造を形成します:ハッシュリンクリスト。
LRUキャッシュアルゴリズムのコアデータ構造は、ハッシュリンクリスト、二重リンクリスト、およびハッシュテーブルの組み合わせです。
ロジックは次のとおりです。
// key 映射到 Node(key, val)
HashMap<Integer, Node> map;
// Node(k1, v1) <-> Node(k2, v2)...
DoubleList cache;
int get(int key) {
if (key 不存在) {
return -1;
} else {
将数据 (key, val) 提到开头;
return val;
}
}
void put(int key, int val) {
Node x = new Node(key, val);
if (key 已存在) {
把旧的数据删除;
将新节点 x 插入到开头;
} else {
if (cache 已满) {
删除链表的最后一个数据腾位置;
删除 map 中映射到该数据的键;
}
将新节点 x 插入到开头;
map 中新建 key 对新节点 x 的映射;
}
}
//作者:labuladong
//链接:https://leetcode-cn.com/problems/lru-cache/solution/lru-ce-lue-xiang-jie-he-shi-xian-by-labuladong/
https://leetcode-cn.com/problems/lru-cache/solution/lruhuan-cun-ji-zhi-by-leetcode-solution/
LRUキャッシュメカニズムは、二重リンクリストで補足されたハッシュテーブルを介して実装できます。 haを使用します。ギリシャ語のテーブルと二重にリンクされたリストは、すべてのキーと値のペアをキャッシュに保持します。
二重リンクリストには、これらのキーと値のペアが使用されている順序で格納されます。ヘッドの近くのキーと値のペアが最も最近使用され、テールの近くのキーと値のペアが最も使用されていません。
ハッシュテーブルは一般的なハッシュマップ(HashMap)であり、キャッシュされたデータのキーを二重リンクリスト内の位置にマップします。
ヒント:二重リンクリストの実装では、ダミーのヘッドとダミーのテールを使用して境界をマークするため、ノードを追加および削除するときに、隣接するノードが存在するかどうかを確認する必要はありません。
Pythonの実装(公式ソリューションコード)
(1.24:公式ソリューションコード)
class DLinkedNode:
def __init__(self, key=0, value=0):
self.key = key
self.value = value
self.prev = None
self.next = None
class LRUCache:
def __init__(self, capacity: int):
self.cache = dict()
# 使用伪头部和伪尾部节点
self.head = DLinkedNode()
self.tail = DLinkedNode()
self.head.next = self.tail
self.tail.prev = self.head
self.capacity = capacity
self.size = 0
def get(self, key: int) -> int:
if key not in self.cache:
return -1
# 如果 key 存在,先通过哈希表定位,再移到头部
node = self.cache[key]
self.moveToHead(node)
return node.value
def put(self, key: int, value: int) -> None:
if key not in self.cache:
# 如果 key 不存在,创建一个新的节点
node = DLinkedNode(key, value)
# 添加进哈希表
self.cache[key] = node
# 添加至双向链表的头部
self.addToHead(node)
self.size += 1
if self.size > self.capacity:
# 如果超出容量,删除双向链表的尾部节点
removed = self.removeTail()
# 删除哈希表中对应的项
self.cache.pop(removed.key)
self.size -= 1
else:
# 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
node = self.cache[key]
node.value = value
self.moveToHead(node)
def addToHead(self, node):
node.prev = self.head
node.next = self.head.next
self.head.next.prev = node
self.head.next = node
def removeNode(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def moveToHead(self, node):
self.removeNode(node)
self.addToHead(node)
def removeTail(self):
node = self.tail.prev
self.removeNode(node)
return node
#作者:LeetCode-Solution
#链接:https://leetcode-cn.com/problems/lru-cache/solution/lruhuan-cun-ji-zhi-by-leetcode-solution/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
148リンクリストの並べ替え
ソース:LeetCode
リンク:https ://leetcode-cn.com/problems/sort-list
ヘッドノードリストヘッドに昇順を押して、ソートされたリストを返します。
上級:
リンクリストをO(n log n)の時間計算量と一定レベルの空間計算量で並べ替えることができますか?
例:
入力:head = [4,2,1,3]
出力:[1,2,3,4]
入力:head = [-1,5,3,4,0]
出力:[-1,0,3,4,5]
入力:head = []
出力:[]
アイデア
ユニオンソートのアイデアは、ソートされるシーケンスを1つの要素が含まれるまでグループ化し、2つの順序付けられたシーケンスを逆方向にマージして、最後にソートされたシーケンスを取得することです。
リンクリストの場合、現在のリンクリストを再帰的に2つのセグメントに分割してからマージすることができます。2つのセグメントを分割する方法は、ダブルポインタ方式を使用することです。p1ポインタは一度に2ステップ移動し、p2ポインタは一度に2ステップ移動します。 p1が最後に到達するまで、一度に1ステップずつ移動します。p2が中央の位置にあるとき、2つのセクションに分割されます。
Pythonの実装
(1.24:学習ドキュメントから。データ構造が再び拡張され、関連する知識が補足されています。)https://github.com/datawhalechina/team-learning-program/blob/master/LeetCodeTencent/148%20% E6%8E%92%E5%BA%8F%E9%93%BE%E8%A1%A8.md
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def sortList(self, head: ListNode) -> ListNode:
if head is None:
return head
return self.mergeSort(head)
def mergeSort(self, node: ListNode) -> ListNode:
if node.next is None:
return node
p1 = node
p2 = node
cute = None
while p1 is not None and p1.next is not None:
cute = p2
p2 = p2.next
p1 = p1.next.next
cute.next = None
l1 = self.mergeSort(node)
l2 = self.mergeSort(p2)
return self.mergeTwoLists(l1, l2)
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
pHead = ListNode(-1)
temp = pHead
while l1 is not None and l2 is not None:
if l1.val < l2.val:
temp.next = l1
l1 = l1.next
else:
temp.next = l2
l2 = l2.next
temp = temp.next
if l1 is not None:
temp.next = l1
if l2 is not None:
temp.next = l2
return pHead.next
155最小スタック
ソース:LeetCode
リンク:https ://leetcode-cn.com/problems/min-stack
プッシュ、ポップ、およびトップ操作をサポートし、一定時間内に最小の要素を取得できるスタックを設計します。
- push(x)-要素xをスタックにプッシュします。
- pop()-スタックの最上位にある要素を削除します。
- top()-スタックの最上位要素を取得します。
- getMin()-スタック内の最小の要素を取得します。
例:
入力:
["MinStack"、 "push"、 "push"、 "push"、 "getMin"、 "pop"、 "top"、 "getMin"]
[[]、[-2]、[0]、[- 3]、[]、[]、[]、[]]
出力:
[null、null、null、null、-3、null、0、-2]
説明:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); ->逆回-3。
minStack.pop();
minStack.top(); ->逆回
0。minStack.getMin(); ->逆回-2。
促す:
pop、top、およびgetMin操作は、常に空でないスタックで呼び出されます
アイデア
公式の問題解決策https://leetcode-cn.com/problems/min-stack/solution/zui-xiao-zhan-by-leetcode-solution/
スタックの性質に応じて、要素aがスタックにプッシュされ、スタック内に他の要素b、c、dがある場合、スタックの最上位にある要素である限り、操作中いつでもがaの場合、スタック内の現在の要素はAre a、b、c、dである必要があります。
次に、各要素aがスタックにプッシュされたときの、現在のスタックの最小値mを格納できます。その後、スタックの最上位要素がaの場合は常に、格納されている最小値mを直接返すことができます。
各要素aが常に対応する最小mと1対1で対応するように、データ構造を設計します。したがって、補助スタックを使用して、要素スタックと同期して挿入および削除し、各要素に対応する最小値を格納できます。
要素をスタックにプッシュする場合、現在の補助スタックの一番上に格納されている最小値を取得し、それを現在の要素と比較して最小値を見つけ、この最小値を補助スタックに挿入します。
要素をスタックからポップする場合は、補助スタックの一番上の要素もポップします。
スタック内の要素の最小値はいつでも、補助スタックの最上位の要素に格納されます
Pythonの実装
class MinStack:
def __init__(self):
self.stack = []
self.min_stack = [math.inf]
def push(self, x: int) -> None:
self.stack.append(x)
self.min_stack.append(min(x, self.min_stack[-1]))
def pop(self) -> None:
self.stack.pop()
self.min_stack.pop()
def top(self) -> int:
return self.stack[-1]
def getMin(self) -> int:
return self.min_stack[-1]
#作者:LeetCode-Solution
#链接:https://leetcode-cn.com/problems/min-stack/solution/zui-xiao-zhan-by-leetcode-solution/
#来源:力扣(LeetCode)
#著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。