diff --git "a/docs/03_\351\223\276\350\241\250/double_link_list.py" "b/docs/03_\351\223\276\350\241\250/double_link_list.py" index 0674cae..581b067 100644 --- "a/docs/03_\351\223\276\350\241\250/double_link_list.py" +++ "b/docs/03_\351\223\276\350\241\250/double_link_list.py" @@ -45,13 +45,14 @@ def appendleft(self, value): raise Exception('LinkedList is Full') node = Node(value=value) if self.root.next is self.root: # empty - node.next = self.root node.prev = self.root + node.next = self.root self.root.next = node self.root.prev = node else: - node.prev = self.root headnode = self.root.next + + node.prev = self.root node.next = headnode headnode.prev = node self.root.next = node diff --git "a/docs/12_\345\237\272\346\234\254\346\216\222\345\272\217\347\256\227\346\263\225/basic_sort.py" "b/docs/12_\345\237\272\346\234\254\346\216\222\345\272\217\347\256\227\346\263\225/basic_sort.py" index d25682d..8c10878 100644 --- "a/docs/12_\345\237\272\346\234\254\346\216\222\345\272\217\347\256\227\346\263\225/basic_sort.py" +++ "b/docs/12_\345\237\272\346\234\254\346\216\222\345\272\217\347\256\227\346\263\225/basic_sort.py" @@ -50,7 +50,7 @@ def insertion_sort(seq): # 找到这个值的合适位置,使得前边的数组有序 [0,i] 有序 pos = i while pos > 0 and value < seq[pos-1]: - seq[pos] = seq[pos-1] # 如果前边的元素比它大,就让它一直前移 + seq[pos] = seq[pos-1] # 如果前边的元素比它大,就让它一直后移 pos -= 1 seq[pos] = value # 找到了合适的位置赋值就好 print(seq) diff --git a/ehco/hashtable/hashtable_adt.py b/ehco/hashtable/hashtable_adt.py index 897c307..e324414 100644 --- a/ehco/hashtable/hashtable_adt.py +++ b/ehco/hashtable/hashtable_adt.py @@ -1,30 +1,8 @@ -class Array(object): +from ehco.array_list.array_and_list import Array - def __init__(self, size=32, init=None): - self._size = size - self._items = [init] * size - def __getitem__(self, index): - return self._items[index] - - def __setitem__(self, index, value): - self._items[index] = value - - def __len__(self): - return self._size - - def clear(self, value=None): - for i in range(self._items): - self._items[i] = value - - def __iter__(self): - for item in self._items: - yield item - - -class Slot(object): +class Slot: """定义一个 hash 表 数组的槽 - 注意,一个槽有三种状态,看你能否想明白。相比链接法解决冲突,二次探查法删除一个 key 的操作稍微复杂。 1.从未使用 HashMap.UNUSED。此槽没有被使用和冲突过,查找时只要找到 UNUSED 就不用再继续探查了 2.使用过但是 remove 了,此时是 HashMap.EMPTY,该探查点后边的元素扔可能是有key @@ -35,7 +13,7 @@ def __init__(self, key, value): self.key, self.value = key, value -class HashTable(object): +class HashTable: # 没有被使用过的 UNUSED = None @@ -48,7 +26,6 @@ def __init__(self): @property def _load_factor(self): - # load factor 超过0.8的时候重新分配 return self.length / float(len(self._table)) def __len__(self): @@ -134,18 +111,18 @@ def __iter__(self): def test_hash_table(): h = HashTable() - h.add('a', 0) - h.add('b', 1) - h.add('c', 2) + h.add("a", 0) + h.add("b", 1) + h.add("c", 2) assert len(h) == 3 - assert h.get('a') == 0 - assert h.get('b') == 1 - assert h.get('hehe') is None + assert h.get("a") == 0 + assert h.get("b") == 1 + assert h.get("hehe") is None - h.remove('a') - assert h.get('a') is None - assert sorted(list(h)) == ['b', 'c'] + h.remove("a") + assert h.get("a") is None + assert sorted(list(h)) == ["b", "c"] n = 50 for i in range(n): diff --git a/ehco/link_list/lru.py b/ehco/link_list/lru.py new file mode 100644 index 0000000..c28bec5 --- /dev/null +++ b/ehco/link_list/lru.py @@ -0,0 +1,186 @@ +from collections import OrderedDict + + +class Node: + def __init__(self, value=None, next=None, prev=None): + self.value = value + self.next = next + self.prev = prev + + def __str__(self): + return f"" + + __repr__ = __str__ + + +class DBL: + def __init__(self): + node = Node("root") + node.prev = node + node.next = node + self.root = node + + self.lens = 0 + + @property + def head(self): + return self.root.next + + @property + def tail(self): + return self.root.prev + + def append(self, value): + node = Node(value) + + self.tail.next = node + node.prev = self.tail + node.next = self.root + self.root.prev = node + self.lens += 1 + + def append_left(self, value): + node = Node(value) + + node.next = self.head + self.head.prev = node + self.root.next = node + node.prev = self.root + self.lens += 1 + + def remove(self, node): + # NOTE for spec case + if node == self.root: + return False + if node == self.tail: + self.root.prev = node.prev + if node.next: + node.next.prev = node.prev + node.prev.next = node.next + del node + self.lens -= 1 + return True + + def iter_item(self): + cur = self.root.next + while cur and cur != self.root: + yield cur + cur = cur.next + + def find(self, value): + for node in self.iter_item(): + if node.value == value: + return node + return None + + def insert(self, value, new_value): + per = self.root + for node in self.iter_item(): + if node.value == value: + temp = Node(new_value) + per.next = temp + temp.prev = per + temp.next = node + node.prev = temp + self.lens += 1 + return True + per = node + self.append(new_value) + + def __str__(self): + return "->".join([str(node.value) for node in self.iter_item()]) + + __repr__ = __str__ + + +class LRU: + def __init__(self, size=10): + self.size = size + self._link = DBL() + self._cache = dict() + + def _move_to_recent(self, node): + if node == self._link.tail: + return + + # pop node from link + node.prev.next = node.next + node.next.prev = node.prev + # set node to tail + now_tail = self._link.tail + now_tail.next = node + node.prev = now_tail + node.next = None + self._link.root.prev = node + + def _append(self, k, v): + self._link.append(v) + self._cache[k] = self._link.tail + # Bind cache key + self._link.tail.cache_key = k + + def _expired_not_used(self): + need_expired = self._link.head + self._link.remove(need_expired) + + def get(self, k): + node = self._cache.pop(k, None) + if not node: + return + self._move_to_recent(node) + return node.value + + def put(self, k, v): + node = self._cache.pop(k, None) + if node: + node.value = v + self._move_to_recent(node) + else: + if self._link.lens == self.size: + self._expired_not_used() + self._append(k, v) + + def __str__(self): + return "->".join([f"{node.cache_key}" for node in self._link.iter_item()]) + + __repr__ = __str__ + + +class OLRU: + def __init__(self, size=10): + self._cache = OrderedDict() + self.size = size + + def get(self, k): + ret = self._cache.pop(k, None) + if ret: + self._cache[k] = ret + return ret + + def put(self, k, v): + ret = self._cache.pop(k, None) + if not ret and len(self._cache) == self.size: + # NOTE pop last recent used item + self._cache.popitem(False) + self._cache[k] = v + + +def test_lru(): + lru = LRU(size=10) + + for i in range(10): + lru.put(i, i) + + for i in range(10): + assert lru.get(i) == i + + print(lru) + lru.put(11, 11) + assert lru.get(11) == 11 + print(lru) + + assert lru.get(0) == None + + for i in range(100): + lru.put(i, i) + print(lru) diff --git a/ehco/queue/dubble_ended_queue.py b/ehco/queue/dubble_ended_queue.py new file mode 100644 index 0000000..1cdf8c0 --- /dev/null +++ b/ehco/queue/dubble_ended_queue.py @@ -0,0 +1,49 @@ +from ehco.link_list.lru import DBL + + +class Dequeue: + def __init__(self): + self._double_link_list = DBL() + + @property + def head(self): + return self._double_link_list.head + + @property + def tail(self): + return self._double_link_list.tail + + def append(self, val): + self._double_link_list.append(val) + + def append_left(self, val): + self._double_link_list.append_left(val) + + def pop(self): + node = self.tail + res = self._double_link_list.remove(node) + if res: + return node.value + + def pop_left(self): + node = self.head + res = self._double_link_list.remove(node) + if res: + return node.value + + +def test_double_ended_queue(): + deq = Dequeue() + + for i in range(10): + deq.append(i) + + for i in range(10): + assert deq.pop_left() == i + + for i in range(10): + deq.append_left(i) + + for i in range(10): + assert deq.pop() == i + diff --git a/ehco/sort/sort.py b/ehco/sort/sort.py new file mode 100644 index 0000000..d4f8b48 --- /dev/null +++ b/ehco/sort/sort.py @@ -0,0 +1,59 @@ +def bubble_sort(array): + "O(n2)" + lens = len(array) - 1 + for i in range(lens): + for j in range(lens - i): + if array[j] > array[j + 1]: + array[j], array[j + 1] = array[j + 1], array[j] + return array + + +def select_sort(array): + "O(n2)" + lens = len(array) + for i in range(lens): + min_idx = i + for j in range(i + 1, lens): + if array[j] < array[min_idx]: + min_idx = j + if min_idx != i: + array[i], array[min_idx] = array[min_idx], array[i] + return array + + +def insert_sort(array): + "O(n2)" + lens = len(array) + for i in range(1, lens): + val = array[i] + pos = i + while pos > 0 and val < array[pos - 1]: + # 把大的值往后移动 + array[pos] = array[pos - 1] + pos -= 1 + array[pos] = val + return array + + +def quick_sort(array): + size = len(array) + if not array or size < 2: + return array + pivot_idx = 0 + pivot = array[pivot_idx] + less_part = [array[i] for i in range(size) if array[i] <= pivot and pivot_idx != i] + great_part = [array[i] for i in range(size) if array[i] > pivot and pivot_idx != i] + return quick_sort(less_part) + [pivot] + quick_sort(great_part) + + +def test_sort(): + import random + + seq = list(range(10)) + random.shuffle(seq) + + assert bubble_sort(seq) == sorted(seq) + assert select_sort(seq) == sorted(seq) + assert insert_sort(seq) == sorted(seq) + assert quick_sort(seq) == sorted(seq) + diff --git a/ehco/stack/stack.py b/ehco/stack/stack.py index b5e2ce1..6b894c8 100644 --- a/ehco/stack/stack.py +++ b/ehco/stack/stack.py @@ -1,156 +1,65 @@ -# -*- coding: utf-8 -*- +from ehco.queue.dubble_ended_queue import Dequeue -# NOTE: 这里拷贝的 double_link_list.py 里的代码 +class Stack: + def __init__(self): + self._dequeue = Dequeue() + self.lens = 0 -class Node(object): - - def __init__(self, value=None, prev=None, next=None): - self.value, self.prev, self.next = value, prev, next - + def push(self, value): + self._dequeue.append(value) + self.lens += 1 -class CircularDoubleLinkedList(object): - """循环双端链表 ADT - 多了个循环其实就是把 root 的 prev 指向 tail 节点,串起来 - """ + def pop(self): + if self.lens > 0: + self.lens -= 1 + return self._dequeue.pop() - def __init__(self, maxsize=None): - self.maxsize = maxsize - node = Node() - node.next, node.prev = node, node - self.root = node - self.length = 0 - def __len__(self): - return self.length +class MinMaxStack: + def __init__(self): + self._stack = Stack() + self._min_stack = Stack() + self._max_stack = Stack() - @property - def headnode(self): - return self.root.next + def push(self, value): + self._stack.push(value) + if self._min_stack.lens == 0: + self._min_stack.push(value) + else: + if value < self.min: + self._min_stack.push(value) - @property - def tailnode(self): - return self.root.prev - - def append(self, value): # O(1), 你发现一般不用 for 循环的就是 O(1),有限个步骤 - if self.maxsize is not None and len(self) > self.maxsize: - raise Exception('LinkedList is Full') - node = Node(value=value) - tailnode = self.tailnode or self.root - - tailnode.next = node - node.prev = tailnode - node.next = self.root - self.root.prev = node - self.length += 1 - - def appendleft(self, value): - if self.maxsize is not None and len(self) > self.maxsize: - raise Exception('LinkedList is Full') - node = Node(value=value) - if self.root.next is self.root: # empty - node.next = self.root - node.prev = self.root - self.root.next = node - self.root.prev = node + if self._max_stack.lens == 0: + self._max_stack.push(value) else: - node.prev = self.root - headnode = self.root.next - node.next = headnode - headnode.prev = node - self.root.next = node - self.length += 1 - - def remove(self, node): # O(1),传入node 而不是 value 我们就能实现 O(1) 删除 - """remove - :param node # 在 lru_cache 里实际上根据key 保存了整个node: - """ - if node is self.root: - return - else: # - node.prev.next = node.next - node.next.prev = node.prev - self.length -= 1 - return node - - def iter_node(self): - if self.root.next is self.root: - return - curnode = self.root.next - while curnode.next is not self.root: - yield curnode - curnode = curnode.next - yield curnode - - def __iter__(self): - for node in self.iter_node(): - yield node.value - - def iter_node_reverse(self): - """相比单链表独有的反序遍历""" - if self.root.prev is self.root: - return - curnode = self.root.prev - while curnode.prev is not self.root: - yield curnode - curnode = curnode.prev - yield curnode - - -############################################################ -# 分割线,下边是本章 内容实现 -############################################################ - - -class Deque(CircularDoubleLinkedList): # 注意这里我们用到了继承,嗯,貌似我说过不会用啥 OOP 特性的,抱歉 + if value > self.max: + self._max_stack.push(value) def pop(self): - '''删除尾节点''' - if len(self) == 0: - raise Exception('empty') - tailnode = self.tailnode - value = tailnode.value - self.remove(tailnode) - return value - - def popleft(self): - if len(self) == 0: - raise Exception('empty') - headnode = self.headnode - value = headnode.value - self.remove(headnode) - return value - - -def test_deque(): - dq = Deque() - dq.append(1) - - dq.append(2) - assert list(dq) == [1, 2] - - dq.appendleft(0) - assert list(dq) == [0, 1, 2] - - dq.pop() - assert list(dq) == [0, 1] + val = self._stack.pop() + now_min = self._min_stack.pop() + if val != now_min: + self._min_stack.push(now_min) - dq.popleft() - assert list(dq) == [1] + now_max = self._max_stack.pop() + if val != now_max: + self._max_stack.push(now_max) + return val - dq.pop() - assert len(dq) == 0 - - -class Stack(object): - def __init__(self): - self.deque = Deque() - - def push(self, value): - self.deque.append(value) + @property + def min(self): + value = self._min_stack.pop() + if value is not None: + self._min_stack.push(value) + return value - def pop(self): - return self.deque.pop() + @property + def max(self): + value = self._max_stack.pop() + if value is not None: + self._max_stack.push(value) + return value def test_stack(): @@ -163,11 +72,21 @@ def test_stack(): assert s.pop() == 1 assert s.pop() == 0 - import pytest # pip install pytest - with pytest.raises(Exception) as excinfo: # 我们来测试是否真的抛出了异常 - s.pop() - assert 'empty' in str(excinfo.value) +def test_min_max_stack(): + s = MinMaxStack() + s.push(0) + s.push(1) + s.push(2) + + assert s.max == 2 + assert s.min == 0 + + assert s.pop() == 2 + assert s.max == 1 + assert s.min == 0 + + s.push(-1) + assert s.max == 1 + assert s.min == -1 -if __name__ == '__main__': - test_stack() diff --git a/ehco/tree/tree.py b/ehco/tree/tree.py new file mode 100644 index 0000000..d2117bb --- /dev/null +++ b/ehco/tree/tree.py @@ -0,0 +1,41 @@ +class Node: + def __init__(self, val=None, left=None, right=None): + self.val = val + self.left = left + self.right = right + + def __str__(self): + return f"Node:{self.val}" + + __repr__ = __str__ + + +class Tree: + def __init__(self, root): + self.root = root + + @classmethod + def build_tree_from_array(cls, array): + # array BFS + + lens = len(array) + node_list = [Node(val) for val in array] + t = cls(node_list[0]) + for idx, node in enumerate(node_list): + left_idx = 2 * idx + 1 + right_idx = 2 * idx + 2 + if left_idx < lens: + node.left = node_list[left_idx] + if right_idx < lens: + node.right = node_list[right_idx] + return t + + def bfs(self): + queue = [self.root] + res = [] + while queue: + cur = queue.pop(0) + res.append(cur.val) + cur.left and queue.append(cur.left) + cur.right and queue.append(cur.right) + return res