Skip to content

Commit 8d005f4

Browse files
Merge pull request #327 from Song2017/master
LRUCache&min_heap
2 parents 5a6cf7d + f194502 commit 8d005f4

File tree

2 files changed

+215
-0
lines changed

2 files changed

+215
-0
lines changed

python/06_linkedlist/LRUCache.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# Definition for singly-linked list.
2+
class DbListNode(object):
3+
def __init__(self, x, y):
4+
self.key = x
5+
self.val = y
6+
self.next = None
7+
self.prev = None
8+
9+
10+
class LRUCache:
11+
'''
12+
leet code: 146
13+
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。
14+
它应该支持以下操作: 获取数据 get 和 写入数据 put 。
15+
获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
16+
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。
17+
当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间
18+
19+
哈希表+双向链表
20+
哈希表: 查询 O(1)
21+
双向链表: 有序, 增删操作 O(1)
22+
23+
Author: Ben
24+
'''
25+
26+
def __init__(self, capacity: int):
27+
self.cap = capacity
28+
self.hkeys = {}
29+
# self.top和self.tail作为哨兵节点, 避免越界
30+
self.top = DbListNode(None, -1)
31+
self.tail = DbListNode(None, -1)
32+
self.top.next = self.tail
33+
self.tail.prev = self.top
34+
35+
def get(self, key: int) -> int:
36+
37+
if key in self.hkeys.keys():
38+
# 更新结点顺序
39+
cur = self.hkeys[key]
40+
# 跳出原位置
41+
cur.next.prev = cur.prev
42+
cur.prev.next = cur.next
43+
# 最近用过的置于链表首部
44+
top_node = self.top.next
45+
self.top.next = cur
46+
cur.prev = self.top
47+
cur.next = top_node
48+
top_node.prev = cur
49+
50+
return self.hkeys[key].val
51+
return -1
52+
53+
def put(self, key: int, value: int) -> None:
54+
if key in self.hkeys.keys():
55+
cur = self.hkeys[key]
56+
cur.val = value
57+
# 跳出原位置
58+
cur.prev.next = cur.next
59+
cur.next.prev = cur.prev
60+
61+
# 最近用过的置于链表首部
62+
top_node = self.top.next
63+
self.top.next = cur
64+
cur.prev = self.top
65+
cur.next = top_node
66+
top_node.prev = cur
67+
else:
68+
# 增加新结点至首部
69+
cur = DbListNode(key, value)
70+
self.hkeys[key] = cur
71+
# 最近用过的置于链表首部
72+
top_node = self.top.next
73+
self.top.next = cur
74+
cur.prev = self.top
75+
cur.next = top_node
76+
top_node.prev = cur
77+
if len(self.hkeys.keys()) > self.cap:
78+
self.hkeys.pop(self.tail.prev.key)
79+
# 去掉原尾结点
80+
self.tail.prev.prev.next = self.tail
81+
self.tail.prev = self.tail.prev.prev
82+
83+
def __repr__(self):
84+
vals = []
85+
p = self.top.next
86+
while p.next:
87+
vals.append(str(p.val))
88+
p = p.next
89+
return '->'.join(vals)
90+
91+
92+
if __name__ == '__main__':
93+
cache = LRUCache(2)
94+
cache.put(1, 1)
95+
cache.put(2, 2)
96+
print(cache)
97+
cache.get(1) # 返回 1
98+
cache.put(3, 3) # 该操作会使得密钥 2 作废
99+
print(cache)
100+
cache.get(2) # 返回 -1 (未找到)
101+
cache.put(4, 4) # 该操作会使得密钥 1 作废
102+
print(cache)
103+
cache.get(1) # 返回 -1 (未找到)
104+
cache.get(3) # 返回 3
105+
print(cache)
106+
cache.get(4) # 返回 4
107+
print(cache)

python/28_heap/min_heap.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
class Heap(object):
2+
'''
3+
索引从0开始的小顶堆
4+
参考: https://github.com/python/cpython/blob/master/Lib/heapq.py
5+
6+
author: Ben
7+
'''
8+
9+
def __init__(self, nums):
10+
self._heap = nums
11+
12+
def _siftup(self, pos):
13+
'''
14+
从上向下的堆化
15+
将pos节点的子节点中的最值提升到pos位置
16+
'''
17+
start = pos
18+
startval = self._heap[pos]
19+
n = len(self._heap)
20+
# 完全二叉树特性
21+
child = pos * 2 + 1
22+
# 比较叶子节点
23+
while child < n:
24+
right = child + 1
25+
# 平衡二叉树的特性, 大的都在右边
26+
if right < n and not self._heap[right] > self._heap[child]:
27+
child = right
28+
self._heap[pos] = self._heap[child]
29+
pos = child
30+
child = pos * 2 + 1
31+
self._heap[pos] = startval
32+
33+
# 此时只有pos是不确定的
34+
self._siftdown(start, pos)
35+
36+
def _siftdown(self, start, pos):
37+
'''
38+
最小堆: 大于start的节点, 除pos外已经是最小堆
39+
以pos为叶子节点, start为根节点之间的元素进行排序. 将pos叶子节点交换到正确的排序位置
40+
操作: 从叶子节点开始, 当父节点的值大于子节点时, 父节点的值降低到子节点
41+
'''
42+
startval = self._heap[pos]
43+
while pos > start:
44+
parent = (pos - 1) >> 1
45+
parentval = self._heap[parent]
46+
if parentval > startval:
47+
self._heap[pos] = parentval
48+
pos = parent
49+
continue
50+
break
51+
self._heap[pos] = startval
52+
53+
def heapify(self):
54+
'''
55+
堆化: 从后向前(从下向上)的方式堆化, _siftup中pos节点的子树已经是有序的,
56+
这样要排序的节点在慢慢减少
57+
1. 因为n/2+1到n的节点是叶子节点(完全二叉树的特性), 它们没有子节点,
58+
所以, 只需要堆化n/2到0的节点, 以对应的父节点为根节点, 将最值向上筛选,
59+
然后交换对应的根节点和查找到的最值
60+
2. 因为开始时待排序树的根节点还没有排序, 为了保证根节点的有序,
61+
需要将子树中根节点交换到正确顺序
62+
'''
63+
n = len(self._heap)
64+
for i in reversed(range(n // 2)):
65+
self._siftup(i)
66+
67+
def heappop(self):
68+
'''
69+
弹出堆首的最值 O(logn)
70+
'''
71+
tail = self._heap.pop()
72+
# 为避免破环完全二叉树特性, 将堆尾元素填充到堆首
73+
# 此时, 只有堆首是未排序的, 只需要一次从上向下的堆化
74+
if self._heap:
75+
peak = self._heap[0]
76+
self._heap[0] = tail
77+
self._siftup(0)
78+
return peak
79+
return tail
80+
81+
def heappush(self, val):
82+
'''
83+
添加元素到堆尾 O(logn)
84+
'''
85+
n = len(self._heap)
86+
self._heap.append(val)
87+
# 此时只有堆尾的节点是未排序的, 将添加的节点迭代到正确的位置
88+
self._siftdown(0, n)
89+
90+
def __repr__(self):
91+
vals = [str(i) for i in self._heap]
92+
return '>'.join(vals)
93+
94+
95+
if __name__ == '__main__':
96+
h = Heap([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
97+
h.heapify()
98+
print(h)
99+
print(h.heappop())
100+
print(h)
101+
h.heappush(3.5)
102+
print(h)
103+
h.heappush(0.1)
104+
print(h)
105+
h.heappush(0.5)
106+
print(h)
107+
print(h.heappop())
108+
print(h)

0 commit comments

Comments
 (0)