├── .gitignore ├── 02_Array_and_list └── 02_array_and_list.py ├── 03_Linked_list ├── 03_double_linked_list.py ├── 03_fib_cache.py ├── 03_linked_list.py ├── 03_lru_cache.py └── 03_lru_cache_OrderedDict.py ├── 04_Queue ├── 04_array_queue.py ├── 04_double_ended_queue.py └── 04_linked_list_queue.py ├── 05_Stack ├── 05_array_stack.py └── 05_deque_stack.py ├── 07_Hashtable └── 07_hashtable.py ├── 08_Dict └── 08_dict_adt.py ├── 09_Set └── 09_set_adt.py ├── 10_Recursion ├── 10_flatten.py └── 10_hanoi_tower.py ├── 11_Linear_search_and_binary_search ├── 11_binary_search.py ├── 11_binary_search_2.py ├── 11_linear_search.py └── 11_lower_bound.py ├── 12_Basic_sort ├── 12_basic_sort.py └── 12_shell_sort.py ├── 13_Advanced_sort ├── 13_merge_sort.py └── 13_quick_sort.py ├── 14_Tree_and_binary_tree └── 14_BinTree.py ├── 15_Heap_and_heapsort ├── 15_heapsort.py ├── 15_max_heap.py ├── 15_min_heap.py └── 15_topk.py ├── 16_Priority_Queue ├── 16_priority_queue.py └── 16_priority_queue_heapq.py ├── 17_Binary_Search_Tree ├── 17_AVL_tree.py └── 17_binary_search_tree.py ├── 18_Graph_and_Graph_Algorithms ├── 18_BFS.py ├── 18_DFS.py ├── 18_Dijkstra's_bad.py ├── 18_Dijkstra_dict.py ├── 18_Dijkstra_pq.py ├── 18_Prim’s_Spanning_Tree.py ├── 18_graph_adt.py └── 18_topological_sorting.py ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── Reference ├── Grokking Algorithms │ ├── binary_search.py │ ├── breadth-first_search.py │ ├── quicksort.py │ └── slectionSort.py ├── Problem Solving with Algorithms and Data Structures │ ├── 1.1 Newton_iterate.py │ ├── Maximum Subsequence Sum.py │ ├── gcd.py │ └── 最大子列和.py └── progs │ ├── assoc.py │ ├── avltree.py │ ├── bintree.py │ ├── bintree0.py │ ├── bintree_huffman.py │ ├── dict-1.py │ ├── dict_bitree.py │ ├── dict_list.py │ ├── dict_optbitree.py │ ├── graph.py │ ├── graph_basic.py │ ├── graph_shortestpath.py │ ├── graph_shortestpath_1.py │ ├── graph_spantree-1.py │ ├── graph_spantree.py │ ├── graph_toposort.py │ ├── list_circlinked.py │ ├── list_doublinked.py │ ├── list_josephus.py │ ├── list_linked.py │ ├── list_linked1.py │ ├── list_node.py │ ├── person.py │ ├── prioqueue.py │ ├── queue_list.py │ ├── rational.py │ ├── rational0.py │ ├── regex.py │ ├── simulation-customs.py │ ├── sort_algs.py │ ├── stack_app1.py │ ├── stack_app2.py │ ├── stack_app3.py │ ├── stack_linked.py │ ├── stack_list.py │ ├── strings.py │ └── tree-list.py └── assets ├── 1553052998478.png ├── 1553053035687.png ├── 1553053092365.png ├── 1553053098758.png ├── 1553053131963.png ├── 1553053168498.png ├── 1553053196554.png ├── 2011010219003441.jpg ├── 251730074203156-1553230031367.jpg ├── 251730074203156.jpg ├── 8394323_1307440587b6WG.jpg ├── 8394323_13074405906V6Q.jpg ├── 849589-20171015223238449-2146169197.gif ├── 849589-20171015224719590-1433219824.gif ├── 849589-20171015225645277-1151100000.gif ├── 849589-20171015230557043-37375010.gif ├── 849589-20171015230936371-1413523412.gif ├── 849589-20180331170017421-364506073.gif ├── adjMat-1553007345454.png ├── adjMat.png ├── adjlist.png ├── bfderive.png ├── bst.png ├── bst_worstcase.png ├── complete_binary_tree.png ├── digraph.png ├── full_binary_tree.png ├── function_growth.png ├── partitionA.png ├── partitionB.png ├── perfect_binary_tree.png └── routeGraph.png /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/linux,python,pycharm 3 | # Edit at https://www.gitignore.io/?templates=linux,python,pycharm 4 | 5 | ### Linux ### 6 | *~ 7 | 8 | # temporary files which can be created if a process still has a handle open of a deleted file 9 | .fuse_hidden* 10 | 11 | # KDE directory preferences 12 | .directory 13 | 14 | # Linux trash folder which might appear on any partition or disk 15 | .Trash-* 16 | 17 | # .nfs files are created when an open file is removed but is still being accessed 18 | .nfs* 19 | 20 | ### PyCharm ### 21 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 22 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 23 | 24 | # User-specific stuff 25 | .vscode 26 | .idea 27 | .idea/**/workspace.xml 28 | .idea/**/tasks.xml 29 | .idea/**/usage.statistics.xml 30 | .idea/**/dictionaries 31 | .idea/**/shelf 32 | 33 | # Generated files 34 | .idea/**/contentModel.xml 35 | 36 | # Sensitive or high-churn files 37 | .idea/**/dataSources/ 38 | .idea/**/dataSources.ids 39 | .idea/**/dataSources.local.xml 40 | .idea/**/sqlDataSources.xml 41 | .idea/**/dynamic.xml 42 | .idea/**/uiDesigner.xml 43 | .idea/**/dbnavigator.xml 44 | 45 | # Gradle 46 | .idea/**/gradle.xml 47 | .idea/**/libraries 48 | 49 | # Gradle and Maven with auto-import 50 | # When using Gradle or Maven with auto-import, you should exclude module files, 51 | # since they will be recreated, and may cause churn. Uncomment if using 52 | # auto-import. 53 | # .idea/modules.xml 54 | # .idea/*.iml 55 | # .idea/modules 56 | 57 | # CMake 58 | cmake-build-*/ 59 | 60 | # Mongo Explorer plugin 61 | .idea/**/mongoSettings.xml 62 | 63 | # File-based project format 64 | *.iws 65 | 66 | # IntelliJ 67 | out/ 68 | 69 | # mpeltonen/sbt-idea plugin 70 | .idea_modules/ 71 | 72 | # JIRA plugin 73 | atlassian-ide-plugin.xml 74 | 75 | # Cursive Clojure plugin 76 | .idea/replstate.xml 77 | 78 | # Crashlytics plugin (for Android Studio and IntelliJ) 79 | com_crashlytics_export_strings.xml 80 | crashlytics.properties 81 | crashlytics-build.properties 82 | fabric.properties 83 | 84 | # Editor-based Rest Client 85 | .idea/httpRequests 86 | 87 | # Android studio 3.1+ serialized cache file 88 | .idea/caches/build_file_checksums.ser 89 | 90 | ### PyCharm Patch ### 91 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 92 | 93 | # *.iml 94 | # modules.xml 95 | # .idea/misc.xml 96 | # *.ipr 97 | 98 | # Sonarlint plugin 99 | .idea/sonarlint 100 | 101 | ### Python ### 102 | # Byte-compiled / optimized / DLL files 103 | __pycache__/ 104 | *.py[cod] 105 | *$py.class 106 | 107 | # C extensions 108 | *.so 109 | 110 | # Distribution / packaging 111 | .Python 112 | build/ 113 | develop-eggs/ 114 | dist/ 115 | downloads/ 116 | eggs/ 117 | .eggs/ 118 | lib/ 119 | lib64/ 120 | parts/ 121 | sdist/ 122 | var/ 123 | wheels/ 124 | pip-wheel-metadata/ 125 | share/python-wheels/ 126 | *.egg-info/ 127 | .installed.cfg 128 | *.egg 129 | MANIFEST 130 | 131 | # PyInstaller 132 | # Usually these files are written by a python script from a template 133 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 134 | *.manifest 135 | *.spec 136 | 137 | # Installer logs 138 | pip-log.txt 139 | pip-delete-this-directory.txt 140 | 141 | # Unit test / coverage reports 142 | htmlcov/ 143 | .tox/ 144 | .nox/ 145 | .coverage 146 | .coverage.* 147 | .cache 148 | nosetests.xml 149 | coverage.xml 150 | *.cover 151 | .hypothesis/ 152 | .pytest_cache/ 153 | 154 | # Translations 155 | *.mo 156 | *.pot 157 | 158 | # Django stuff: 159 | *.log 160 | local_settings.py 161 | db.sqlite3 162 | 163 | # Flask stuff: 164 | instance/ 165 | .webassets-cache 166 | 167 | # Scrapy stuff: 168 | .scrapy 169 | 170 | # Sphinx documentation 171 | docs/_build/ 172 | 173 | # PyBuilder 174 | target/ 175 | 176 | # Jupyter Notebook 177 | .ipynb_checkpoints 178 | 179 | # IPython 180 | profile_default/ 181 | ipython_config.py 182 | 183 | # pyenv 184 | .python-version 185 | 186 | # celery beat schedule file 187 | celerybeat-schedule 188 | 189 | # SageMath parsed files 190 | *.sage.py 191 | 192 | # Environments 193 | .env 194 | .venv 195 | env/ 196 | venv/ 197 | ENV/ 198 | env.bak/ 199 | venv.bak/ 200 | 201 | # Spyder project settings 202 | .spyderproject 203 | .spyproject 204 | 205 | # Rope project settings 206 | .ropeproject 207 | 208 | # mkdocs documentation 209 | /site 210 | 211 | # mypy 212 | .mypy_cache/ 213 | .dmypy.json 214 | dmypy.json 215 | 216 | # Pyre type checker 217 | .pyre/ 218 | 219 | ### Python Patch ### 220 | .venv/ 221 | 222 | # End of https://www.gitignore.io/api/linux,python,pycharm 223 | -------------------------------------------------------------------------------- /02_Array_and_list/02_array_and_list.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): # 关键属性:分配空间和存储单位(使用列表的单个元素作为一个存储单位) 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | def test_array(): 25 | size = 10 26 | a = Array(size) 27 | a[0] = 1 28 | assert a[0] == 1 29 | assert len(a) == 10 30 | -------------------------------------------------------------------------------- /03_Linked_list/03_double_linked_list.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | __slots__ = ('value', 'prev', 'next') 3 | 4 | def __init__(self, value=None, prev=None, next=None): 5 | self.value, self.prev, self.next = value, prev, next 6 | 7 | 8 | class CircularDoubleLinkedList(object): 9 | """循环双端链表 ADT 10 | 循环就是把root的prev指向tail节点,串起来 11 | """ 12 | 13 | def __init__(self, maxsize=None): # 关键属性:可用空间,根节点,长度 14 | self.maxsize = maxsize 15 | node = Node() 16 | node.next, node.prev = node, node 17 | self.root = node 18 | self.length = 0 19 | 20 | def __len__(self): 21 | return self.length 22 | 23 | def headnode(self): 24 | return self.root.next 25 | 26 | def tailnode(self): 27 | return self.root.prev 28 | 29 | def append(self, value): 30 | if self.maxsize is not None and len(self) >= self.maxsize: # 先看看插入的链表是否已满 31 | raise Exception('LinkedList is full.') 32 | node = Node(value=value) 33 | tailnode = self.tailnode() 34 | 35 | tailnode.next = node 36 | node.prev = tailnode 37 | node.next = self.root 38 | self.root.prev = node 39 | self.length += 1 40 | 41 | def appendleft(self, value): 42 | if self.maxsize is not None and len(self) >= self.maxsize: # 检查链表是否已满 43 | raise Exception('LinkedList is full.') 44 | node = Node(value=value) 45 | 46 | headnode = self.headnode() 47 | self.root.next = node 48 | node.prev = self.root 49 | node.next = headnode 50 | headnode.prev = node 51 | self.length += 1 52 | 53 | def remove(self, node): 54 | """remove 55 | :param node: 传入node 而不是 value 我们就能实现 O(1) 删除 56 | :return: 57 | """ 58 | if node is self.root: # 判断是否为根节点 59 | return 60 | else: 61 | node.prev.next = node.next 62 | node.next.prev = node.prev 63 | self.length -= 1 64 | return node 65 | 66 | def iter_node(self): 67 | if self.root.next is self.root: 68 | return 69 | curnode = self.root.next 70 | while curnode.next is not self.root: 71 | yield curnode 72 | curnode = curnode.next 73 | yield curnode 74 | 75 | def __iter__(self): 76 | for node in self.iter_node(): 77 | yield node.value 78 | 79 | def iter_node_reverse(self): 80 | """相比单链表独有的反序遍历""" 81 | if self.root.prev is self.root: 82 | return 83 | curnode = self.root.prev 84 | while curnode.prev is not self.root: 85 | yield curnode 86 | curnode = curnode.prev 87 | yield curnode 88 | 89 | 90 | def test_double_link_list(): 91 | dll = CircularDoubleLinkedList() 92 | assert len(dll) == 0 93 | 94 | dll.appendleft(0) 95 | assert list(dll) == [0] 96 | assert len(dll) == 1 97 | assert dll.root.next is not dll.root 98 | headnode = dll.headnode() 99 | assert headnode.value == 0 100 | dll.remove(headnode) 101 | assert len(dll) == 0 102 | dll.append(0) 103 | dll.append(1) 104 | dll.append(2) 105 | 106 | assert list(dll) == [0, 1, 2] 107 | 108 | assert [node.value for node in dll.iter_node()] == [0, 1, 2] 109 | assert [node.value for node in dll.iter_node_reverse()] == [2, 1, 0] 110 | 111 | headnode = dll.headnode() 112 | assert headnode.value == 0 113 | dll.remove(headnode) 114 | assert len(dll) == 2 115 | assert [node.value for node in dll.iter_node()] == [1, 2] 116 | 117 | dll.appendleft(0) 118 | assert [node.value for node in dll.iter_node()] == [0, 1, 2] -------------------------------------------------------------------------------- /03_Linked_list/03_fib_cache.py: -------------------------------------------------------------------------------- 1 | def cache(func): 2 | data = {} 3 | def wrapper(n): 4 | if n in data: 5 | return data[n] 6 | else: 7 | res = func(n) 8 | data[n] = res 9 | return res 10 | return wrapper 11 | 12 | 13 | @cache 14 | def fib(n): 15 | if n <= 2: # 1 or 2 16 | return 1 17 | else: 18 | return fib(n-1) + fib(n-2) 19 | 20 | for i in range(1, 50): 21 | print(fib(i)) -------------------------------------------------------------------------------- /03_Linked_list/03_linked_list.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | __slots__ = ('value', 'next') 3 | 4 | def __init__(self, value=None, next=None): # 定义链表中的单个节点 5 | self.value = value 6 | self.next = next 7 | 8 | def __str__(self): 9 | return ''.format(self.value, self.next) 10 | 11 | __repr__ = __str__ 12 | 13 | 14 | class LinkedList(object): 15 | """链接表 ADT 16 | [ROOT] -> [node0] -> [node1] -> [node2] 17 | """ 18 | 19 | def __init__(self, maxsize=None): 20 | """链表的关键属性:可用空间,根节点,尾节点指针,长度 21 | :param maxsize: int or None, 如果是 None,链表可用空间可无限扩充 22 | """ 23 | self.maxsize = maxsize 24 | self.root = Node() # 默认 root 节点指向 None 25 | self.tailnode = None 26 | self.length = 0 27 | 28 | def __len__(self): 29 | return self.length 30 | 31 | def append(self, value): 32 | if self.maxsize is not None and len(self) >= self.maxsize: # 先看看插入的链表是否已满 33 | raise Exception('LinkedList is Full') 34 | node = Node(value) # 构造节点 35 | tailnode = self.tailnode 36 | if tailnode is None: # 检查链表是否为空,即没有插入过新节点 37 | self.root.next = node # # 还没有 append 过,length = 0, 追加到 root 后 38 | else: # 否则追加到最后一个节点的后边,并更新最后一个节点是 append 的节点 39 | tailnode.next = node 40 | self.tailnode = node # 更新尾节点指向append的节点 41 | self.length += 1 42 | 43 | def appendleft(self, value): 44 | if self.maxsize is not None and len(self) >= self.maxsize: 45 | raise Exception('LinkedList is Full') 46 | node = Node(value) 47 | if self.tailnode is None: # 如果原链表为空,插入第一个元素需要设置 tailnode 48 | self.tailnode = node 49 | 50 | headnode = self.root.next # 考虑原链表为空或不为空两种情况,执行代码相同 51 | self.root.next = node 52 | node.next = headnode 53 | self.length += 1 54 | 55 | def iter_node(self): 56 | """遍历 从 head 节点到 tail 节点""" 57 | curnode = self.root.next # 从第一个节点开始遍历 58 | while curnode is not self.tailnode: # 当前遍历节点为尾节点时终止循环 59 | yield curnode 60 | curnode = curnode.next # 移动到下一个节点 61 | if curnode is not None: 62 | yield curnode # yield当前遍历到的节点 63 | 64 | def __iter__(self): 65 | for node in self.iter_node(): 66 | yield node.value 67 | 68 | def remove(self, value): 69 | """删除包含值的一个节点,将其前一个节点的next指向被查询节点的下一个节点即可 70 | 71 | :param value: 要删除的值 72 | :return: 1或-1,表明删除操作是否成功 73 | """ 74 | prevnode = self.root # 需要设置变量记住要删除节点的上一个节点,初始值为根节点 75 | for curnode in self.iter_node(): # 遍历查找等于value值的节点 76 | if curnode.value == value: 77 | prevnode.next = curnode.next 78 | if curnode is self.tailnode: # NOTE: 注意更新 tailnode 79 | if prevnode is self.root: 80 | self.tailnode = None # 当前一个节点为根节点时,仍将尾节点指向None 81 | else: 82 | self.tailnode = prevnode # 否则指向前一个节点即可 83 | del curnode 84 | self.length -= 1 85 | return 1 # 表明删除成功 86 | else: 87 | prevnode = curnode 88 | return -1 # 表明删除失败 89 | 90 | def find(self, value): # O(n) 91 | """查找一个节点,返回序号,从0开始 92 | 93 | :param value: 查找的值 94 | """ 95 | index = 0 96 | for node in self.iter_node(): 97 | if node.value == value: 98 | return index 99 | index += 1 100 | return -1 # 没找到 101 | 102 | def popleft(self): 103 | """删除第一个链表节点 104 | """ 105 | if self.root.next is None: 106 | raise Exception('pop from empty LinkedList') 107 | headnode = self.root.next 108 | self.root.next = headnode.next 109 | self.length -= 1 110 | value = headnode.value 111 | 112 | if self.tailnode is headnode: # 单节点删除 tailnode 的处理 113 | self.tailnode = None 114 | del headnode 115 | return value 116 | 117 | def clear(self): 118 | for node in self.iter_node(): 119 | del node 120 | self.root.next = None 121 | self.length = 0 122 | self.tailnode = None 123 | 124 | 125 | def test_linked_list(): 126 | ll = LinkedList() 127 | 128 | ll.append(0) 129 | ll.append(1) 130 | ll.append(2) 131 | ll.append(3) 132 | 133 | assert len(ll) == 4 134 | assert ll.find(2) == 2 135 | assert ll.find(-1) == -1 136 | 137 | assert ll.remove(0) == 1 138 | assert ll.remove(10) == -1 139 | assert ll.remove(2) == 1 140 | assert len(ll) == 2 141 | assert list(ll) == [1, 3] 142 | assert ll.find(0) == -1 143 | 144 | ll.appendleft(0) 145 | assert list(ll) == [0, 1, 3] 146 | assert len(ll) == 3 147 | 148 | headvalue = ll.popleft() 149 | assert headvalue == 0 150 | assert len(ll) == 2 151 | assert list(ll) == [1, 3] 152 | 153 | assert ll.popleft() == 1 154 | assert list(ll) == [3] 155 | ll.popleft() 156 | assert len(ll) == 0 157 | assert ll.tailnode is None 158 | 159 | ll.clear() 160 | assert len(ll) == 0 161 | assert list(ll) == [] 162 | 163 | 164 | def test_linked_list_remove(): 165 | ll = LinkedList() 166 | ll.append(3) 167 | ll.append(4) 168 | ll.append(5) 169 | ll.append(6) 170 | ll.append(7) 171 | ll.remove(7) 172 | print(list(ll)) 173 | 174 | 175 | def test_linked_list_append(): 176 | ll = LinkedList() 177 | ll.appendleft(1) 178 | ll.append(2) 179 | assert list(ll) == [1, 2] 180 | -------------------------------------------------------------------------------- /03_Linked_list/03_lru_cache.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | class Node(object): 5 | __slots__ = ('value', 'prev', 'key', 'next') 6 | 7 | def __init__(self, value=None, prev=None, key=None, next=None): 8 | self.value, self.prev, self.key, self.next = value, prev, key, next 9 | 10 | 11 | class CircularDoubleLinkedList(object): 12 | """循环双端链表 ADT 13 | 循环就是把root的prev指向tail节点,串起来 14 | """ 15 | 16 | def __init__(self, maxsize=None): 17 | self.maxsize = maxsize 18 | node = Node() 19 | node.next, node.prev = node, node 20 | self.root = node 21 | self.length = 0 22 | 23 | def __len__(self): 24 | return self.length 25 | 26 | def headnode(self): 27 | return self.root.next 28 | 29 | def tailnode(self): 30 | return self.root.prev 31 | 32 | def append(self, node): 33 | tailnode = self.tailnode() 34 | 35 | tailnode.next = node 36 | node.prev = tailnode 37 | node.next = self.root 38 | self.root.prev = node 39 | self.length += 1 40 | 41 | def remove(self, node): 42 | """remove 43 | :param node: 传入node 而不是 value 我们就能实现 O(1) 删除 44 | :return: 45 | """ 46 | if node is self.root: 47 | return 48 | else: 49 | node.prev.next = node.next 50 | node.next.prev = node.prev 51 | self.length -= 1 52 | return node 53 | 54 | 55 | class LRUCache(object): 56 | def __init__(self, maxsize=16): 57 | self.maxsize = maxsize 58 | self.cache = {} 59 | self.access = CircularDoubleLinkedList() 60 | self.isfull = len(self.cache) >= self.maxsize 61 | 62 | def __call__(self, func): 63 | def wrapper(n): 64 | cachenode = self.cache.get(n) 65 | if cachenode is not None: # hit 66 | self.access.remove(cachenode) 67 | self.access.append(cachenode) 68 | value = cachenode.value 69 | else: # miss 70 | value = func(n) 71 | newnode = Node(key=n, value=value) 72 | self.cache[n] = newnode 73 | if self.isfull: # 队列已满 74 | lru_node = self.access.headnode() 75 | self.access.remove(lru_node) 76 | self.access.append(newnode) 77 | del self.cache[lru_node.key] 78 | else: # 队列未满 79 | self.access.append(newnode) 80 | self.isfull = len(self.cache) >= self.maxsize 81 | return value 82 | 83 | return wrapper 84 | 85 | 86 | @LRUCache() 87 | def fib(n): 88 | if n <= 2: # 1 or 2 89 | return 1 90 | else: 91 | return fib(n - 1) + fib(n - 2) 92 | 93 | 94 | def test_lru_cache(): 95 | beg = time.time() 96 | for i in range(1, 50): 97 | print(fib(i)) 98 | end = time.time() 99 | print(end - beg) 100 | 101 | 102 | if __name__ == '__main__': 103 | test_lru_cache() 104 | -------------------------------------------------------------------------------- /03_Linked_list/03_lru_cache_OrderedDict.py: -------------------------------------------------------------------------------- 1 | """ 2 | 运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。 3 | 4 | 获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。 5 | 写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除删除最长时间未使用的数据值,从而为新的数据值留出空间。 6 | 7 | 进阶: 8 | 9 | 你是否可以在 O(1) 时间复杂度内完成这两种操作? 10 | 11 | 示例: 12 | 13 | LRUCache cache = new LRUCache( 2 /* 缓存容量 */ ); 14 | 15 | cache.put(1, 1); 16 | cache.put(2, 2); 17 | cache.get(1); // 返回 1 18 | cache.put(3, 3); // 该操作会使得密钥 2 作废 19 | cache.get(2); // 返回 -1 (未找到) 20 | cache.put(4, 4); // 该操作会使得密钥 1 作废 21 | cache.get(1); // 返回 -1 (未找到) 22 | cache.get(3); // 返回 3 23 | cache.get(4); // 返回 4 24 | """ 25 | 26 | from collections import OrderedDict 27 | 28 | 29 | class LRUCache(object): 30 | """Implement LRUCache using OrderedDict""" 31 | 32 | def __init__(self, capacity: int): 33 | self._ordered_dict = OrderedDict() 34 | self._capacity = capacity 35 | 36 | def get(self, key: int) -> int: # 取回值,假定值存在,若不存在返回-1 37 | self._move_to_end_if_exist(key) 38 | 39 | return self._ordered_dict.get(key, -1) 40 | 41 | def put(self, key: int, value: int) -> None: # 添加或更新键值对 42 | self._move_to_end_if_exist(key) 43 | 44 | self._ordered_dict[key] = value 45 | if len(self._ordered_dict) > self._capacity: 46 | self._ordered_dict.popitem(last=False) # popitem支持弹出头部或尾部 47 | 48 | def _move_to_end_if_exist(self, key: int) -> None: 49 | if key in self._ordered_dict: 50 | self._ordered_dict.move_to_end(key) 51 | -------------------------------------------------------------------------------- /04_Queue/04_array_queue.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class FullError(Exception): 25 | pass 26 | 27 | 28 | class EmptyError(Exception): 29 | pass 30 | 31 | 32 | class ArrayQueue(): 33 | def __init__(self, maxsize): 34 | self.maxsize = maxsize 35 | self.array = Array(maxsize) 36 | self.head = 0 37 | self.tail = 0 38 | 39 | def __len__(self): 40 | return self.head - self.tail 41 | 42 | def push(self, value): 43 | if len(self) >= self.maxsize: 44 | raise FullError('queue full') 45 | self.array[self.head % self.maxsize] = value 46 | self.head += 1 47 | 48 | def pop(self): 49 | if len(self) <= 0: 50 | raise EmptyError('queue empty') 51 | value = self.array[self.tail % self.maxsize] 52 | self.tail += 1 53 | return value 54 | 55 | 56 | def test_array_queue(): 57 | import pytest 58 | size = 5 59 | q = ArrayQueue(size) 60 | for i in range(size): 61 | q.push(i) 62 | 63 | with pytest.raises(FullError) as excinfo: 64 | q.push(size) 65 | assert 'queue full' == str(excinfo.value) 66 | 67 | assert len(q) == size 68 | 69 | assert q.pop() == 0 70 | assert q.pop() == 1 71 | 72 | q.push(5) 73 | assert len(q) == 4 74 | 75 | assert q.pop() == 2 76 | assert q.pop() == 3 77 | assert q.pop() == 4 78 | assert q.pop() == 5 79 | 80 | assert len(q) == 0 81 | with pytest.raises(EmptyError) as excinfo: 82 | q.pop() 83 | assert 'empty' in str(excinfo.value) -------------------------------------------------------------------------------- /04_Queue/04_double_ended_queue.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | __slots__ = ('value', 'prev', 'next') 3 | 4 | def __init__(self, value=None, prev=None, next=None): 5 | self.value, self.prev, self.next = value, prev, next 6 | 7 | 8 | class CircularDoubleLinkedList(object): 9 | """循环双端链表 ADT 10 | 循环就是把root的prev指向tail节点,串起来 11 | """ 12 | 13 | def __init__(self, maxsize=None): 14 | self.maxsize = maxsize 15 | node = Node() 16 | node.next, node.prev = node, node 17 | self.root = node 18 | self.length = 0 19 | 20 | def __len__(self): 21 | return self.length 22 | 23 | def headnode(self): 24 | return self.root.next 25 | 26 | def tailnode(self): 27 | return self.root.prev 28 | 29 | def append(self, value): 30 | if self.maxsize is not None and len(self) >= self.maxsize: # 先看看插入的链表是否已满 31 | raise Exception('LinkedList is full.') 32 | node = Node(value=value) 33 | tailnode = self.tailnode() 34 | 35 | tailnode.next = node 36 | node.prev = tailnode 37 | node.next = self.root 38 | self.root.prev = node 39 | self.length += 1 40 | 41 | def appendleft(self, value): 42 | if self.maxsize is not None and len(self) >= self.maxsize: 43 | raise Exception('LinkedList is full.') 44 | node = Node(value=value) 45 | 46 | headnode = self.headnode() 47 | self.root.next = node 48 | node.prev = self.root 49 | node.next = headnode 50 | headnode.prev = node 51 | self.length += 1 52 | 53 | def remove(self, node): 54 | """remove 55 | :param node: 传入node 而不是 value 我们就能实现 O(1) 删除 56 | :return: 57 | """ 58 | if node is self.root: 59 | return 60 | else: 61 | node.prev.next = node.next 62 | node.next.prev = node.prev 63 | self.length -= 1 64 | return node 65 | 66 | def iter_node(self): 67 | if self.root.next is self.root: 68 | return 69 | curnode = self.root.next 70 | while curnode.next is not self.root: 71 | yield curnode 72 | curnode = curnode.next 73 | yield curnode 74 | 75 | def __iter__(self): 76 | for node in self.iter_node(): 77 | yield node.value 78 | 79 | def iter_node_reverse(self): 80 | """相比单链表独有的反序遍历""" 81 | if self.root.prev is self.root: 82 | return 83 | curnode = self.root.prev 84 | while curnode.prev is not self.root: 85 | yield curnode 86 | curnode = curnode.prev 87 | yield curnode 88 | 89 | 90 | class DoubleEndedQueue(CircularDoubleLinkedList): 91 | 92 | def pop(self): 93 | if self.tailnode() is self.root: 94 | raise Exception('LinkedList is empty.') 95 | tailnode = self.tailnode() 96 | value = tailnode.value 97 | self.remove(tailnode) 98 | return value 99 | 100 | def popleft(self): 101 | if self.headnode() is self.root: 102 | raise Exception('LinkedList is empty.') 103 | headnode = self.headnode() 104 | value = headnode.value 105 | self.remove(headnode) 106 | return value 107 | 108 | 109 | def test_double_ended_queue(): 110 | mydeque = DoubleEndedQueue() 111 | assert len(mydeque) == 0 112 | 113 | mydeque.appendleft(0) 114 | assert list(mydeque) == [0] 115 | assert len(mydeque) == 1 116 | assert mydeque.root.next is not mydeque.root 117 | headnode = mydeque.headnode() 118 | assert headnode.value == 0 119 | mydeque.remove(headnode) 120 | assert len(mydeque) == 0 121 | mydeque.append(0) 122 | mydeque.append(1) 123 | mydeque.append(2) 124 | 125 | assert list(mydeque) == [0, 1, 2] 126 | 127 | assert [node.value for node in mydeque.iter_node()] == [0, 1, 2] 128 | assert [node.value for node in mydeque.iter_node_reverse()] == [2, 1, 0] 129 | 130 | headnode = mydeque.headnode() 131 | assert headnode.value == 0 132 | mydeque.remove(headnode) 133 | assert len(mydeque) == 2 134 | assert [node.value for node in mydeque.iter_node()] == [1, 2] 135 | 136 | mydeque.appendleft(0) 137 | assert [node.value for node in mydeque.iter_node()] == [0, 1, 2] 138 | 139 | mydeque.append(3) 140 | mydeque.pop() 141 | assert [node.value for node in mydeque.iter_node()] == [0, 1, 2] 142 | mydeque.popleft() 143 | assert [node.value for node in mydeque.iter_node()] == [1, 2] 144 | -------------------------------------------------------------------------------- /04_Queue/04_linked_list_queue.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | __slots__ = ('value', 'next') 3 | 4 | def __init__(self, value=None, next=None): # 定义链表中的单个节点 5 | self.value = value 6 | self.next = next 7 | 8 | def __str__(self): 9 | return ''.format(self.value, self.next) 10 | 11 | __repr__ = __str__ 12 | 13 | 14 | class LinkedList(object): 15 | """链接表 ADT 16 | [ROOT] -> [node0] -> [node1] -> [node2] 17 | """ 18 | 19 | def __init__(self, maxsize=None): 20 | """链表的关键属性:可用空间,根节点,尾节点指针,长度 21 | :param maxsize: int or None, 如果是 None,链表可用空间可无限扩充 22 | """ 23 | self.maxsize = maxsize 24 | self.root = Node() # 默认 root 节点指向 None 25 | self.tailnode = None 26 | self.length = 0 27 | 28 | def __len__(self): 29 | return self.length 30 | 31 | def append(self, value): 32 | if self.maxsize is not None and len(self) >= self.maxsize: # 先看看插入的链表是否已满 33 | raise Exception('LinkedList is Full') 34 | node = Node(value) # 构造节点 35 | tailnode = self.tailnode 36 | if tailnode is None: # 检查链表是否为空,即没有插入过新节点 37 | self.root.next = node # # 还没有 append 过,length = 0, 追加到 root 后 38 | else: # 否则追加到最后一个节点的后边,并更新最后一个节点是 append 的节点 39 | tailnode.next = node 40 | self.tailnode = node # 更新尾节点指向append的节点 41 | self.length += 1 42 | 43 | def appendleft(self, value): 44 | if self.maxsize is not None and len(self) >= self.maxsize: 45 | raise Exception('LinkedList is Full') 46 | node = Node(value) 47 | if self.tailnode is None: # 如果原链表为空,插入第一个元素需要设置 tailnode 48 | self.tailnode = node 49 | 50 | headnode = self.root.next # 考虑原链表为空或不为空两种情况,执行代码相同 51 | self.root.next = node 52 | node.next = headnode 53 | self.length += 1 54 | 55 | def iter_node(self): 56 | """遍历 从 head 节点到 tail 节点""" 57 | curnode = self.root.next # 从第一个节点开始遍历 58 | while curnode is not self.tailnode: # 当前遍历节点为尾节点时终止循环 59 | yield curnode 60 | curnode = curnode.next # 移动到下一个节点 61 | if curnode is not None: 62 | yield curnode # yield当前遍历到的节点 63 | 64 | def __iter__(self): 65 | for node in self.iter_node(): 66 | yield node.value 67 | 68 | def remove(self, value): 69 | """删除包含值的一个节点,将其前一个节点的next指向被查询节点的下一个节点即可 70 | 71 | :param value: 要删除的值 72 | :return: 1或-1,表明删除操作是否成功 73 | """ 74 | prevnode = self.root # 需要设置变量记住要删除节点的上一个节点,初始值为根节点 75 | for curnode in self.iter_node(): # 遍历查找等于value值的节点 76 | if curnode.value == value: 77 | prevnode.next = curnode.next 78 | if curnode is self.tailnode: # NOTE: 注意更新 tailnode 79 | if prevnode is self.root: 80 | self.tailnode = None # 当前一个节点为根节点时,仍将尾节点指向None 81 | else: 82 | self.tailnode = prevnode # 否则指向前一个节点即可 83 | del curnode 84 | self.length -= 1 85 | return 1 # 表明删除成功 86 | else: 87 | prevnode = curnode 88 | return -1 # 表明删除失败 89 | 90 | def find(self, value): # O(n) 91 | """查找一个节点,返回序号,从0开始 92 | 93 | :param value: 查找的值 94 | """ 95 | index = 0 96 | for node in self.iter_node(): 97 | if node.value == value: 98 | return index 99 | index += 1 100 | return -1 # 没找到 101 | 102 | def popleft(self): 103 | """删除第一个链表节点 104 | """ 105 | if self.root.next is None: 106 | raise Exception('pop from empty LinkedList') 107 | headnode = self.root.next 108 | self.root.next = headnode.next 109 | self.length -= 1 110 | value = headnode.value 111 | 112 | if self.tailnode is headnode: # 单节点删除 tailnode 的处理 113 | self.tailnode = None 114 | del headnode 115 | return value 116 | 117 | def clear(self): 118 | for node in self.iter_node(): 119 | del node 120 | self.root.next = None 121 | self.length = 0 122 | self.tailnode = None 123 | 124 | 125 | ######################################## 126 | # Queue的实现 127 | ######################################## 128 | class FullError(Exception): 129 | pass 130 | 131 | 132 | class EmptyError(Exception): 133 | pass 134 | 135 | 136 | class Queue(object): 137 | def __init__(self, maxsize=None): 138 | self.maxsize = maxsize 139 | self._item_linked_list = LinkedList() 140 | 141 | def __len__(self): 142 | return len(self._item_linked_list) 143 | 144 | def push(self, value): 145 | if self.maxsize is not None and len(self) >= self.maxsize: # 注意判断队列是否已满 146 | raise FullError('queue full') 147 | return self._item_linked_list.append(value) 148 | 149 | def pop(self): 150 | if len(self) <= 0: # 注意判断队列是否为空 151 | raise EmptyError('queue empty') 152 | return self._item_linked_list.popleft() 153 | 154 | 155 | def test_queue(): 156 | q = Queue() 157 | q.push(0) 158 | q.push(1) 159 | q.push(2) 160 | 161 | assert len(q) == 3 162 | 163 | assert q.pop() == 0 164 | assert q.pop() == 1 165 | assert q.pop() == 2 166 | 167 | import pytest 168 | with pytest.raises(EmptyError) as excinfo: 169 | q.pop() 170 | assert 'queue empty' == str(excinfo.value) -------------------------------------------------------------------------------- /05_Stack/05_array_stack.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): # 关键属性:分配空间和存储单位(使用列表的单个元素作为一个存储单位) 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class FullError(Exception): 25 | pass 26 | 27 | 28 | class EmptyError(Exception): 29 | pass 30 | 31 | 32 | class Stack: 33 | def __init__(self, maxsize): 34 | self.maxsize = maxsize 35 | self.array = Array(maxsize) 36 | self.head = 0 37 | self.tail = 0 38 | 39 | def __len__(self): 40 | return self.head - self.tail 41 | 42 | def push(self, value): 43 | if len(self) >= self.maxsize: 44 | raise FullError('stack full') 45 | self.array[self.head % self.maxsize] = value 46 | self.head += 1 47 | 48 | def pop(self): 49 | if len(self) <= 0: 50 | raise EmptyError('stack empty') 51 | self.head -= 1 52 | value = self.array[self.head % self.maxsize] 53 | return value 54 | 55 | def is_empty(self): 56 | return len(self) == 0 57 | 58 | 59 | def test_stack(): 60 | s = Stack(5) 61 | for i in range(5): 62 | s.push(i) 63 | 64 | assert len(s) == 5 65 | import pytest 66 | with pytest.raises(FullError) as excinfo: 67 | s.push(5) 68 | assert 'full' in str(excinfo.value) 69 | assert s.pop() == 4 70 | assert s.pop() == 3 71 | assert s.pop() == 2 72 | assert s.pop() == 1 73 | assert s.pop() == 0 74 | 75 | assert s.is_empty() 76 | 77 | with pytest.raises(Exception) as excinfo: 78 | s.pop() 79 | assert 'empty' in str(excinfo.value) 80 | -------------------------------------------------------------------------------- /05_Stack/05_deque_stack.py: -------------------------------------------------------------------------------- 1 | class Node(object): 2 | __slots__ = ('value', 'prev', 'next') 3 | 4 | def __init__(self, value=None, prev=None, next=None): 5 | self.value, self.prev, self.next = value, prev, next 6 | 7 | 8 | class CircularDoubleLinkedList(object): 9 | """循环双端链表 ADT 10 | 循环就是把root的prev指向tail节点,串起来 11 | """ 12 | 13 | def __init__(self, maxsize=None): 14 | self.maxsize = maxsize 15 | node = Node() 16 | node.next, node.prev = node, node 17 | self.root = node 18 | self.length = 0 19 | 20 | def __len__(self): 21 | return self.length 22 | 23 | def headnode(self): 24 | return self.root.next 25 | 26 | def tailnode(self): 27 | return self.root.prev 28 | 29 | def append(self, value): 30 | if self.maxsize is not None and len(self) >= self.maxsize: # 先看看插入的链表是否已满 31 | raise Exception('LinkedList is full.') 32 | node = Node(value=value) 33 | tailnode = self.tailnode() 34 | 35 | tailnode.next = node 36 | node.prev = tailnode 37 | node.next = self.root 38 | self.root.prev = node 39 | self.length += 1 40 | 41 | def appendleft(self, value): 42 | if self.maxsize is not None and len(self) >= self.maxsize: 43 | raise Exception('LinkedList is full.') 44 | node = Node(value=value) 45 | 46 | headnode = self.headnode() 47 | self.root.next = node 48 | node.prev = self.root 49 | node.next = headnode 50 | headnode.prev = node 51 | self.length += 1 52 | 53 | def remove(self, node): 54 | """remove 55 | :param node: 传入node 而不是 value 我们就能实现 O(1) 删除 56 | :return: 57 | """ 58 | if node is self.root: 59 | return 60 | else: 61 | node.prev.next = node.next 62 | node.next.prev = node.prev 63 | self.length -= 1 64 | return node 65 | 66 | def iter_node(self): 67 | if self.root.next is self.root: 68 | return 69 | curnode = self.root.next 70 | while curnode.next is not self.root: 71 | yield curnode 72 | curnode = curnode.next 73 | yield curnode 74 | 75 | def __iter__(self): 76 | for node in self.iter_node(): 77 | yield node.value 78 | 79 | def iter_node_reverse(self): 80 | """相比单链表独有的反序遍历""" 81 | if self.root.prev is self.root: 82 | return 83 | curnode = self.root.prev 84 | while curnode.prev is not self.root: 85 | yield curnode 86 | curnode = curnode.prev 87 | yield curnode 88 | 89 | 90 | class DoubleEndedQueue(CircularDoubleLinkedList): 91 | 92 | def pop(self): 93 | if self.tailnode() is self.root: 94 | raise Exception('LinkedList is empty.') 95 | tailnode = self.tailnode() 96 | value = tailnode.value 97 | self.remove(tailnode) 98 | return value 99 | 100 | def popleft(self): 101 | if self.headnode() is self.root: 102 | raise Exception('LinkedList is empty.') 103 | headnode = self.headnode() 104 | value = headnode.value 105 | self.remove(headnode) 106 | return value 107 | 108 | 109 | class Stack: 110 | def __init__(self): 111 | self.deque = DoubleEndedQueue() 112 | 113 | def push(self, value): 114 | return self.deque.append(value) 115 | 116 | def pop(self): 117 | return self.deque.pop() 118 | 119 | def __len__(self): 120 | return len(self.deque) 121 | 122 | def is_empty(self): 123 | return len(self.deque) == 0 124 | 125 | 126 | def test_stack(): 127 | s = Stack() 128 | for i in range(3): 129 | s.push(i) 130 | 131 | assert len(s) == 3 132 | assert s.pop() == 2 133 | assert s.pop() == 1 134 | assert s.pop() == 0 135 | 136 | assert s.is_empty() 137 | 138 | import pytest 139 | with pytest.raises(Exception) as excinfo: 140 | s.pop() 141 | assert 'empty' in str(excinfo.value) 142 | -------------------------------------------------------------------------------- /07_Hashtable/07_hashtable.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32, init=None): # 加入每个槽位的初始值,还是默认为None 3 | self._size = size 4 | self._items = [init] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class Slot(object): 25 | """定义一个 hash 表 数组的槽 26 | 注意,一个槽有三种状态或者说三种值。相比链接法解决冲突,二次探查法删除一个 key 的操作稍微复杂。 27 | 1.从未使用过,值为HashMap.UNUSED(None)。此槽没有被使用和冲突过,查找时只要找到 UNUSED 就不用再继续探查了 28 | 2.使用过但是 remove 了,此时值为 HashMap.EMPTY,该探查点后边的元素仍可能是有key 29 | 3.槽正在使用 Slot 节点 30 | """ 31 | 32 | def __init__(self, key, value): 33 | self.key, self.value = key, value 34 | 35 | 36 | class HashTable(object): 37 | UNUSED = None 38 | EMPTY = Slot(None, None) 39 | 40 | def __init__(self): 41 | self._table = Array(8, init=HashTable.UNUSED) # key-value对以slot对象的形式保存在数组中,slot初始值为HashMap.UNUSED 42 | self.length = 0 43 | 44 | @property 45 | def _load_factor(self): 46 | # 负载因子超过0.8重新分配空间 47 | return self.length / float(len(self._table)) 48 | 49 | def __len__(self): 50 | return self.length 51 | 52 | def _hash(self, key): 53 | return abs(hash(key)) % len(self._table) 54 | 55 | def _find_key(self, key): 56 | """查找key,返回key在数组中的位置 57 | 若index位置的值为UNUSED,说明槽未使用过,key在数组中不存在 58 | 若为EMPTY或key值,则设法返回key在数组中的位置 59 | """ 60 | index = self._hash(key) 61 | _len = len(self._table) 62 | while self._table[index] is not HashTable.UNUSED: 63 | if self._table[index] is HashTable.EMPTY: # 若值为EMPTY,继续探查点后边的元素 64 | index = (index * 5 + 1) % _len 65 | continue # 跳过当前循环的剩余语句 66 | elif self._table[index].key == key: # 若值不为EMPTY,检查slot的key值是否等于key 67 | return index 68 | else: # 有值且不为key,则继续探查点后边的元素 69 | index = (index * 5 + 1) % _len 70 | return None 71 | 72 | def _find_slot_for_insert(self, key): 73 | index = self._hash(key) 74 | _len = len(self._table) 75 | while not self._slot_can_insert(index): 76 | index = (index * 5 + 1) % _len 77 | return index 78 | 79 | def _slot_can_insert(self, index): 80 | return self._table[index] is HashTable.EMPTY or self._table[index] is HashTable.UNUSED 81 | 82 | def __contains__(self, key): # 实现散列表的 in 操作 83 | index = self._find_key(key) 84 | return index is not None 85 | 86 | def add(self, key, value): 87 | """向散列表中添加key-value对 88 | 首先查找key是否已经在散列表中,若已存在,重置其value并返回False; 89 | 若不存在,查找可供插入的槽并插入Slot,并检查负载因子,若有必要则进行重哈希 90 | """ 91 | index = self._find_key(key) 92 | if index is not None: # 更新已存在的key 93 | self._table[index].value = value 94 | return False 95 | else: 96 | index = self._find_slot_for_insert(key) 97 | self._table[index] = Slot(key, value) # 用key-value对构造Slot并插入到数组 98 | self.length += 1 99 | if self._load_factor >= 0.8: 100 | self._rehash() 101 | return True 102 | 103 | def _rehash(self): 104 | oldtable = self._table 105 | newsize = len(self._table) * 2 106 | self._table = Array(newsize, HashTable.UNUSED) # 使用新的大小构造数组 107 | self.length = 0 108 | 109 | for slot in oldtable: # 迭代旧表并将非空节点添加到新数组 110 | if slot is not HashTable.UNUSED and slot is not HashTable.EMPTY: 111 | index = self._find_slot_for_insert(slot.key) 112 | self._table[index] = slot 113 | self.length += 1 114 | 115 | def get(self, key, default=None): 116 | index = self._find_key(key) 117 | if index is None: # 首先检查key是否存在 118 | return default 119 | else: 120 | return self._table[index].value 121 | 122 | def remove(self, key): 123 | index = self._find_key(key) 124 | if index is None: # 首先检查key是否存在 125 | raise KeyError() 126 | else: 127 | value = self._table[index].value 128 | self.length -= 1 129 | self._table[index] = HashTable.EMPTY 130 | return value 131 | 132 | def __iter__(self): 133 | """迭代数组中的slot,若slot不为空,则返回其key值 134 | :return: 135 | """ 136 | for slot in self._table: 137 | if slot not in (HashTable.EMPTY, HashTable.UNUSED): 138 | yield slot.key 139 | 140 | 141 | def test_hash_table(): 142 | h = HashTable() 143 | h.add('a', 0) 144 | h.add('b', 1) 145 | h.add('c', 2) 146 | assert len(h) == 3 147 | assert h.get('a') == 0 148 | assert h.get('b') == 1 149 | assert h.get('hehe') is None 150 | 151 | h.add('a', 3) 152 | assert h.get('a') == 3 153 | 154 | h.remove('a') 155 | assert h.get('a') is None 156 | assert sorted(list(h)) == ['b', 'c'] 157 | 158 | n = 50 159 | for i in range(n): 160 | h.add(i, i) 161 | 162 | for i in range(n): 163 | assert h.get(i) == i -------------------------------------------------------------------------------- /08_Dict/08_dict_adt.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32, init=None): # 加入每个槽位的初始值,还是默认为None 3 | self._size = size 4 | self._items = [init] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class Slot(object): 25 | """定义一个 hash 表 数组的槽 26 | 注意,一个槽有三种状态,看你能否想明白。相比链接法解决冲突,二次探查法删除一个 key 的操作稍微复杂。 27 | 1.从未使用过,值为HashMap.UNUSED(None)。此槽没有被使用和冲突过,查找时只要找到 UNUSED 就不用再继续探查了 28 | 2.使用过但是 remove 了,此时值为 HashMap.EMPTY,该探查点后边的元素仍可能是有key 29 | 3.槽正在使用 Slot 节点 30 | """ 31 | 32 | def __init__(self, key, value): 33 | self.key, self.value = key, value 34 | 35 | 36 | class HashTable(object): 37 | UNUSED = None 38 | EMPTY = Slot(None, None) 39 | 40 | def __init__(self): 41 | self._table = Array(8, init=HashTable.UNUSED) # key-value对以slot对象的形式保存在数组中,slot初始值为HashMap.UNUSED 42 | self.length = 0 43 | 44 | @property 45 | def _load_factor(self): 46 | # 负载因子超过0.8重新分配空间 47 | return self.length / float(len(self._table)) 48 | 49 | def __len__(self): 50 | return self.length 51 | 52 | def _hash(self, key): 53 | return abs(hash(key)) % len(self._table) 54 | 55 | def _find_key(self, key): 56 | """查找key,返回key在数组中的位置 57 | 若index位置的值为UNUSED,说明槽未使用过,key在数组中不存在 58 | 若为EMPTY或key值,则设法返回key在数组中的位置 59 | """ 60 | index = self._hash(key) 61 | _len = len(self._table) 62 | while self._table[index] is not HashTable.UNUSED: 63 | if self._table[index] is HashTable.EMPTY: # 若值为EMPTY,继续探查点后边的元素 64 | index = (index * 5 + 1) % _len 65 | continue # 跳过当前循环的剩余语句 66 | elif self._table[index].key == key: # 若值不为EMPTY,检查slot的key值是否等于key 67 | return index 68 | else: # 有值且不为key,则继续探查点后边的元素 69 | index = (index * 5 + 1) % _len 70 | return None 71 | 72 | def _find_slot_for_insert(self, key): 73 | index = self._hash(key) 74 | _len = len(self._table) 75 | while not self._slot_can_insert(index): 76 | index = (index * 5 + 1) % _len 77 | return index 78 | 79 | def _slot_can_insert(self, index): 80 | return self._table[index] is HashTable.EMPTY or self._table[index] is HashTable.UNUSED 81 | 82 | def __contains__(self, key): # 实现散列表的 in 操作 83 | index = self._find_key(key) 84 | return index is not None 85 | 86 | def add(self, key, value): 87 | """向散列表中添加key-value对 88 | 首先查找key是否已经在散列表中,若已存在,重置其value并返回False; 89 | 若不存在,查找可供插入的槽并插入Slot,并检查负载因子,若有必要则进行重哈希 90 | """ 91 | index = self._find_key(key) 92 | if index is not None: # 更新已存在的key 93 | self._table[index].value = value 94 | return False 95 | else: 96 | index = self._find_slot_for_insert(key) 97 | self._table[index] = Slot(key, value) # 用key-value对构造Slot并插入到数组 98 | self.length += 1 99 | if self._load_factor >= 0.8: 100 | self._rehash() 101 | return True 102 | 103 | def _rehash(self): 104 | oldtable = self._table 105 | newsize = len(self._table) * 2 106 | self._table = Array(newsize, HashTable.UNUSED) # 使用新的大小构造数组 107 | self.length = 0 108 | 109 | for slot in oldtable: # 迭代旧表并将非空节点添加到新数组 110 | if slot is not HashTable.UNUSED and slot is not HashTable.EMPTY: 111 | index = self._find_slot_for_insert(slot.key) 112 | self._table[index] = slot 113 | self.length += 1 114 | 115 | def get(self, key, default=None): 116 | index = self._find_key(key) 117 | if index is None: # 首先检查key是否存在 118 | return default 119 | else: 120 | return self._table[index].value 121 | 122 | def remove(self, key): 123 | index = self._find_key(key) 124 | if index is None: # 首先检查key是否存在 125 | raise KeyError() 126 | else: 127 | value = self._table[index].value 128 | self.length -= 1 129 | self._table[index] = HashTable.EMPTY 130 | return value 131 | 132 | def __iter__(self): 133 | """迭代数组中的slot,若slot不为空,则返回其key值 134 | :return: 135 | """ 136 | for slot in self._table: 137 | if slot not in (HashTable.EMPTY, HashTable.UNUSED): 138 | yield slot.key 139 | 140 | 141 | class DictADT(HashTable): 142 | 143 | def __setitem__(self, key, value): 144 | return self.add(key, value) 145 | 146 | def __getitem__(self, key): 147 | if key not in self: 148 | raise KeyError() 149 | else: 150 | return self.get(key) 151 | 152 | def _iter_slot(self): 153 | for slot in self._table: 154 | if slot not in (HashTable.EMPTY, HashTable.UNUSED): 155 | yield slot 156 | 157 | def pop(self, key, default): 158 | if default and key not in self: 159 | return default 160 | else: 161 | return self.remove(key) 162 | 163 | def items(self): 164 | for slot in self._iter_slot(): 165 | yield (slot.key, slot.value) 166 | 167 | def keys(self): 168 | for slot in self._iter_slot(): 169 | yield slot.key 170 | 171 | def values(self): 172 | for slot in self._iter_slot(): 173 | yield slot.value 174 | 175 | 176 | def test_dict_adt(): 177 | import random 178 | d = DictADT() 179 | 180 | d['a'] = 1 181 | assert d['a'] == 1 182 | d.remove('a') 183 | 184 | ll = list(range(30)) 185 | random.shuffle(ll) 186 | for i in ll: 187 | d.add(i, i) 188 | 189 | for i in range(30): 190 | assert d.get(i) == i 191 | 192 | assert sorted(list(d.keys())) == sorted(ll) 193 | assert sorted(list(d)) == sorted(ll) -------------------------------------------------------------------------------- /10_Recursion/10_flatten.py: -------------------------------------------------------------------------------- 1 | # 实现一个 flatten 函数,把嵌套的列表扁平化,你需要用递归函数来实现。 2 | # 比如 [[1,2], [1,2,3] -> [1,2,1,2,3] 3 | 4 | def flatten(mylist): 5 | result = [] 6 | def nested(mylist): 7 | for item in mylist: 8 | if isinstance(item, list): 9 | nested(item) 10 | else: 11 | result.append(item) 12 | nested(mylist) 13 | return result 14 | 15 | def test_flatten(): 16 | list01 = [1, 2, 3, 4] 17 | assert flatten(list01) == [1, 2, 3, 4] 18 | list02 = [1, [2, 3], 4] 19 | assert flatten(list02) == [1, 2, 3, 4] 20 | list03 = [1, [2, [3, 4]]] 21 | assert flatten(list03) == [1, 2, 3, 4] 22 | list04 = [1, [2, [3, [4]]]] 23 | assert flatten(list04) == [1, 2, 3, 4] 24 | -------------------------------------------------------------------------------- /10_Recursion/10_hanoi_tower.py: -------------------------------------------------------------------------------- 1 | def move(n, source, dest, inter): 2 | if n >= 1: 3 | move(n-1, source, inter, dest) 4 | print('move {} --> {}'.format(source, dest)) 5 | move(n-1, inter, dest, source) 6 | 7 | move(3, 'A', 'C', 'B') -------------------------------------------------------------------------------- /11_Linear_search_and_binary_search/11_binary_search.py: -------------------------------------------------------------------------------- 1 | def binary_search(sorted_list, beg, end, value): # 双闭区间, end=len(a)-1 2 | if not sorted_list: 3 | return -1 4 | 5 | while beg <= end: 6 | mid = (beg + end) // 2 7 | if sorted_list[mid] == value: 8 | return mid 9 | elif sorted_list[mid] < value: 10 | beg = mid + 1 11 | else: 12 | end = mid - 1 13 | return -1 14 | 15 | 16 | def test_binary_search(): 17 | a = list(range(10)) 18 | 19 | assert binary_search(a, 0, len(a) - 1, 3) == 3 20 | assert binary_search(a, 0, len(a) - 1, 10) == -1 21 | assert binary_search(a, 0, len(a) - 1, 9) == 9 22 | 23 | assert binary_search(None, 0, len(a) - 1, 0) == -1 24 | 25 | assert binary_search(a, 0, len(a) - 1, 0) == 0 26 | assert binary_search(a, 0, len(a) - 1, 9) == 9 27 | -------------------------------------------------------------------------------- /11_Linear_search_and_binary_search/11_binary_search_2.py: -------------------------------------------------------------------------------- 1 | def binary_search(sorted_list, beg, end, value): # 左开右闭区间, end=len(sorted_list) 2 | while beg < end: 3 | mid = beg + (end - beg) // 2 4 | if sorted_list[mid] == value: 5 | return mid 6 | elif sorted_list[mid] < value: 7 | beg = mid + 1 8 | else: 9 | end = mid 10 | return beg 11 | 12 | 13 | def test_binary_search(): 14 | a = list(range(10)) 15 | 16 | assert binary_search(a, 0, len(a), 3) == 3 17 | assert binary_search(a, 0, len(a), 10) == 10 18 | assert binary_search(a, 0, len(a), 9) == 9 19 | 20 | assert binary_search(a, 0, len(a), 0) == 0 21 | -------------------------------------------------------------------------------- /11_Linear_search_and_binary_search/11_linear_search.py: -------------------------------------------------------------------------------- 1 | number_list = list(range(8)) 2 | 3 | def linear_search(value, iterable): 4 | for index, val in enumerate(iterable): 5 | if val == value: 6 | return index 7 | return -1 8 | 9 | assert linear_search(5, number_list) == 5 -------------------------------------------------------------------------------- /11_Linear_search_and_binary_search/11_lower_bound.py: -------------------------------------------------------------------------------- 1 | def lower_bound(array, first, last, value): # 返回[first, last)内第一个不小于value的值的位置 2 | while first < last: # 搜索区间[first, last)不为空 3 | mid = first + (last - first) // 2 # 防溢出 4 | if array[mid] < value: # 要找第一个不小于value的值,所以此处应该为小于符号 5 | first = mid + 1 6 | else: 7 | last = mid 8 | return first # last也行,因为[first, last)为空的时候它们重合 9 | 10 | 11 | def test_binary_search(): 12 | a = list(range(10)) 13 | 14 | assert lower_bound(a, 0, 10, 3) == 3 15 | assert lower_bound(a, 0, 10, 10) == 10 16 | assert lower_bound(a, 0, 10, 9) == 9 17 | assert lower_bound(a, 0, 10, 0) == 0 18 | 19 | assert lower_bound(a, 0, 10, 9) == 9 20 | -------------------------------------------------------------------------------- /12_Basic_sort/12_basic_sort.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def bubble_sort(seq): 5 | n = len(seq) 6 | for i in range(n - 1): 7 | for j in range(n - 1 - i): # 需要减去i是因为每一轮冒泡最大的元素都会冒泡到最后,无需再比较 8 | if seq[j] > seq[j + 1]: 9 | seq[j], seq[j + 1] = seq[j + 1], seq[j] 10 | 11 | 12 | def test_bubble_sort(): 13 | seq = list(range(10)) 14 | random.shuffle(seq) 15 | bubble_sort(seq) 16 | assert seq == sorted(seq) 17 | 18 | 19 | def selection_sort(seq): 20 | n = len(seq) 21 | for i in range(n - 1): 22 | min_index = i # 假设当前下标的元素是最小的 23 | for j in range(i + 1, n): # 从i之后开始找到最小的元素,一直找到最后一个元素 24 | if seq[j] < seq[min_index]: 25 | min_index = j # 循环结束后得到最小元素的下标 26 | if min_index != i: # 如果最小的元素不是当前下标的元素,则交换位置 27 | seq[i], seq[min_index] = seq[min_index], seq[i] 28 | 29 | 30 | def test_selection_sort(): 31 | seq = list(range(10)) 32 | random.shuffle(seq) 33 | selection_sort(seq) 34 | assert seq == sorted(seq) 35 | 36 | 37 | def insertion_sort(seq): 38 | n = len(seq) 39 | for i in range(1, n): # 从第二个元素开始遍历 40 | value = seq[i] # 取出当前位置元素的值 41 | pos = i # 找到这个值的合适位置,使得前边的数组有序 [0,i] 有序 42 | while pos > 0 and value < seq[pos - 1]: # 比较取出值与前面的元素值,如果取出值跟小,继续前移 43 | seq[pos] = seq[pos - 1] # 将比较过的值往后移 44 | pos -= 1 # 继续往前比较(前移取出值的位置) 45 | seq[pos] = value # 将取出的值插入到当前位置 46 | 47 | 48 | def test_insertion_sort(): 49 | seq = list(range(10)) 50 | random.shuffle(seq) 51 | insertion_sort(seq) 52 | assert seq == sorted(seq) 53 | -------------------------------------------------------------------------------- /12_Basic_sort/12_shell_sort.py: -------------------------------------------------------------------------------- 1 | def shell_sort(seq): 2 | n = len(seq) 3 | group = 2 # 每次将序列分成n/2^i个子序列(i从1开始递增) 4 | gap = n // group # 子序列数量,同时也是下标增量值 5 | while gap > 0: # 增量值最终递减到1 6 | # 对每个子序列进行插入排序,共进行gap轮 7 | for i in range(gap): 8 | # 对当前序列进行插入排序,从当前序列的第二个元素即i+gap开始比较(初始元素下标为i) 9 | for j in range(i + gap, n, gap): # 以gap为step获取遍历序列 10 | value = seq[j] 11 | pos = j 12 | 13 | while pos >= gap and value < seq[pos - gap]: # pos-gap>=0,因此pos>=gap 14 | seq[pos] = seq[pos - gap] 15 | pos -= gap 16 | 17 | seq[pos] = value 18 | gap = gap // group 19 | 20 | 21 | def test_insertion_sort(): 22 | import random 23 | seq = list(range(10)) 24 | random.shuffle(seq) 25 | shell_sort(seq) 26 | assert seq == sorted(seq) 27 | -------------------------------------------------------------------------------- /13_Advanced_sort/13_merge_sort.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def merge_sort(seq): 5 | n = len(seq) 6 | if n <= 1: 7 | return seq 8 | else: 9 | mid = n // 2 10 | # 递归的对左右部分进行分割排序 11 | left_part = merge_sort(seq[:mid]) 12 | right_part = merge_sort(seq[mid:]) 13 | 14 | # 合并排序后的左右部分 15 | new_seq = merge_two_part(left_part, right_part) 16 | return new_seq 17 | 18 | 19 | def merge_two_part(sorted_a, sorted_b): 20 | length_a = len(sorted_a) 21 | length_b = len(sorted_b) 22 | a = b = 0 23 | new_seq = list() 24 | 25 | while a < length_a and b < length_b: 26 | if sorted_a[a] < sorted_b[b]: 27 | new_seq.append(sorted_a[a]) 28 | a += 1 29 | else: 30 | new_seq.append(sorted_b[b]) 31 | b += 1 32 | 33 | if a < length_a: 34 | new_seq.extend(sorted_a[a:]) 35 | else: 36 | new_seq.extend(sorted_b[b:]) 37 | 38 | return new_seq 39 | 40 | 41 | def test_merge_sort(): 42 | seq = list(range(10)) 43 | random.shuffle(seq) 44 | new_seq = merge_sort(seq) 45 | assert new_seq == sorted(seq) 46 | -------------------------------------------------------------------------------- /13_Advanced_sort/13_quick_sort.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | 4 | def quicksort(array): 5 | if len(array) < 2: # 递归出口,空数组或者只有一个元素的数组都是有序的 6 | return array 7 | else: 8 | pivot = array[0] 9 | less_part = [i for i in array[1:] if i < pivot] 10 | great_part = [i for i in array[1:] if i >= pivot] 11 | return quicksort(less_part) + [pivot] + quicksort(great_part) 12 | 13 | 14 | def test_quicksort(): 15 | seq = list(range(10)) 16 | random.shuffle(seq) 17 | sorted_seq = sorted(seq) 18 | quicksorted_seq = quicksort(seq) 19 | assert sorted_seq == quicksorted_seq 20 | 21 | 22 | def quicksort_inplace(array, beg, end): # 注意这里我们都用左闭右开区间,end 传入 len(array) 23 | # If the length of the list is less than or equal to one, it is already sorted. 24 | # If it is greater, then it can be partitioned and recursively sorted. 25 | if beg < end - 1: # 当序列元素小于或等于1时,序列已经是有序的,不需要再继续进行递归的快排操作(原地进行) 26 | pivot = partition(array, beg, end) 27 | quicksort_inplace(array, beg, pivot) 28 | quicksort_inplace(array, pivot + 1, end) 29 | 30 | 31 | def partition(array, beg, end): 32 | pivot_index = beg 33 | pivot = array[pivot_index] 34 | left = pivot_index + 1 35 | right = end - 1 # 开区间,最后一个元素位置是 end-1 [0, end-1] or [0: end),括号表示开区间 36 | 37 | while True: 38 | # 从左边找到比 pivot 大的 39 | while left <= right and array[left] < pivot: 40 | left += 1 41 | # 从右边找到比 pivot 小的, 注意停止循环的条件 42 | while right >= left and array[right] >= pivot: 43 | right -= 1 44 | 45 | if left > right: 46 | break 47 | else: 48 | array[left], array[right] = array[right], array[left] 49 | 50 | array[pivot_index], array[right] = array[right], array[pivot_index] 51 | return right # 新的 pivot 位置 52 | 53 | 54 | def partition_desc(array, beg, end): 55 | pivot_index = beg 56 | pivot = array[pivot_index] 57 | left = pivot_index + 1 58 | right = end - 1 # 开区间,最后一个元素位置是 end-1 [0, end-1] or [0: end),括号表示开区间 59 | 60 | while True: 61 | # 从左边找到比 pivot 大的 62 | while left <= right and array[left] >= pivot: 63 | left += 1 64 | # 从右边找到比 pivot 小的 65 | while right >= left and array[right] < pivot: 66 | right -= 1 67 | 68 | if left > right: 69 | break 70 | else: 71 | array[left], array[right] = array[right], array[left] 72 | 73 | array[pivot_index], array[right] = array[right], array[pivot_index] 74 | return right # 新的 pivot 位置 75 | 76 | 77 | def findkth(array, beg, end, k): 78 | index = partition_desc(array, beg, end) 79 | if index == k - 1: 80 | return array[index] 81 | elif index < k - 1: 82 | return findkth(array, index + 1, end, k) 83 | else: 84 | return findkth(array, beg, index, k) 85 | 86 | 87 | def test_partition(): 88 | l = [4, 1, 2, 8] 89 | assert partition(l, 0, len(l)) == 2 90 | l = [1, 2, 3, 4] 91 | assert partition(l, 0, len(l)) == 0 92 | l = [4, 3, 2, 1] 93 | assert partition(l, 0, len(l)) 94 | 95 | 96 | def test_quicksort_inplace(): 97 | seq = list(range(10)) 98 | random.shuffle(seq) 99 | sorted_seq = sorted(seq) 100 | quicksort_inplace(seq, 0, len(seq)) 101 | assert sorted_seq == seq 102 | 103 | assert findkth(seq, 0, len(seq), 3) == 7 104 | -------------------------------------------------------------------------------- /14_Tree_and_binary_tree/14_BinTree.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Queue(object): 5 | def __init__(self): 6 | self._items = deque() 7 | 8 | def append(self, value): 9 | return self._items.append(value) 10 | 11 | def pop(self): 12 | return self._items.popleft() 13 | 14 | def empty(self): 15 | return len(self._items) == 0 16 | 17 | 18 | class Stack(object): 19 | def __init__(self): 20 | self._items = deque() 21 | 22 | def push(self, value): 23 | return self._items.append(value) 24 | 25 | def pop(self): 26 | return self._items.pop() 27 | 28 | def empty(self): 29 | return len(self._items) == 0 30 | 31 | 32 | node_list = [ 33 | {'data': 'A', 'left': 'B', 'right': 'C', 'is_root': True}, 34 | {'data': 'B', 'left': 'D', 'right': 'E', 'is_root': False}, 35 | {'data': 'D', 'left': None, 'right': None, 'is_root': False}, 36 | {'data': 'E', 'left': 'H', 'right': None, 'is_root': False}, 37 | {'data': 'H', 'left': None, 'right': None, 'is_root': False}, 38 | {'data': 'C', 'left': 'F', 'right': 'G', 'is_root': False}, 39 | {'data': 'F', 'left': None, 'right': None, 'is_root': False}, 40 | {'data': 'G', 'left': 'I', 'right': 'J', 'is_root': False}, 41 | {'data': 'I', 'left': None, 'right': None, 'is_root': False}, 42 | {'data': 'J', 'left': None, 'right': None, 'is_root': False}, 43 | ] 44 | 45 | 46 | class BinTreeNode(object): 47 | def __init__(self, data, left=None, right=None): 48 | self.data, self.left, self.right = data, left, right 49 | 50 | 51 | class BinTree(object): 52 | def __init__(self, root=None): 53 | self.root = root 54 | 55 | @classmethod 56 | def build_from(cls, node_list): 57 | """通过节点信息构造二叉树 58 | 第一次遍历使用data数据分别构造 node 节点并存入字典,node.left和node.right仍然为None 59 | 第二次遍历从字典中取出node节点并给 root 和 孩子赋值(用node赋值) 60 | 最后用 root 节点初始化这个类并返回一个对象 61 | 62 | :param node_list: {'data': 'A', 'left': None, 'right': None, 'is_root': False} 63 | """ 64 | node_dict = {} 65 | for node_data in node_list: 66 | data = node_data['data'] 67 | node_dict[data] = BinTreeNode(data) 68 | for node_data in node_list: 69 | data = node_data['data'] 70 | node = node_dict[data] 71 | node.left = node_dict.get(node_data['left']) 72 | node.right = node_dict.get(node_data['right']) 73 | if node_data['is_root']: 74 | root = node 75 | return cls(root) 76 | 77 | def preorder_trav(self, node): 78 | """先序遍历""" 79 | if node is not None: 80 | print(node.data) # 递归函数里先处理根 81 | self.preorder_trav(node.left) # 递归处理左子树 82 | self.preorder_trav(node.right) # 递归处理右子树 83 | 84 | def inorder_trav(self, node): 85 | """中序遍历""" 86 | if node is not None: 87 | self.preorder_trav(node.left) 88 | print(node.data) 89 | self.preorder_trav(node.right) 90 | 91 | def postorder_trav(self, node): 92 | """后序遍历""" 93 | if node is not None: 94 | self.preorder_trav(node.left) 95 | self.preorder_trav(node.right) 96 | print(node.data) 97 | 98 | def preorder_trav_use_stack(self, node): 99 | s = Stack() 100 | while node or not s.empty(): # 循环重复整个过程 101 | while node: # 不断往左子树遍历 102 | print(node.data) # 先序处理节点 103 | s.push(node) # 第一次遇到节点 104 | node = node.left 105 | if s.empty() is not None: # 左子树为空时回到上一节点往右子树遍历 106 | node = s.pop() # 回到上一个节点的方式,第二次遇到节点 107 | node = node.right # 从上一个节点的右边开始遍历 108 | 109 | def inorder_trav_use_stack(self, node): 110 | s = Stack() 111 | while node or not s.empty(): 112 | while node: 113 | print(node.data) 114 | s.push(node) 115 | node = node.left 116 | if s.empty() is not None: 117 | node = s.pop() 118 | print(node.data) # 中序处理节点 119 | node = node.right 120 | 121 | def postorder_trav_use_stack(self, node): 122 | pass 123 | 124 | def layer_trav_use_queue(self, node): 125 | """使用队列进行层序遍历""" 126 | q = Queue() 127 | q.append(node) 128 | while not q.empty(): 129 | curnode = q.pop() 130 | print(curnode.data) 131 | if curnode.left: 132 | q.append(curnode.left) 133 | if curnode.right: 134 | q.append(curnode.right) 135 | 136 | def layer_trav_use_list(self, node): 137 | """ 138 | 使用两个列表进行层序遍历,一个列表存放当前层节点,一个列表存放下一层节点 139 | :param node: 140 | :return: 141 | """ 142 | curnodes = [node] 143 | next_nodes = [] 144 | while curnodes or next_nodes: 145 | for item in curnodes: 146 | print(item.data) 147 | if item.left: 148 | next_nodes.append(item.left) 149 | if item.right: 150 | next_nodes.append(item.right) 151 | curnodes = next_nodes 152 | next_nodes = [] 153 | 154 | def reverse(self, node): 155 | """ 156 | 和遍历操作类似,递归进行交换 157 | :param node: 158 | :return: 159 | """ 160 | if node is not None: 161 | node.left, node.right = node.right, node.left # 交换在函数中的位置前后都可以 162 | self.reverse(node.left) 163 | self.reverse(node.right) 164 | 165 | 166 | btree = BinTree.build_from(node_list) 167 | print('====先序遍历=====') 168 | btree.preorder_trav(btree.root) 169 | 170 | print('====使用 stack 实现先序遍历=====') 171 | btree.preorder_trav_use_stack(btree.root) 172 | 173 | print('====用堆栈层序遍历=====') 174 | btree.layer_trav_use_queue(btree.root) 175 | print('====用列表层序遍历=====') 176 | btree.layer_trav_use_list(btree.root) 177 | 178 | btree.reverse(btree.root) 179 | print('====反转之后的结果=====') 180 | btree.layer_trav_use_queue(btree.root) 181 | -------------------------------------------------------------------------------- /15_Heap_and_heapsort/15_heapsort.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): # 关键属性:分配空间和存储单位(使用列表的单个元素作为一个存储单位) 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class MaxHeap(object): 25 | def __init__(self, maxsize=None): 26 | self.maxsize = maxsize 27 | self._elements = Array(maxsize) 28 | self._count = 0 29 | 30 | def __len__(self): 31 | return self._count 32 | 33 | def add(self, value): 34 | if self._count >= self.maxsize: 35 | raise Exception('full') 36 | self._elements[self._count] = value 37 | 38 | self._count += 1 39 | self._siftup(self._count - 1) # 维持堆的特性 40 | 41 | def _siftup(self, ndx): 42 | if ndx > 0: 43 | parent = (ndx - 1) // 2 44 | if self._elements[ndx] > self._elements[parent]: # 如果插入的值大于 parent,一直交换 45 | self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx] 46 | self._siftup(parent) 47 | 48 | def extract(self): 49 | if self._count <= 0: 50 | raise Exception('empty') 51 | value = self._elements[0] # 保存 root 值 52 | self._count -= 1 53 | self._elements[0] = self._elements[self._count] # 最右下的节点放到root后siftDown 54 | self._siftdown(0) # 维持堆特性 55 | return value 56 | 57 | def _siftdown(self, ndx): 58 | left = ndx * 2 + 1 59 | right = ndx * 2 + 2 60 | # 找出当前节点及左右子节点中的最大值,与当前节点交换位置,并递归地对换下去的节点执行siftdown操作 61 | largest = ndx 62 | if left < self._count and self._elements[left] > self._elements[largest] and right < self._count and \ 63 | self._elements[left] >= self._elements[right]: 64 | largest = left 65 | elif left < self._count and self._elements[left] > self._elements[largest] and right >= self._count: 66 | largest = left 67 | elif right < self._count and self._elements[right] > self._elements[largest]: 68 | largest = right 69 | if largest != ndx: 70 | self._elements[ndx], self._elements[largest] = self._elements[largest], self._elements[ndx] 71 | self._siftdown(largest) 72 | 73 | 74 | class MinHeap(object): 75 | def __init__(self, maxsize=None): 76 | self.maxsize = maxsize 77 | self._elements = Array(maxsize) 78 | self._count = 0 79 | 80 | def __len(self): 81 | return self._count 82 | 83 | def add(self, value): 84 | if self._count >= self.maxsize: 85 | raise Exception('full') 86 | self._elements[self._count] = value 87 | self._count += 1 88 | self._siftup(self._count - 1) 89 | 90 | def _siftup(self, ndx): 91 | if ndx > 0: 92 | parent = (ndx - 1) // 2 93 | if self._elements[ndx] < self._elements[parent]: 94 | self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx] 95 | self._siftup(parent) 96 | 97 | def extract(self): 98 | if self._count <= 0: 99 | raise Exception('empty') 100 | value = self._elements[0] 101 | self._count -= 1 102 | self._elements[0] = self._elements[self._count] 103 | self._siftdown(0) 104 | return value 105 | 106 | def _siftdown(self, ndx): 107 | left = (ndx * 2) + 1 108 | right = (ndx * 2) + 2 109 | # 找出当前节点及左右子节点中的最小值,与当前节点交换位置,并递归地对换下去的节点执行siftdown操作 110 | smallest = ndx 111 | if left < self._count and self._elements[left] < self._elements[smallest] and \ 112 | self._elements[left] <= self._elements[right]: 113 | smallest = left 114 | elif right < self._count and self._elements[right] < self._elements[smallest]: 115 | smallest = right 116 | if smallest != ndx: 117 | self._elements[ndx], self._elements[smallest] = self._elements[smallest], self._elements[ndx] 118 | self._siftdown(smallest) 119 | 120 | 121 | def heapsort_reverse(array): 122 | length = len(array) 123 | maxheap = MaxHeap(length) 124 | for i in array: 125 | maxheap.add(i) 126 | res = [] 127 | for i in range(length): 128 | res.append(maxheap.extract()) 129 | return res 130 | 131 | 132 | def heapsort(array): 133 | length = len(array) 134 | minheap = MinHeap(length) 135 | for i in array: 136 | minheap.add(i) 137 | res = [] 138 | for i in range(length): 139 | res.append(minheap.extract()) 140 | return res 141 | 142 | 143 | def heapsort_inplace(array): 144 | length = len(array) 145 | maxheap = MaxHeap(length) 146 | for i in array: 147 | maxheap.add(i) 148 | while maxheap._count > 1: 149 | maxheap._elements[0], maxheap._elements[maxheap._count - 1] = maxheap._elements[maxheap._count - 1], \ 150 | maxheap._elements[0] 151 | maxheap._count -= 1 152 | maxheap._siftdown(0) 153 | return list(maxheap._elements) 154 | 155 | 156 | def test_heapsort_reverse(): 157 | import random 158 | mylist = list(range(10)) 159 | random.shuffle(mylist) 160 | assert heapsort_reverse(mylist) == sorted(mylist, reverse=True) 161 | 162 | 163 | def test_heapsort(): 164 | import random 165 | mylist = list(range(10)) 166 | random.shuffle(mylist) 167 | assert heapsort(mylist) == sorted(mylist) 168 | 169 | 170 | def test_heapsort_inplace(): 171 | import random 172 | mylist = list(range(100)) 173 | random.shuffle(mylist) 174 | assert heapsort_inplace(mylist) == sorted(mylist) 175 | -------------------------------------------------------------------------------- /15_Heap_and_heapsort/15_max_heap.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): # 关键属性:分配空间和存储单位(使用列表的单个元素作为一个存储单位) 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class MaxHeap(object): 25 | def __init__(self, maxsize=None): 26 | self.maxsize = maxsize 27 | self._elements = Array(maxsize) 28 | self._count = 0 29 | 30 | def __len__(self): 31 | return self._count 32 | 33 | def add(self, value): 34 | if self._count >= self.maxsize: 35 | raise Exception('full') 36 | self._elements[self._count] = value 37 | 38 | self._count += 1 39 | self._siftup(self._count - 1) # 维持堆的特性 40 | 41 | def _siftup(self, ndx): 42 | if ndx > 0: 43 | parent = (ndx - 1) // 2 44 | if self._elements[ndx] > self._elements[parent]: # 如果插入的值大于 parent,一直交换 45 | self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx] 46 | self._siftup(parent) 47 | 48 | def extract(self): 49 | if self._count <= 0: 50 | raise Exception('empty') 51 | value = self._elements[0] # 保存 root 值 52 | self._count -= 1 53 | self._elements[0] = self._elements[self._count] # 最右下的节点放到root后siftDown 54 | self._siftdown(0) # 维持堆特性 55 | return value 56 | 57 | def _siftdown(self, ndx): 58 | left = ndx * 2 + 1 59 | right = ndx * 2 + 2 60 | # 找出当前节点及左右子节点中的最大值,与当前节点交换位置,并递归地对换下去的节点执行siftdown操作 61 | largest = ndx 62 | if left < self._count and self._elements[left] > self._elements[largest] and right < self._count and \ 63 | self._elements[left] >= self._elements[right]: 64 | largest = left 65 | elif left < self._count and self._elements[left] > self._elements[largest] and right >= self._count: 66 | largest = left 67 | elif right < self._count and self._elements[right] > self._elements[largest]: 68 | largest = right 69 | if largest != ndx: 70 | self._elements[ndx], self._elements[largest] = self._elements[largest], self._elements[ndx] 71 | self._siftdown(largest) 72 | 73 | 74 | def test_maxheap(): 75 | import random 76 | n = 10 77 | h = MaxHeap(n) 78 | mylist = list(range(n)) 79 | random.shuffle(mylist) 80 | for i in mylist: 81 | h.add(i) 82 | for i in reversed(range(n)): 83 | assert i == h.extract() 84 | -------------------------------------------------------------------------------- /15_Heap_and_heapsort/15_min_heap.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): # 关键属性:分配空间和存储单位(使用列表的单个元素作为一个存储单位) 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class MinHeap(object): 25 | def __init__(self, maxsize=None): 26 | self.maxsize = maxsize 27 | self._elements = Array(maxsize) 28 | self._count = 0 29 | 30 | def __len(self): 31 | return self._count 32 | 33 | def add(self, value): 34 | if self._count >= self.maxsize: 35 | raise Exception('full') 36 | self._elements[self._count] = value 37 | self._count += 1 38 | self._siftup(self._count - 1) 39 | 40 | def _siftup(self, ndx): 41 | if ndx > 0: 42 | parent = (ndx - 1) // 2 43 | if self._elements[ndx] < self._elements[parent]: 44 | self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx] 45 | self._siftup(parent) 46 | 47 | def extract(self): 48 | if self._count <= 0: 49 | raise Exception('empty') 50 | value = self._elements[0] 51 | self._count -= 1 52 | self._elements[0] = self._elements[self._count] 53 | self._siftdown(0) 54 | return value 55 | 56 | def _siftdown(self, ndx): 57 | left = (ndx * 2) + 1 58 | right = (ndx * 2) + 2 59 | # 找出当前节点及左右子节点中的最小值,与当前节点交换位置,并递归地对换下去的节点执行siftdown操作 60 | smallest = ndx 61 | if left < self._count and self._elements[left] < self._elements[smallest] and right < self._count and \ 62 | self._elements[left] <= self._elements[right]: 63 | smallest = left 64 | elif left < self._count and self._elements[left] < self._elements[smallest] and right >= self._count: 65 | smallest = left 66 | elif right < self._count and self._elements[right] < self._elements[smallest]: 67 | smallest = right 68 | if smallest != ndx: 69 | self._elements[ndx], self._elements[smallest] = self._elements[smallest], self._elements[ndx] 70 | self._siftdown(smallest) 71 | 72 | 73 | def test_maxheap(): 74 | import random 75 | n = 10 76 | h = MinHeap(n) 77 | mylist = list(range(n)) 78 | random.shuffle(mylist) 79 | for i in mylist: 80 | h.add(i) 81 | for i in range(n): 82 | assert i == h.extract() 83 | -------------------------------------------------------------------------------- /15_Heap_and_heapsort/15_topk.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | 3 | 4 | class TopK(object): 5 | """获取大量元素 topk 大个元素,固定内存 6 | 思路: 7 | 1. 先放入元素前 k 个建立一个最小堆 8 | 2. 迭代剩余元素: 9 | 如果当前元素小于堆顶元素,跳过该元素(肯定不是前 k 大) 10 | 否则替换堆顶元素为当前元素,并重新调整堆 11 | """ 12 | 13 | def __init__(self, iterable, k): 14 | self.minheap = [] 15 | self.capacity = k 16 | self.iterable = iterable 17 | 18 | def push(self, value): 19 | if len(self.minheap) >= self.capacity: 20 | min_value = self.minheap[0] 21 | if value > min_value: 22 | heapq.heapreplace(self.minheap, value) # 返回并且pop堆顶最小值,推入新的 val 值并调整堆 23 | else: 24 | heapq.heappush(self.minheap, value) # 前面 k 个元素直接放入minheap 25 | 26 | def get_topk(self): 27 | for i in self.iterable: 28 | self.push(i) 29 | return self.minheap 30 | 31 | 32 | def test(): 33 | import random 34 | mylist = list(range(1000)) # 这里可以是一个可迭代元素,节省内存 35 | random.shuffle(mylist) 36 | _ = TopK(mylist, 10) 37 | print(_.get_topk()) 38 | 39 | 40 | if __name__ == '__main__': 41 | test() 42 | -------------------------------------------------------------------------------- /16_Priority_Queue/16_priority_queue.py: -------------------------------------------------------------------------------- 1 | class Array(object): 2 | def __init__(self, size=32): # 关键属性:分配空间和存储单位(使用列表的单个元素作为一个存储单位) 3 | self._size = size 4 | self._items = [None] * size 5 | 6 | def __getitem__(self, index): # Called to implement evaluation of self[index]实现下标访问. 7 | return self._items[index] 8 | 9 | def __setitem__(self, index, value): # Called to implement assignment to self[index]. 10 | self._items[index] = value 11 | 12 | def __len__(self): 13 | return self._size 14 | 15 | def clear(self, value=None): 16 | for i in range(len(self._items)): 17 | self._items[i] = value 18 | 19 | def __iter__(self): 20 | for item in self._items: 21 | yield item 22 | 23 | 24 | class MinHeap(object): 25 | def __init__(self, maxsize=None): 26 | self.maxsize = maxsize 27 | self._elements = Array(maxsize) 28 | self._count = 0 29 | 30 | def __len(self): 31 | return self._count 32 | 33 | def add(self, value): 34 | if self._count >= self.maxsize: 35 | raise Exception('full') 36 | self._elements[self._count] = value 37 | self._count += 1 38 | self._siftup(self._count - 1) 39 | 40 | def _siftup(self, ndx): 41 | if ndx > 0: 42 | parent = (ndx - 1) // 2 43 | if self._elements[ndx] < self._elements[parent]: 44 | self._elements[ndx], self._elements[parent] = self._elements[parent], self._elements[ndx] 45 | self._siftup(parent) 46 | 47 | def extract(self): 48 | if self._count <= 0: 49 | raise Exception('empty') 50 | value = self._elements[0] 51 | self._count -= 1 52 | self._elements[0] = self._elements[self._count] 53 | self._siftdown(0) 54 | return value 55 | 56 | def _siftdown(self, ndx): 57 | left = (ndx * 2) + 1 58 | right = (ndx * 2) + 2 59 | # 找出当前节点及左右子节点中的最小值,与当前节点交换位置,并递归地对换下去的节点执行siftdown操作 60 | smallest = ndx 61 | if left < self._count and self._elements[left] < self._elements[smallest] and right < self._count and \ 62 | self._elements[left] <= self._elements[right]: 63 | smallest = left 64 | elif left < self._count and self._elements[left] < self._elements[smallest] and right >= self._count: 65 | smallest = left 66 | elif right < self._count and self._elements[right] < self._elements[smallest]: 67 | smallest = right 68 | if smallest != ndx: 69 | self._elements[ndx], self._elements[smallest] = self._elements[smallest], self._elements[ndx] 70 | self._siftdown(smallest) 71 | 72 | 73 | class PriorityQueue(object): 74 | def __init__(self, maxsize): 75 | self.maxsize = maxsize 76 | self._minheap = MinHeap(maxsize) 77 | 78 | def push(self, priority, value): 79 | # 注意这里把这个 tuple push 进去,python 比较 tuple 从第一个开始比较 80 | # 这样就实现了按照优先级排序 81 | entry = (priority, value) 82 | self._minheap.add(entry) # 入队的时候会根据 priority 维持堆的特性 83 | 84 | def pop(self, with_priority=False): 85 | entry = self._minheap.extract() 86 | if with_priority: 87 | return entry 88 | else: 89 | return entry[1] 90 | 91 | def is_empty(self): 92 | return self._minheap._count == 0 93 | -------------------------------------------------------------------------------- /16_Priority_Queue/16_priority_queue_heapq.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | import itertools 3 | 4 | 5 | class PriorityQueue(object): 6 | REMOVED = '' # 被删除任务的占位字符 7 | 8 | def __init__(self): 9 | self.pq = [] # 初始化heapq所使用的list 10 | self.entry_finder = {} # 任务到优先级条目的映射 11 | self.counter = itertools.count() # 唯一计数器 12 | 13 | def add_task(self, task, priority=0): 14 | """添加一个新任务或者更新一个已存在任务的优先级""" 15 | if task in self.entry_finder: 16 | self.remove_task(task) 17 | count = next(self.counter) 18 | entry = [priority, count, task] 19 | self.entry_finder[task] = entry 20 | heapq.heappush(self.pq, entry) 21 | 22 | def remove_task(self, task): 23 | """将一个已存在的任务标记为REMOVED,若未找到Raise KeyError。""" 24 | entry = self.entry_finder.pop(task) 25 | entry[-1] = PriorityQueue.REMOVED 26 | 27 | def pop_task(self): 28 | """删除并返回最小优先级任务,如果队列已空Raise KeyError""" 29 | while self.pq: # 循环直到弹出值不为REMOVED的task才返回 30 | priority, count, task = heapq.heappop(self.pq) 31 | if task is not PriorityQueue.REMOVED: 32 | del self.entry_finder[task] 33 | return task 34 | raise KeyError('pop from an empty priority queue') 35 | 36 | def is_empty(self): 37 | return len(self.entry_finder) == 0 38 | -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_BFS.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Queue(object): 5 | def __init__(self): 6 | self._deque = deque() 7 | 8 | def __len__(self): 9 | return len(self._deque) 10 | 11 | def push(self, value): 12 | return self._deque.append(value) 13 | 14 | def pop(self): 15 | return self._deque.popleft() 16 | 17 | 18 | graph = { 19 | 'A': ['B', 'F'], 20 | 'B': ['C', 'I', 'G'], 21 | 'C': ['B', 'I', 'D'], 22 | 'D': ['C', 'I', 'G', 'H', 'E'], 23 | 'E': ['D', 'H', 'F'], 24 | 'F': ['A', 'G', 'E'], 25 | 'G': ['B', 'F', 'H', 'D'], 26 | 'H': ['G', 'D', 'E'], 27 | 'I': ['B', 'C', 'D'], 28 | } 29 | 30 | 31 | def BFS(graph, start): 32 | search_queue = Queue() 33 | searched = set() 34 | search_queue.push(start) 35 | while search_queue: 36 | cur_node = search_queue.pop() 37 | if cur_node not in searched: 38 | print(cur_node) # or yield cur_node 39 | searched.add(cur_node) 40 | for node in graph[cur_node]: 41 | search_queue.push(node) 42 | 43 | 44 | BFS(graph, 'A') 45 | -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_DFS.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | 4 | class Stack: 5 | def __init__(self): 6 | self._deque = deque() 7 | 8 | def push(self, value): 9 | return self._deque.append(value) 10 | 11 | def pop(self): 12 | return self._deque.pop() 13 | 14 | def __len__(self): 15 | return len(self._deque) 16 | 17 | def is_empty(self): 18 | return len(self._deque) == 0 19 | 20 | 21 | graph = { 22 | 'A': ['B', 'F'], 23 | 'B': ['C', 'I', 'G'], 24 | 'C': ['B', 'I', 'D'], 25 | 'D': ['C', 'I', 'G', 'H', 'E'], 26 | 'E': ['D', 'H', 'F'], 27 | 'F': ['A', 'G', 'E'], 28 | 'G': ['B', 'F', 'H', 'D'], 29 | 'H': ['G', 'D', 'E'], 30 | 'I': ['B', 'C', 'D'], 31 | } 32 | 33 | DFS_searched = set() 34 | 35 | 36 | def DFS_recursive(graph, start): 37 | if start not in DFS_searched: 38 | print(start) 39 | DFS_searched.add(start) 40 | for node in graph[start]: 41 | if node not in DFS_searched: 42 | DFS_recursive(graph, node) 43 | 44 | 45 | print('dfs recursive:') 46 | DFS_recursive(graph, 'A') 47 | 48 | 49 | def DFS(graph, start): 50 | s = Stack() 51 | s.push(start) 52 | searched = set() 53 | while not s.is_empty(): 54 | cur_node = s.pop() 55 | if cur_node not in searched: 56 | print(cur_node) 57 | searched.add(cur_node) 58 | for node in reversed(graph[cur_node]): # 注意入栈出栈的顺序 59 | s.push(node) 60 | 61 | 62 | print('dfs use stack:') 63 | DFS(graph, 'A') 64 | -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_Dijkstra's_bad.py: -------------------------------------------------------------------------------- 1 | """ 2 | 本解法只适用于有向无环图 3 | 仅用于理解算法思想,时间复杂度和空间复杂度都很辣鸡 4 | """ 5 | 6 | graph = { 7 | 'A': {'B': 6, 'C': 2}, 8 | 'B': {'D': 1}, 9 | 'C': {'B': 3, 'D': 5}, 10 | 'D': {}, 11 | } 12 | 13 | costs = { 14 | 'B': 6, 15 | 'C': 2, 16 | 'D': float('inf') 17 | } 18 | 19 | parents = { 20 | 'B': 'A', 21 | 'C': 'A', 22 | 'D': None 23 | } 24 | 25 | processed = set() 26 | 27 | 28 | def find_lowest_cost_node(costs): 29 | """ 30 | 遍历所有节点,找出未处理过的节点中开销最小的节点 31 | :param costs: 32 | :return: 33 | """ 34 | lowest_cost = float('inf') 35 | lowest_cost_node = None 36 | for node in costs: # 遍历所有节点 37 | cost = costs[node] 38 | if cost < lowest_cost and node not in processed: # 比较开销,找出最小开销节点 39 | lowest_cost = cost 40 | lowest_cost_node = node 41 | return lowest_cost_node 42 | 43 | node = find_lowest_cost_node(costs) # 在未处理的节点中找到开销最小的节点 44 | while node is not None: 45 | cost = costs[node] 46 | neighbors = graph[node] 47 | for n in neighbors.keys(): # 遍历当前节点的所有邻居 48 | new_cost = cost + neighbors[n] 49 | if new_cost < costs[n]: # 如果经当前节点前往该邻居更近 50 | costs[n] = new_cost # 更新该邻居的开销 51 | parents[n] = node # 同时更新该邻居的父亲节点为当前节点 52 | processed.add(node) # 将当前节点标记为处理过 53 | node = find_lowest_cost_node(costs) # 找出接下来要处理的节点,并循环 54 | 55 | print(parents.items()) 56 | -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_Dijkstra_dict.py: -------------------------------------------------------------------------------- 1 | graph = { 2 | 'B': {'A': 5, 'D': 1, 'G': 2}, 3 | 'A': {'B': 5, 'D': 3, 'E': 12, 'F' :5}, 4 | 'D': {'B': 1, 'G': 1, 'E': 1, 'A': 3}, 5 | 'G': {'B': 2, 'D': 1, 'C': 2}, 6 | 'C': {'G': 2, 'E': 1, 'F': 16}, 7 | 'E': {'A': 12, 'D': 1, 'C': 1, 'F': 2}, 8 | 'F': {'A': 5, 'E': 2, 'C': 16}} 9 | 10 | def dijkstra(graph, start): 11 | unvisted = {node:float('inf') for node in graph} 12 | visited = {} 13 | current = start 14 | currentDistance = 0 15 | unvisted[current] = currentDistance 16 | 17 | while True: 18 | for neighbor, weight in graph[current].items(): 19 | if neighbor not in unvisted: 20 | continue 21 | newDistance = currentDistance + weight 22 | if newDistance < unvisted[neighbor]: 23 | unvisted[neighbor] = newDistance 24 | # 将当前节点及确定的最短距离加入已确定最短距离集合,并从原集合中删除 25 | visited[current] = currentDistance 26 | del unvisted[current] 27 | if not unvisted: 28 | break 29 | # 若未遍历的节点集合未空,从中选出当前距离最小的节点 30 | candidates = [node for node in unvisted.items()] 31 | current, currentDistance = sorted(candidates, key=lambda x: x[1])[0] 32 | return visited 33 | 34 | visited = dijkstra(graph, 'A') 35 | print(visited) -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_Dijkstra_pq.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | import itertools 3 | 4 | 5 | class PriorityQueue(object): 6 | REMOVED = '' # 被删除任务的占位字符 7 | 8 | def __init__(self): 9 | self.pq = [] # 初始化heapq所使用的list 10 | self.entry_finder = {} # 任务到优先级条目的映射 11 | self.counter = itertools.count() # 唯一计数器 12 | 13 | def add_task(self, task, priority=0): 14 | """添加一个新任务或者更新一个已存在任务的优先级""" 15 | if task in self.entry_finder: 16 | self.remove_task(task) 17 | count = next(self.counter) 18 | entry = [priority, count, task] 19 | self.entry_finder[task] = entry 20 | heapq.heappush(self.pq, entry) 21 | 22 | def remove_task(self, task): 23 | """将一个已存在的任务标记为REMOVED,若未找到Raise KeyError。""" 24 | entry = self.entry_finder.pop(task) 25 | entry[-1] = PriorityQueue.REMOVED 26 | 27 | def pop_task(self): 28 | """删除并返回最小优先级任务,如果队列已空Raise KeyError""" 29 | while self.pq: # 循环直到弹出值不为REMOVED的task才返回 30 | priority, count, task = heapq.heappop(self.pq) 31 | if task is not PriorityQueue.REMOVED: 32 | del self.entry_finder[task] 33 | return task 34 | raise KeyError('pop from an empty priority queue') 35 | 36 | def is_empty(self): 37 | return len(self.entry_finder) == 0 38 | 39 | 40 | class Vertex: 41 | def __init__(self, key, distance=float('inf')): 42 | """ 43 | 在类的构造方法中,直接初始化id(key字符串)以及邻接字典。 44 | """ 45 | self.id = key 46 | self.connectedTo = {} 47 | self.distance = distance 48 | self.predecessor = None 49 | 50 | def addNeighbor(self, nbr, weight=0): 51 | """ 52 | 添加邻接顶点,将邻接顶点对象以及相连边的权重作为参数传入 53 | """ 54 | self.connectedTo[nbr] = weight 55 | 56 | def __str__(self): 57 | return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo]) 58 | 59 | def __lt__(self, other): 60 | pass 61 | 62 | def getConnections(self): 63 | """ 64 | 返回顶点的所有邻接顶点(的key),注意此返回结果为生成器 65 | """ 66 | return self.connectedTo.keys() 67 | 68 | def getId(self): 69 | return self.id 70 | 71 | def getWeight(self, nbr): 72 | """ 73 | 通过邻接顶点对象在邻接字典中获取权重值 74 | """ 75 | return self.connectedTo[nbr] 76 | 77 | def getDistance(self): 78 | return self.distance 79 | 80 | def setDistance(self, value): 81 | self.distance = value 82 | 83 | def getPred(self): 84 | return self.predecessor 85 | 86 | def setPred(self, value): 87 | self.predecessor = value 88 | 89 | 90 | class Graph: 91 | def __init__(self): 92 | """ 93 | 在构造方法中初始化字典以及表示顶点个数的属性。 94 | """ 95 | self.vertList = {} 96 | self.numVertics = 0 97 | 98 | def addVertex(self, key): 99 | """ 100 | 构造并添加顶点到图中 101 | """ 102 | self.numVertics += 1 103 | newVertex = Vertex(key) 104 | self.vertList[key] = newVertex 105 | return newVertex 106 | 107 | def getVertex(self, key): 108 | """ 109 | 通过顶点key获取顶点对象,不存在返回None 110 | """ 111 | if key in self.vertList: 112 | return self.vertList[key] 113 | else: 114 | return None 115 | 116 | def __contains__(self, key): 117 | return key in self.vertList 118 | 119 | def addEdge(self, start, end, wight=0): 120 | """ 121 | 添加从start顶点到end顶点的边并设置权重,若顶点在图中不存在则创建顶点并加入图中 122 | """ 123 | if start not in self.vertList: 124 | nv = self.addVertex(start) 125 | if end not in self.vertList: 126 | nv = self.addVertex(end) 127 | self.vertList[start].addNeighbor(self.vertList[end], wight) 128 | 129 | def getVertices(self): 130 | return self.vertList.keys() 131 | 132 | def __iter__(self): 133 | return iter(self.vertList.values()) 134 | 135 | 136 | def dijkstra(graph, start): 137 | # 用图中的节点构建优先级队列 138 | pq = PriorityQueue() 139 | start.setDistance(0) 140 | pq_list = [(v, v.getDistance()) for v in graph] 141 | for v, priority in pq_list: 142 | pq.add_task(v, priority) 143 | # 从队列中取出路径长度最小的顶点,更新其邻居节点的距离 144 | while not pq.is_empty(): 145 | currentVert = pq.pop_task() 146 | for nextVert in currentVert.getConnections(): 147 | newDist = currentVert.getDistance() + currentVert.getWeight(nextVert) 148 | if newDist < nextVert.getDistance(): 149 | nextVert.setDistance(newDist) 150 | nextVert.setPred(currentVert) 151 | pq.add_task(nextVert, newDist) # 通过此方法更新队列中任务的优先级 152 | 153 | 154 | def create_graph(): 155 | g = Graph() 156 | for i in range(6): 157 | g.addVertex(i) 158 | 159 | g.addEdge(0, 1, 5) 160 | g.addEdge(0, 5, 2) 161 | g.addEdge(1, 2, 4) 162 | g.addEdge(2, 3, 9) 163 | g.addEdge(3, 4, 7) 164 | g.addEdge(3, 5, 3) 165 | g.addEdge(4, 0, 1) 166 | g.addEdge(5, 4, 8) 167 | g.addEdge(5, 2, 1) 168 | return g 169 | 170 | 171 | graph = create_graph() 172 | start = graph.getVertex(1) 173 | dijkstra(graph, start) 174 | for v in graph: 175 | print("( %s , %s )" % (v.getId(), v.getPred())) 176 | -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_Prim’s_Spanning_Tree.py: -------------------------------------------------------------------------------- 1 | import heapq 2 | import itertools 3 | 4 | 5 | class PriorityQueue(object): 6 | REMOVED = '' # 被删除任务的占位字符 7 | 8 | def __init__(self): 9 | self.pq = [] # 初始化heapq所使用的list 10 | self.entry_finder = {} # 任务到优先级条目的映射,用来快速的在队列中找到任务对应优先级条目 11 | self.counter = itertools.count() # 唯一计数器 12 | 13 | def add_task(self, task, priority=0): 14 | """添加一个新任务或者更新一个已存在任务的优先级""" 15 | if task in self.entry_finder: 16 | self.remove_task(task) 17 | count = next(self.counter) 18 | entry = [priority, count, task] 19 | self.entry_finder[task] = entry 20 | heapq.heappush(self.pq, entry) 21 | 22 | def remove_task(self, task): 23 | """将一个已存在的任务标记为REMOVED,若未找到Raise KeyError。""" 24 | entry = self.entry_finder.pop(task) 25 | entry[-1] = PriorityQueue.REMOVED 26 | 27 | def pop_task(self): 28 | """删除并返回最小优先级任务,如果队列已空Raise KeyError""" 29 | while self.pq: # 循环直到弹出值不为REMOVED的task才返回 30 | priority, count, task = heapq.heappop(self.pq) 31 | if task is not PriorityQueue.REMOVED: 32 | del self.entry_finder[task] 33 | return task 34 | raise KeyError('pop from an empty priority queue') 35 | 36 | def is_empty(self): 37 | return len(self.entry_finder) == 0 38 | 39 | def __contains__(self, item): 40 | return item in self.entry_finder 41 | 42 | 43 | class Vertex: 44 | def __init__(self, key, distance=float('inf')): 45 | """ 46 | 在类的构造方法中,直接初始化id(key字符串)以及邻接字典。 47 | """ 48 | self.id = key 49 | self.connectedTo = {} 50 | self.distance = distance 51 | self.predecessor = None 52 | 53 | def addNeighbor(self, nbr, weight=0): 54 | """ 55 | 添加邻接顶点,将邻接顶点对象以及相连边的权重作为参数传入 56 | """ 57 | self.connectedTo[nbr] = weight 58 | 59 | def __str__(self): 60 | return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo]) 61 | 62 | def __lt__(self, other): 63 | pass 64 | 65 | def getConnections(self): 66 | """ 67 | 返回顶点的所有邻接顶点(的key),注意此返回结果为生成器 68 | """ 69 | return self.connectedTo.keys() 70 | 71 | def getId(self): 72 | return self.id 73 | 74 | def getWeight(self, nbr): 75 | """ 76 | 通过邻接顶点对象在邻接字典中获取权重值 77 | """ 78 | return self.connectedTo[nbr] 79 | 80 | def getDistance(self): 81 | return self.distance 82 | 83 | def setDistance(self, value): 84 | self.distance = value 85 | 86 | def getPred(self): 87 | return self.predecessor 88 | 89 | def setPred(self, value): 90 | self.predecessor = value 91 | 92 | 93 | class Graph: 94 | def __init__(self): 95 | """ 96 | 在构造方法中初始化字典以及表示顶点个数的属性。 97 | """ 98 | self.vertList = {} 99 | self.numVertics = 0 100 | 101 | def addVertex(self, key): 102 | """ 103 | 构造并添加顶点到图中 104 | """ 105 | self.numVertics += 1 106 | newVertex = Vertex(key) 107 | self.vertList[key] = newVertex 108 | return newVertex 109 | 110 | def getVertex(self, key): 111 | """ 112 | 通过顶点key获取顶点对象,不存在返回None 113 | """ 114 | if key in self.vertList: 115 | return self.vertList[key] 116 | else: 117 | return None 118 | 119 | def __contains__(self, key): 120 | return key in self.vertList 121 | 122 | def addEdge(self, start, end, wight=0): 123 | """ 124 | 添加从start顶点到end顶点的边并设置权重,若顶点在图中不存在则创建顶点并加入图中 125 | """ 126 | if start not in self.vertList: 127 | nv = self.addVertex(start) 128 | if end not in self.vertList: 129 | nv = self.addVertex(end) 130 | self.vertList[start].addNeighbor(self.vertList[end], wight) 131 | 132 | def getVertices(self): 133 | return self.vertList.keys() 134 | 135 | def __iter__(self): 136 | return iter(self.vertList.values()) 137 | 138 | 139 | def prim(graph, start): 140 | # 构造优先级队列 141 | pq = PriorityQueue() 142 | start.setDistance(0) 143 | pq_list = [(v, v.getDistance()) for v in graph] 144 | for v, priority in pq_list: 145 | pq.add_task(v, priority) 146 | # 从队列中取出当前距离最小的顶点,更新其邻居节点的距离 147 | while not pq.is_empty(): 148 | currentVert = pq.pop_task() 149 | for nextVert in currentVert.getConnections(): 150 | newDist = currentVert.getWeight(nextVert) 151 | if nextVert in pq and newDist < nextVert.getDistance(): 152 | # 更新队列中节点的距离和父节点 153 | nextVert.setDistance(newDist) 154 | nextVert.setPred(currentVert) 155 | pq.add_task(nextVert, newDist) 156 | 157 | def create_graph(): 158 | g = Graph() 159 | for i in range(6): 160 | g.addVertex(i) 161 | 162 | g.addEdge(0, 1, 5) 163 | g.addEdge(0, 5, 2) 164 | g.addEdge(1, 2, 4) 165 | g.addEdge(2, 3, 9) 166 | g.addEdge(3, 4, 7) 167 | g.addEdge(3, 5, 3) 168 | g.addEdge(4, 0, 1) 169 | g.addEdge(5, 4, 8) 170 | g.addEdge(5, 2, 1) 171 | return g 172 | 173 | 174 | graph = create_graph() 175 | start = graph.getVertex(1) 176 | prim(graph, start) 177 | for v in graph: 178 | print("( %s , %s )" % (v.getId(), v.getPred())) -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_graph_adt.py: -------------------------------------------------------------------------------- 1 | class Vertex: 2 | def __init__(self, key): 3 | """在类的构造方法中,直接初始化id(key字符串)以及邻接字典。""" 4 | self.id = key 5 | self.connectedTo = {} 6 | 7 | def addNeighbor(self, nbr, weight=0): 8 | """添加邻接顶点,将邻接顶点对象以及相连边的权重作为参数传入""" 9 | self.connectedTo[nbr] = weight 10 | 11 | def __str__(self): 12 | return str(self.id) + ' connectedTo: ' + str([x.id for x in self.connectedTo]) 13 | 14 | def getConnections(self): 15 | """返回顶点的所有邻接顶点(的key),注意此返回结果为生成器""" 16 | return self.connectedTo.keys() 17 | 18 | def getId(self): 19 | return self.id 20 | 21 | def getWight(self, nbr): 22 | """通过邻接顶点对象在邻接字典中获取权重值""" 23 | return self.connectedTo[nbr] 24 | 25 | 26 | class Graph: 27 | def __init__(self): 28 | """在构造方法中初始化字典以及表示顶点个数的属性。""" 29 | self.vertList = {} 30 | self.numVertics = 0 31 | 32 | def addVertex(self, key): 33 | """构造并添加顶点到图中""" 34 | self.numVertics += 1 35 | newVertex = Vertex(key) 36 | self.vertList[key] = newVertex 37 | return newVertex 38 | 39 | def getVertex(self, key): 40 | """通过顶点key获取顶点对象,不存在返回None""" 41 | if key in self.vertList: 42 | return self.vertList[key] 43 | else: 44 | return None 45 | 46 | def __contains__(self, key): 47 | return key in self.vertList 48 | 49 | def addEdge(self, start, end, wight=0): 50 | """添加从start顶点到end顶点的边并设置权重,若顶点在图中不存在则创建顶点并加入图中""" 51 | if start not in self.vertList: 52 | nv = self.addVertex(start) 53 | if end not in self.vertList: 54 | nv = self.addVertex(end) 55 | self.vertList[start].addNeighbor(self.vertList[end], wight) 56 | 57 | def getVertices(self): 58 | return self.vertList.keys() 59 | 60 | def __iter__(self): 61 | return iter(self.vertList.values()) 62 | 63 | 64 | def test_graph(): 65 | g = Graph() 66 | for i in range(6): 67 | g.addVertex(i) 68 | 69 | assert len(g.vertList) == 6 70 | g.addEdge(0, 1, 5) 71 | g.addEdge(0, 5, 2) 72 | g.addEdge(1, 2, 4) 73 | g.addEdge(2, 3, 9) 74 | g.addEdge(3, 4, 7) 75 | g.addEdge(3, 5, 3) 76 | g.addEdge(4, 0, 1) 77 | g.addEdge(5, 4, 8) 78 | g.addEdge(5, 2, 1) 79 | 80 | for v in g: 81 | for w in v.getConnections(): 82 | print("( %s , %s )" % (v.getId(), w.getId())) 83 | -------------------------------------------------------------------------------- /18_Graph_and_Graph_Algorithms/18_topological_sorting.py: -------------------------------------------------------------------------------- 1 | # Python program to print topological sorting of a DAG 2 | from collections import defaultdict 3 | 4 | 5 | # Class to represent a graph 6 | class Graph: 7 | def __init__(self, vertices): 8 | self.graph = defaultdict(list) # dictionary containing adjacency List 9 | self.V = vertices # No. of vertices 10 | 11 | # function to add an edge to graph 12 | def addEdge(self, u, v): 13 | self.graph[u].append(v) 14 | 15 | # A recursive function used by topologicalSort 16 | def topologicalSortUtil(self, v, visited, stack): 17 | 18 | # Mark the current node as visited. 19 | visited[v] = True 20 | 21 | # Recur for all the vertices adjacent to this vertex 22 | for i in self.graph[v]: 23 | if visited[i] == False: 24 | self.topologicalSortUtil(i, visited, stack) 25 | 26 | # Push current vertex to stack which stores result 27 | stack.insert(0, v) 28 | 29 | # The function to do Topological Sort. It uses recursive 30 | # topologicalSortUtil() 31 | def topologicalSort(self): 32 | # Mark all the vertices as not visited 33 | visited = [False] * self.V 34 | stack = [] 35 | 36 | # Call the recursive helper function to store Topological 37 | # Sort starting from all vertices one by one 38 | for i in range(self.V): 39 | if visited[i] == False: 40 | self.topologicalSortUtil(i, visited, stack) 41 | 42 | # Print contents of the stack 43 | print(stack) 44 | 45 | 46 | g = Graph(6) 47 | g.addEdge(5, 2) 48 | g.addEdge(5, 0) 49 | g.addEdge(4, 0) 50 | g.addEdge(4, 1) 51 | g.addEdge(2, 3) 52 | g.addEdge(3, 1) 53 | 54 | print("Following is a Topological Sort of the given graph") 55 | g.topologicalSort() 56 | # This code is contributed by Neelam Yadav 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Xie Wei 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.tuna.tsinghua.edu.cn/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | pylint = "*" 8 | 9 | [packages] 10 | pytest = "*" 11 | 12 | [requires] 13 | python_version = "3.6" 14 | -------------------------------------------------------------------------------- /Reference/Grokking Algorithms/binary_search.py: -------------------------------------------------------------------------------- 1 | def binary_search(mylist, item): 2 | low = 0 3 | high = len(mylist) - 1 4 | 5 | while low <= high: 6 | mid = (low + high) // 2 7 | guess = mylist[mid] 8 | if guess == item: 9 | return mid 10 | elif guess < item: 11 | low = mid + 1 12 | else: 13 | high = mid - 1 14 | return None 15 | 16 | 17 | my_list = [1, 3, 5, 14, 25, 36, 46, 64, 100] 18 | 19 | print(binary_search(my_list, 46)) 20 | print(binary_search(my_list, 3)) 21 | print(binary_search(my_list, 25)) 22 | -------------------------------------------------------------------------------- /Reference/Grokking Algorithms/breadth-first_search.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | 3 | graph = { 4 | 'kaka': ['waynerv'] 5 | } 6 | 7 | def check_method(person): 8 | return person[-1] == 'v' 9 | 10 | def search(name): 11 | search_queue = deque() 12 | search_queue += graph[name] 13 | searched = [] 14 | while search_queue: 15 | person = search_queue.popleft() 16 | if person not in searched: 17 | if check_method(person): 18 | print(f'{person} pass the check.') 19 | return True 20 | else: 21 | search_queue += graph[person] 22 | searched.append(person) 23 | return False 24 | 25 | search('kaka') -------------------------------------------------------------------------------- /Reference/Grokking Algorithms/quicksort.py: -------------------------------------------------------------------------------- 1 | def quicksort(mylist): 2 | if len(mylist) < 2: 3 | return mylist 4 | else: 5 | pivot = mylist[0] 6 | less = [i for i in mylist[1:] if i <= pivot] 7 | greater = [i for i in mylist[1:] if i > pivot] 8 | return quicksort(less) + [pivot] + quicksort(greater) 9 | 10 | 11 | print(quicksort([2, 5, 6, 4, 23, 4545, 343, 655, 343, 42, 754])) 12 | -------------------------------------------------------------------------------- /Reference/Grokking Algorithms/slectionSort.py: -------------------------------------------------------------------------------- 1 | def findbiggest(arr): 2 | biggest = arr[0] 3 | biggest_index = 0 4 | for i in range(1, len(arr)): 5 | if arr[i] > biggest: 6 | biggest = arr[i] 7 | biggest_index = i 8 | return biggest_index 9 | 10 | 11 | def selectsort(arr): 12 | newarr = [] 13 | for i in range(len(arr)): 14 | big = findbiggest(arr) 15 | newarr.append(arr.pop(big)) 16 | 17 | return newarr 18 | 19 | 20 | mylist = [1, 3, 5, 14, 25, 36, 46, 64, 100] 21 | print(selectsort(mylist)) 22 | -------------------------------------------------------------------------------- /Reference/Problem Solving with Algorithms and Data Structures/1.1 Newton_iterate.py: -------------------------------------------------------------------------------- 1 | def sqrt(x): 2 | y = x 3 | while abs(y*y - x) >= 1e-10: 4 | y = (y + x/y) / 2 5 | return y 6 | 7 | print(sqrt(10)) -------------------------------------------------------------------------------- /Reference/Problem Solving with Algorithms and Data Structures/Maximum Subsequence Sum.py: -------------------------------------------------------------------------------- 1 | K = int(input()) 2 | number_list = list(map(int, input().split())) 3 | max_sum = 0 4 | this_sum = 0 5 | start = number_list[0] 6 | end = number_list[-1] 7 | temp_first_number = number_list[0] 8 | count = 0 9 | for i in range(K): 10 | if this_sum > 0: 11 | count += 1 12 | this_sum += number_list[i] 13 | if this_sum > max_sum: 14 | max_sum = this_sum 15 | end = number_list[i] 16 | start = number_list[i - count] 17 | elif this_sum <= 0: 18 | this_sum = 0 19 | count = 0 20 | print(max_sum, start, end) 21 | -------------------------------------------------------------------------------- /Reference/Problem Solving with Algorithms and Data Structures/gcd.py: -------------------------------------------------------------------------------- 1 | def gcd(m,n): 2 | # m & n are int. 3 | while m % n != 0: 4 | m, n = n, m % n 5 | return n 6 | 7 | 8 | class Fraction(): 9 | def __init__(self, top, bottom): 10 | self.num = top 11 | self.den = bottom 12 | 13 | def __str__(self): 14 | return str(self.num)+'/'+str(self.den) 15 | 16 | def show(self): 17 | print(self.num, '/', self.den) 18 | 19 | def __add__(self, other): 20 | newnum = self.num * other.den + self.den * other.num 21 | newden = self.den * other.den 22 | common = gcd(newnum, newden) 23 | return Fraction(newnum//common, newden//common) 24 | 25 | def __eq__(self, other): 26 | return self.num * other.den == self.den * other.num 27 | 28 | x = Fraction(1,2) 29 | y = Fraction(2,3) 30 | print(x+y) 31 | print(x == y) -------------------------------------------------------------------------------- /Reference/Problem Solving with Algorithms and Data Structures/最大子列和.py: -------------------------------------------------------------------------------- 1 | K = int(input()) 2 | number_list = list(map(int, input().split())) 3 | max_sum = 0 4 | this_sum = 0 5 | for i in number_list: 6 | this_sum += i 7 | if this_sum > max_sum: 8 | max_sum = this_sum 9 | elif this_sum < 0: 10 | this_sum = 0 11 | print(max_sum) 12 | -------------------------------------------------------------------------------- /Reference/progs/assoc.py: -------------------------------------------------------------------------------- 1 | """ 字典的基本关联类 2 | """ 3 | 4 | 5 | class Assoc: 6 | def __init__(self, key, value): 7 | self.key = key 8 | self.value = value 9 | 10 | def __lt__(self, other): 11 | return self.key < other.key 12 | 13 | def __le__(self, other): 14 | return self.key < other.key or self.key == other.key 15 | 16 | def __str__(self): 17 | return "Assoc({0},{1})".format(self.key, self.value) 18 | 19 | if __name__ == '__main__': 20 | a1 = Assoc(1, 2) 21 | print(str(a1)) 22 | pass 23 | -------------------------------------------------------------------------------- /Reference/progs/avltree.py: -------------------------------------------------------------------------------- 1 | """ AVL 树实现 2 | """ 3 | 4 | from bintree import BinTNode # , print_BiTNodes 5 | # from stack_list import * 6 | from assoc import Assoc 7 | from dict_bitree import DictBinTree 8 | 9 | 10 | class AVLNode(BinTNode): 11 | def __init__(self, data): 12 | BinTNode.__init__(self, data) 13 | self.bf = 0 14 | 15 | 16 | class DictAVL(DictBinTree): 17 | def __init__(self): 18 | DictBinTree.__init__(self) 19 | 20 | @staticmethod 21 | def LL(a, b): 22 | a.left = b.right 23 | b.right = a 24 | a.bf = b.bf = 0 25 | return b 26 | 27 | @staticmethod 28 | def RR(a, b): 29 | a.right = b.left 30 | b.left = a 31 | a.bf = b.bf = 0 32 | return b 33 | 34 | @staticmethod 35 | def LR(a, b): 36 | c = b.right 37 | a.left, b.right = c.right, c.left 38 | c.left, c.right = b, a 39 | if c.bf == 0: # c 本身就是插入结点 40 | a.bf = b.bf = 0 41 | elif c.bf == 1: # 新结点在 c 左子树 42 | a.bf = -1 43 | b.bf = 0 44 | else: # 新结点在 c 右子树 45 | a.bf = 0 46 | b.bf = 1 47 | c.bf = 0 48 | return c 49 | 50 | @staticmethod 51 | def RL(a, b): 52 | c = b.left 53 | a.right, b.left = c.left, c.right 54 | c.left, c.right = a, b 55 | if c.bf == 0: # c 本身就是插入结点 56 | a.bf = 0 57 | b.bf = 0 58 | elif c.bf == 1: # 新结点在 c 的左子树 59 | a.bf = 0 60 | b.bf = -1 61 | else: # 新结点在 c 的右子树 62 | a.bf = 1 63 | b.bf = 0 64 | c.bf = 0 65 | return c 66 | 67 | def insert(self, key, value): 68 | a = p = self._root 69 | if a is None: 70 | self._root = AVLNode(Assoc(key, value)) 71 | return 72 | pa = q = None # 维持 pa, q 为 a, p 的父结点 73 | while p: # 确定插入位置及最小非平衡子树 74 | if key == p.data.key: # key存在,修改关联值 75 | p.data.value = value 76 | return 77 | if p.bf != 0: 78 | pa, a = q, p # 已知最小非平衡子树 79 | q = p 80 | if key < p.data.key: 81 | p = p.left 82 | else: 83 | p = p.right 84 | # q 是插入点的父结点,parent,a 记录最小非平衡子树 85 | node = AVLNode(Assoc(key, value)) 86 | if key < q.data.key: 87 | q.left = node # 作为左子结点 88 | else: 89 | q.right = node # 或右子结点 90 | # 新结点已插入,a 是最小不平衡子树 91 | if key < a.data.key: # 新结点在 a 的左子树 92 | p = b = a.left 93 | d = 1 94 | else: # 新结点在 a 的右子树 95 | p = b = a.right 96 | d = -1 # d记录新结点在a哪棵子树 97 | # 修改 b 到新结点路上各结点的BF值,b 为 a 的子结点 98 | while p != node: # node 一定存在,不用判断 p 空 99 | if key < p.data.key: # p 的左子树增高 100 | p.bf = 1 101 | p = p.left 102 | else: # p的右子树增高 103 | p.bf = -1 104 | p = p.right 105 | if a.bf == 0: # a原BF为0,不会失衡 106 | a.bf = d 107 | return 108 | if a.bf == -d: # 新结点在较低子树里 109 | a.bf = 0 110 | return 111 | # 新结点在较高子树,失衡,必须调整 112 | if d == 1: # 新结点在 a 的左子树 113 | if b.bf == 1: 114 | b = DictAVL.LL(a, b) # LL 调整 115 | else: 116 | b = DictAVL.LR(a, b) # LR 调整 117 | else: # 新结点在 a 的右子树 118 | if b.bf == -1: 119 | b = DictAVL.RR(a, b) # RR 调整 120 | else: 121 | b = DictAVL.RL(a, b) # RL 调整 122 | 123 | if pa is None: 124 | self._root = b # 原 a 为树根 125 | else: 126 | if pa.left == a: 127 | pa.left = b 128 | else: 129 | pa.right = b 130 | 131 | def delete(self, key): 132 | return 133 | 134 | def print(self): 135 | for entry in self.entries(): 136 | print(entry.key, entry.value) 137 | 138 | def depth(self): 139 | def depth0(t): 140 | if t is None: 141 | return 0 142 | return max(depth0(t.left), 143 | depth0(t.right)) + 1 144 | return depth0(self._root) 145 | 146 | # END class 147 | 148 | 149 | def build_AVLTree(entries): 150 | dic = DictAVL() 151 | for k, v in entries: 152 | dic.insert(k, v) 153 | return dic 154 | 155 | 156 | def build_random_AVL(n): 157 | dic = DictAVL() 158 | for i in range(n): 159 | dic.insert(randint(1, n*2), randint(1, 100)) 160 | return dic 161 | 162 | 163 | if __name__ == '__main__': 164 | from random import randint 165 | 166 | ## dic1 = build_random_AVL(20) 167 | ## for entry in dic1.inorder(): 168 | ## print(entry.key, entry.value) 169 | 170 | dic2 = DictAVL() 171 | for i in range(15): # 15 entries with increasing keys 172 | dic2.insert(i, i*i) 173 | print(dic2.depth()) 174 | for entry in dic2.entries(): 175 | print(entry.key, entry.value) 176 | 177 | ## dic3 = build_random_AVL(1000) 178 | ## print(dic3.depth()) 179 | ## 180 | ## dic4 = DictAVL() 181 | ## for i in range(1000): # 1000 entries with increasing keys 182 | ## dic4.insert(i, i*i) 183 | ## print(dic4.depth()) 184 | 185 | 186 | pass 187 | -------------------------------------------------------------------------------- /Reference/progs/bintree.py: -------------------------------------------------------------------------------- 1 | """ 二叉树结点类和二叉树相关函数,二叉树类等 2 | """ 3 | 4 | 5 | class BinTreeNodeError(ValueError): 6 | pass 7 | 8 | 9 | class BinTNode: 10 | def __init__(self, dat, left=None, right=None): 11 | self.data = dat 12 | self.left = left 13 | self.right = right 14 | # end of class 15 | 16 | 17 | def count_BinTNodes(t): 18 | if t is None: 19 | return 0 20 | else: 21 | return 1 + count_BinTNodes(t.left) + count_BinTNodes(t.right) 22 | 23 | 24 | def sum_BinTNodes(t): 25 | if t is None: 26 | return 0 27 | else: 28 | return t.dat + sum_BinTNodes(t.left) + sum_BinTNodes(t.right) 29 | 30 | 31 | def preorder(t, proc): 32 | if t is None: 33 | return 34 | assert(isinstance(t, BinTNode)) 35 | proc(t.data) 36 | preorder(t.left, proc) 37 | preorder(t.right, proc) 38 | 39 | 40 | def inorder(t, proc): 41 | if t is None: 42 | return 43 | inorder(t.left, proc) 44 | proc(t.data) 45 | inorder(t.right, proc) 46 | 47 | 48 | def postorder(t, proc): 49 | if t is None: 50 | return 51 | postorder(t.left, proc) 52 | postorder(t.right, proc) 53 | proc(t.data) 54 | 55 | from queue_list import * 56 | 57 | 58 | def levelorder(t, proc): 59 | q = SQueue() 60 | q.enqueue(t) 61 | while not q.is_empty(): 62 | t = q.dequeue() 63 | if t is None: 64 | continue 65 | q.enqueue(t.left) 66 | q.enqueue(t.right) 67 | proc(t.data) 68 | 69 | 70 | from stack_list import * 71 | 72 | 73 | def preorder_nonrec(t, proc): 74 | s = SStack() 75 | while t or not s.is_empty(): 76 | while t: # go down along left chain 77 | s.push(t.right) # push right branch into stack 78 | proc(t.data) 79 | t = t.left 80 | t = s.pop() # left chain ends, backtrack 81 | 82 | 83 | # def preorder_nonrec(t, proc): 84 | # s = SStack() 85 | # while t: 86 | # while t: # go down along left chain 87 | # if t.right: 88 | # s.push(t.right) # push non-empty right branch into stack 89 | # proc(t.data) 90 | # t = t.left 91 | # if s.is_empty(): 92 | # break 93 | # t = s.pop() # left chain ends, backtrack 94 | 95 | 96 | def inorder_nonrec(t, proc): 97 | s = SStack() 98 | while t or not s.is_empty(): 99 | while t: 100 | s.push(t) 101 | t = t.left 102 | t = s.pop() 103 | proc(t.data) 104 | t = t.right 105 | 106 | 107 | def postorder_nonrec(t, proc): 108 | s = SStack() 109 | while t or not s.is_empty(): 110 | while t: # iterate until top has no child 111 | s.push(t) 112 | t = t.left if t.left else t.right 113 | # if we can go left, go, otherwise, go right 114 | t = s.pop() # get the node to be access 115 | proc(t.data) 116 | if not s.is_empty() and s.top().left == t: 117 | t = s.top().right # end of left visit, turn right 118 | else: 119 | t = None # end of right visit, force to backtrack 120 | 121 | def preorder_elements(t): 122 | s = SStack() 123 | while t or not s.is_empty(): 124 | while t: 125 | s.push(t.right) 126 | yield t.data 127 | t = t.left 128 | t = s.pop() 129 | 130 | 131 | def print_BinTNodes(t): 132 | if t is None: 133 | print("^", end="") 134 | return 135 | print("(" + str(t.data), end="") 136 | print_BinTNodes(t.left) 137 | print_BinTNodes(t.right) 138 | print(")", end="") 139 | 140 | 141 | class BinTree: 142 | def __init__(self): 143 | self._root = None 144 | 145 | def is_empty(self): 146 | return self._root is None 147 | 148 | def set_root(self, rootnode): 149 | self._root = rootnode 150 | 151 | def set_left(self, leftchild): 152 | self._root.left = leftchild 153 | 154 | def set_right(self, rightchild): 155 | self._root.right = rightchild 156 | 157 | def root(self): 158 | return self._root 159 | 160 | def leftchild(self): 161 | return self._root.left 162 | 163 | def rightchild(self): 164 | return self._root.right 165 | 166 | def preorder_elements(self): 167 | t, s = self._root, SStack() 168 | while t or not s.is_empty(): 169 | while t: 170 | s.push(t.right) 171 | yield t.data 172 | t = t.left 173 | t = s.pop() 174 | 175 | 176 | if __name__ == '__main__': 177 | t1 = BinTNode(1, BinTNode(2, BinTNode(3), BinTNode(4)), BinTNode(5)) 178 | 179 | print_BinTNodes(t1) 180 | print() 181 | 182 | preorder(t1, lambda x: print(x, end=" ")) 183 | print() 184 | 185 | preorder_nonrec(t1, lambda x: print(x, end=" ")) 186 | print() 187 | 188 | inorder(t1, lambda x: print(x, end=" ")) 189 | print() 190 | 191 | 192 | inorder_nonrec(t1, lambda x: print(x, end=" ")) 193 | print() 194 | 195 | postorder(t1, lambda x: print(x, end=" ")) 196 | print() 197 | 198 | 199 | postorder_nonrec(t1, lambda x: print(x, end=" ")) 200 | print() 201 | 202 | for x in preorder_elements(t1): 203 | print(x) 204 | -------------------------------------------------------------------------------- /Reference/progs/bintree0.py: -------------------------------------------------------------------------------- 1 | """ 用嵌套的 Python list 实现二叉树,用这种二叉树实现算术表达式和计算 2 | """ 3 | 4 | 5 | def BinTree(data, left=None, right=None): 6 | return [data, left, right] 7 | 8 | 9 | def is_empty_BinTree(btree): 10 | return btree is None 11 | 12 | 13 | def root(btree): 14 | return btree[0] 15 | 16 | 17 | def left(btree): 18 | return btree[1] 19 | 20 | 21 | def right(btree): 22 | return btree[2] 23 | 24 | 25 | def set_root(btree, data): 26 | btree[0] = data 27 | 28 | 29 | def set_left(btree, left): 30 | btree[1] = left 31 | 32 | 33 | def set_right(btree, right): 34 | btree[2] = right 35 | 36 | ############################################### 37 | #### Functions for ############################ 38 | #### building and manipulating ################ 39 | #### mathematical expressions ################ 40 | 41 | 42 | def make_sum(a, b): 43 | return ('+', a, b) 44 | 45 | 46 | def make_prod(a, b): 47 | return ('*', a, b) 48 | 49 | 50 | def make_diff(a, b): 51 | return ('-', a, b) 52 | 53 | 54 | def make_div(a, b): 55 | return ('/', a, b) 56 | 57 | 58 | def is_basic_exp(a): 59 | return not isinstance(a, tuple) 60 | 61 | 62 | def is_compose_exp(a): 63 | return isinstance(a, tuple) 64 | 65 | 66 | def eval_exp(e): 67 | if is_basic_exp(e): 68 | return e 69 | op, a, b = e[0], eval_exp(e[1]), eval_exp(e[2]) 70 | if op == '+': 71 | return eval_sum(a, b) 72 | elif op == '-': 73 | return eval_diff(a, b) 74 | elif op == '*': 75 | return eval_prod(a, b) 76 | elif op == '/': 77 | return eval_div(a, b) 78 | else: 79 | raise ValueError("Unknown operator:", op) 80 | 81 | 82 | def is_number(x): 83 | return (isinstance(x, int) or isinstance(x, float) or 84 | isinstance(x, complex)) 85 | 86 | 87 | def eval_sum(a, b): 88 | if is_number(a) and is_number(b): 89 | return a + b 90 | if is_number(a) and a == 0: 91 | return b 92 | if is_number(b) and b == 0: 93 | return a 94 | return make_sum(a, b) 95 | 96 | 97 | def eval_diff(a, b): 98 | if is_number(a) and is_number(b): 99 | return a - b 100 | if is_number(a) and a == 0: 101 | return -b 102 | if is_number(b) and b == 0: 103 | return a 104 | return make_diff(a, b) 105 | 106 | 107 | def eval_prod(a, b): 108 | if is_number(a) and is_number(b): 109 | return a * b 110 | if is_number(a) and a == 0 or is_number(b) and b == 0: 111 | return 0 112 | if is_number(a) and a == 1: 113 | return b 114 | if is_number(b) and b == 1: 115 | return a 116 | return make_prod(a, b) 117 | 118 | 119 | def eval_div(a, b): 120 | if is_number(a) and is_number(b): 121 | return a / b 122 | if is_number(a) and a == 0: 123 | return 0 124 | if is_number(b) and b == 0: 125 | raise ZeroDivisionError 126 | if is_number(b) and b == 1: 127 | return a 128 | return make_div(a, b) 129 | 130 | 131 | if __name__ == '__main__': 132 | ## t1 = btree(2, btree(4, [], []), btree(8, [], [])) 133 | ## print(t1) 134 | ## set_leftch(leftch(t1), btree(5, [], [])) 135 | ## print(t1) 136 | 137 | e1 = make_prod(make_sum(2, 3), make_diff(4, 5)) 138 | e2 = make_prod(make_diff(make_prod(2, 'a'), 3), make_diff(4, 5)) 139 | e3 = make_div(make_sum(make_prod(2, 7), make_div(0, 'b')), make_div('a', 1)) 140 | 141 | # eval_exp(['$', 2, 3]) # This will cause an exception because $ is not a valid operator 142 | -------------------------------------------------------------------------------- /Reference/progs/bintree_huffman.py: -------------------------------------------------------------------------------- 1 | """ 基于二叉树实现 Huffman 树 2 | """ 3 | 4 | from prioqueue import PrioQueue # , PrioQueueError 5 | from bintree import BinTNode, print_BinTNodes 6 | 7 | 8 | class HTNode(BinTNode): 9 | def __lt__(self, othernode): 10 | return self.data < othernode.data 11 | 12 | 13 | class HuffmanPrioQ(PrioQueue): 14 | def number(self): 15 | return len(self._elems) 16 | 17 | 18 | def HuffmanTree(weights): 19 | trees = HuffmanPrioQ() 20 | for w in weights: 21 | trees.enqueue(HTNode(w)) 22 | while trees.number() > 1: 23 | t1 = trees.dequeue() 24 | t2 = trees.dequeue() 25 | x = t1.data + t2.data 26 | trees.enqueue(HTNode(x, t1, t2)) 27 | return trees.dequeue() 28 | 29 | 30 | if __name__ == '__main__': 31 | 32 | t = BinTNode(1, BinTNode(2), BinTNode(3)) 33 | print_BinTNodes(t) 34 | print("\n") 35 | 36 | h = HuffmanTree([2, 3, 7, 10, 4, 2, 5]) 37 | print_BinTNodes(h) 38 | 39 | pass 40 | -------------------------------------------------------------------------------- /Reference/progs/dict-1.py: -------------------------------------------------------------------------------- 1 | """ 二分检索等函数 2 | """ 3 | 4 | from assoc import Assoc 5 | from random import randint 6 | 7 | # Suppose lst is a list of Assoc object, 8 | # where e.key and e.value give their key and value respectively 9 | 10 | 11 | def bisearch(lst, key): 12 | low, high = 0, len(lst)-1 13 | while low <= high: # There are elements in the interval 14 | mid = (low + high) // 2 15 | if key == lst[mid].key: 16 | return lst[mid].value 17 | if key < lst[mid].key: 18 | high = mid - 1 # continue in the lower half part 19 | else: 20 | low = mid + 1 # continue in the higher half part 21 | 22 | 23 | # A simple digit-str/general-str hash function 24 | def int_str_hash(sn): 25 | h = 0 26 | for c in sn: 27 | h = (h*10 + int(c)*31) % 65521 28 | return h 29 | 30 | 31 | class LSet: # A part of a simple set class 32 | def __init__(self, elems=[]): 33 | self._elems = [] 34 | for x in elems: 35 | if x not in self._elems: 36 | self._elems.append(x) 37 | 38 | def includes(self, e): 39 | return e in self._elems 40 | 41 | 42 | def str_hash(s): 43 | h1 = 0 44 | for c in s: 45 | h1 = h1 * 29 + ord(c) 46 | return h1 47 | 48 | 49 | if __name__ == '__main__': 50 | 51 | lst1 = [Assoc(randint(1, 30), i) for i in range(16)] 52 | lst1.sort() 53 | print(list(map(str, lst1))) 54 | for i in range(1, 30, 3): 55 | ind = bisearch(lst1, i) 56 | print("Search", i, "in the list and get:", ind) 57 | 58 | print("12345:",str_hash("12345")) 59 | print("asdfg:",str_hash("asdfg")) 60 | 61 | pass 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Reference/progs/dict_bitree.py: -------------------------------------------------------------------------------- 1 | """ 基于二叉树的字典实现 2 | """ 3 | 4 | from bintree import BinTNode # , print_BiTNodes 5 | from stack_list import * 6 | from assoc import Assoc 7 | from random import randint 8 | 9 | 10 | def bt_search(btree, key): 11 | bt = btree 12 | while bt: 13 | entry = bt.data 14 | if key < entry.key: 15 | bt = bt.left 16 | elif key > entry.key: 17 | bt = bt.right 18 | else: 19 | return entry.value 20 | return None 21 | 22 | 23 | class DictBinTree: 24 | def __init__(self): 25 | self._root = None 26 | 27 | def is_empty(self): 28 | return self._root is None 29 | 30 | def search(self, key): 31 | bt = self._root 32 | while bt: 33 | entry = bt.data 34 | if key < entry.key: 35 | bt = bt.left 36 | elif key > entry.key: 37 | bt = bt.right 38 | else: 39 | return entry.value 40 | return None 41 | 42 | def insert(self, key, value): 43 | bt = self._root 44 | if bt is None: 45 | self._root = BinTNode(Assoc(key, value)) 46 | return 47 | while True: 48 | entry = bt.data 49 | if key < entry.key: 50 | if bt.left is None: 51 | bt.left = BinTNode(Assoc(key, value)) 52 | return 53 | bt = bt.left 54 | elif key > entry.key: 55 | if bt.right is None: 56 | bt.right = BinTNode(Assoc(key, value)) 57 | return 58 | bt = bt.right 59 | else: 60 | bt.data.value = value 61 | return 62 | 63 | def values(self): 64 | t, s = self._root, SStack() 65 | while t or not s.is_empty(): 66 | while t: 67 | s.push(t) 68 | t = t.left 69 | t = s.pop() 70 | yield t.data.value 71 | t = t.right 72 | 73 | def entries(self): 74 | t, s = self._root, SStack() 75 | while t or not s.is_empty(): 76 | while t: 77 | s.push(t) 78 | t = t.left 79 | t = s.pop() 80 | yield t.data.key, t.data.value 81 | t = t.right 82 | 83 | def delete(self, key): 84 | p, q = None, self._root # keep p the parent of q 85 | while q and q.data.key != key: 86 | p = q 87 | if key < q.data.key: 88 | q = q.left 89 | else: 90 | q = q.right 91 | if q is None: 92 | return # key is not in the tree 93 | # Now q refers to key node, p is its parent or None 94 | if q.left is None: # q has no left child 95 | if p is None: 96 | self._root = q.right # q == self._root 97 | elif q is p.left: 98 | p.left = q.right 99 | else: 100 | p.right = q.right # here q == p.right 101 | return 102 | r = q.left 103 | while r.right: 104 | r = r.right 105 | r.right = q.right 106 | if p is None: 107 | self._root = q.left # q == self._root 108 | elif p.left is q: 109 | p.left = q.left 110 | else: 111 | p.right = q.left 112 | 113 | def print(self): 114 | for k, v in self.entries(): 115 | print(k, v) 116 | # END class 117 | 118 | 119 | def build_dictBinTree(entries): 120 | dic = DictBinTree() 121 | for k, v in entries: 122 | dic.insert(k, v) 123 | return dic 124 | 125 | 126 | if __name__ == '__main__': 127 | # from random import randint 128 | 129 | data = [(x, 1) for x in 130 | [26, 15, 18, 7, 30, 29, 3, 17, 10, 22, 34, 9]] 131 | dic1 = build_dictBinTree(data) 132 | 133 | for entry in dic1.entries(): 134 | print(entry) 135 | 136 | for i in range(20): 137 | n = randint(1, 31) 138 | print("try delete", n, end=", ") 139 | dic1.delete(n) 140 | print('') 141 | 142 | for entry in dic1.entries(): 143 | print(entry) 144 | 145 | pass 146 | 147 | -------------------------------------------------------------------------------- /Reference/progs/dict_list.py: -------------------------------------------------------------------------------- 1 | """ 基于 list 的字典实现 2 | """ 3 | 4 | from assoc import Assoc 5 | 6 | 7 | class DictList: 8 | def __init__(self): 9 | self._elems = [] 10 | 11 | def is_empty(self): 12 | return not self._elems 13 | 14 | def num(self): 15 | return len(self._elems) 16 | 17 | def search(self, key): 18 | for a in self._elems: 19 | if a.key == key: 20 | return a.value 21 | return None 22 | 23 | def insert(self, key, value): 24 | self._elems.append(Assoc(key, value)) 25 | 26 | def delete(self, key): 27 | for i in range(len(self._elems)): 28 | if self._elems[i].key == key: 29 | self._elems.pop(i) 30 | return 31 | 32 | def entries(self): 33 | for a in self._elems: 34 | yield a.key, a.value 35 | 36 | def values(self): 37 | for a in self._elems: 38 | yield a.value 39 | # end of class 40 | 41 | class DictOrdList(DictList): 42 | def insert(self, key, value): 43 | elems = self._elems 44 | low, high = 0, len(elems)-1 45 | while low <= high: # There are elements in the interval 46 | mid = (low + high) // 2 47 | if key == elems[mid].key: 48 | elems[mid].value = value 49 | if key < elems[mid].key: 50 | high = mid - 1 # continue in the lower half part 51 | else: 52 | low = mid + 1 # continue in the higher half part 53 | 54 | def search(self, key): 55 | elems = self._elems 56 | low, high = 0, len(elems)-1 57 | while low <= high: # There are elements in the interval 58 | mid = (low + high) // 2 59 | if key == elems[mid].key: 60 | return elems[mid].value 61 | if key < elems[mid].key: 62 | high = mid - 1 # continue in the lower half part 63 | else: 64 | low = mid + 1 # continue in the higher half part 65 | 66 | if __name__ != "main": 67 | from random import randint 68 | 69 | dic1 = DictList() 70 | 71 | for i in range(10): 72 | dic1.insert(randint(1, 50), randint(1, 100)) 73 | 74 | for k, v in dic1.entries(): 75 | print(k, v) 76 | 77 | print("-"*30) 78 | 79 | for i in range(20): 80 | key = randint(1, 50) 81 | r = dic1.search(key) 82 | if r: 83 | print(key, r) 84 | dic1.delete(key) 85 | 86 | print("-"*30) 87 | for e in dic1.entries(): 88 | print(e) 89 | -------------------------------------------------------------------------------- /Reference/progs/dict_optbitree.py: -------------------------------------------------------------------------------- 1 | """ 最佳二叉树的实现 2 | """ 3 | 4 | from bintree import BinTNode # , print_BiTNodes 5 | from assoc import Assoc 6 | from dict_bitree import DictBinTree 7 | 8 | inf = float("inf") 9 | 10 | 11 | class DictOptBinTree(DictBinTree): 12 | def __init__(self, seq): 13 | DictBinTree.__init__(self) 14 | data = sorted(seq) 15 | self._root = DictOptBinTree.buildOBT(data, 0, len(data)-1) 16 | 17 | @staticmethod 18 | def buildOBT(data, start, end): 19 | if start > end: 20 | return None 21 | mid = (end + start)//2 22 | left = DictOptBinTree.buildOBT(data, start, mid-1) 23 | right = DictOptBinTree.buildOBT(data, mid+1, end) 24 | return BinTNode(Assoc(*data[mid]), left, right) 25 | 26 | 27 | def build_opt_btree(wp, wq): 28 | """ Assume wp is a list of n values representing weights of 29 | internal nodes, wq is a list of n+1 values representing 30 | weights of n+1 external nodes. This function builds the 31 | optimal binary searching tree from wp and wq. 32 | """ 33 | num = len(wp)+1 34 | if len(wq) != num: 35 | raise ValueError("Arguments of build_opt_btree are wrong.") 36 | w = [[0]*num for j in range(num)] 37 | c = [[0]*num for j in range(num)] 38 | r = [[0]*num for j in range(num)] 39 | for i in range(num): # 计算所有的 w[i][j] 40 | w[i][i] = wq[i] 41 | for j in range(i+1, num): 42 | w[i][j] = w[i][j-1] + wp[j-1] + wq[j] 43 | for i in range(0, num-1): # Set trees with only one node 44 | c[i][i+1] = w[i][i+1] 45 | r[i][i+1] = i 46 | 47 | for m in range(2, num): 48 | # 算 m 个内部结点的最佳树(n–m+1棵) 49 | for i in range(0, num-m): 50 | k0, j = i, i+m 51 | wmin = inf 52 | for k in range(i, j): 53 | # 在[i,j)里找使C[i][k]+C[k+1][j]最小的k 54 | if c[i][k] + c[k+1][j] < wmin: 55 | wmin = c[i][k] + c[k+1][j] 56 | k0 = k 57 | c[i][j] = w[i][j] + wmin 58 | r[i][j] = k0 59 | 60 | return c, r 61 | 62 | if __name__ == '__main__': 63 | # from random import randint 64 | 65 | wp = [5, 1, 2] 66 | wq = [4, 3, 1, 1] 67 | 68 | trees = build_opt_btree(wp, wq) 69 | 70 | print(trees[0]) 71 | print(trees[1]) 72 | 73 | wp = [5, 1, 2, 6, 8, 10] 74 | wq = [4, 3, 3, 1, 6, 12, 9] 75 | 76 | trees = build_opt_btree(wp, wq) 77 | 78 | print(trees[0]) 79 | print(trees[1]) 80 | 81 | ## "Result:" 82 | ## [[0, 12, 23, 35, 66, 112, 167], 83 | ## [0, 0, 7, 16, 38, 80, 130], 84 | ## [0, 0, 0, 6, 24, 62, 112], 85 | ## [0, 0, 0, 0, 13, 46, 96], 86 | ## [0, 0, 0, 0, 0, 26, 71], 87 | ## [0, 0, 0, 0, 0, 0, 31], 88 | ## [0, 0, 0, 0, 0, 0, 0]] 89 | ## 90 | ## [[0, 0, 0, 0, 3, 3, 4], 91 | ## [0, 0, 1, 1, 3, 4, 4], 92 | ## [0, 0, 0, 2, 3, 4, 4], 93 | ## [0, 0, 0, 0, 3, 4, 4], 94 | ## [0, 0, 0, 0, 0, 4, 5], 95 | ## [0, 0, 0, 0, 0, 0, 5], 96 | ## [0, 0, 0, 0, 0, 0, 0]] 97 | -------------------------------------------------------------------------------- /Reference/progs/graph.py: -------------------------------------------------------------------------------- 1 | """ 基本的图实现:邻接矩阵实现,链接表实现 2 | """ 3 | 4 | inf = float("inf") # 表示无穷大 5 | 6 | 7 | class GraphError(TypeError): 8 | pass 9 | 10 | 11 | class Graph: # basic graph class, using adjacent matrix 12 | def __init__(self, mat, unconn=0): 13 | vnum = len(mat) 14 | for x in mat: 15 | if len(x) != vnum: # 检查是否为方阵 16 | raise ValueError("Argument for class 'Graph' is bad.") 17 | self._mat = [mat[i][:] for i in range(vnum)] # 做 mat 的拷贝 18 | self._unconn = unconn 19 | self._vnum = vnum 20 | 21 | def vertex_num(self): 22 | return self._vnum 23 | 24 | def _invalid(self, v): 25 | return 0 > v or v >= self._vnum 26 | 27 | def add_vertex(self): 28 | raise GraphError( 29 | "Adj-Matrix does not support 'add_vertex'.") 30 | 31 | def add_edge(self, vi, vj, val=1): 32 | if self._invalid(vi) or self._invalid(vj): 33 | raise GraphError(str(vi) + ' or ' + str(vj) + 34 | " is not a valid vertex.") 35 | self._mat[vi][vj] = val 36 | 37 | def get_edge(self, vi, vj): 38 | if self._invalid(vi) or self._invalid(vj): 39 | raise GraphError(str(vi) + ' or ' + str(vj) + 40 | " is not a valid vertex.") 41 | return self._mat[vi][vj] 42 | 43 | def out_edges(self, vi): 44 | if self._invalid(vi): 45 | raise GraphError(str(vi) + " is not a valid vertex.") 46 | return self._out_edges(self._mat[vi], self._unconn) 47 | 48 | @staticmethod 49 | def _out_edges(row, unconn): 50 | edges = [] 51 | for i in range(len(row)): 52 | if row[i] != unconn: 53 | edges.append((i, row[i])) 54 | return edges 55 | 56 | # @staticmethod 57 | # def _out_edges(row, unconn): 58 | # for i in range(len(row)): 59 | # if row[i] != unconn: 60 | # yield (i, row[i]) 61 | 62 | def __str__(self): 63 | return "[\n" + ",\n".join(map(str, self._mat)) + "\n]"\ 64 | + "\nUnconnected: " + str(self._unconn) 65 | 66 | 67 | class GraphAL(Graph): 68 | def __init__(self, mat=[], unconn=0): 69 | vnum = len(mat) 70 | for x in mat: 71 | if len(x) != vnum: # 检查是否方阵 72 | raise ValueError("Argument for 'GraphA' is bad.") 73 | self._mat = [Graph._out_edges(mat[i], unconn) 74 | for i in range(vnum)] 75 | self._vnum = vnum 76 | self._unconn = unconn 77 | 78 | def add_vertex(self): # 增加新顶点时安排一个新编号 79 | self._mat.append([]) 80 | self._vnum += 1 81 | return self._vnum - 1 82 | 83 | def add_edge(self, vi, vj, val=1): 84 | if self._vnum == 0: 85 | raise GraphError("Cannot add edge into empty graph.") 86 | if self._invalid(vi) or self._invalid(vj): 87 | raise GraphError(str(vi) + ' or ' + str(vj) + 88 | " is not a valid vertex.") 89 | row = self._mat[vi] 90 | i = 0 91 | while i < len(row): 92 | if row[i][0] == vj: # 修改 mat[vi][vj] 的值 93 | self._mat[vi][i] = (vj, val) 94 | return 95 | if row[i][0] > vj: # 原无到vj的边,退出循环在正确位置加入 96 | break 97 | i += 1 98 | self._mat[vi].insert(i, (vj, val)) 99 | 100 | def get_edge(self, vi, vj): 101 | if self._invalid(vi) or self._invalid(vj): 102 | raise GraphError(str(vi) + ' or ' + str(vj) + 103 | " is not a valid vertex.") 104 | for i, val in self._mat[vi]: 105 | if i == vj: 106 | return val 107 | return self._unconn 108 | 109 | def out_edges(self, vi): 110 | if self._invalid(vi): 111 | raise GraphError(str(vi) + " is not a valid vertex.") 112 | return self._mat[vi] 113 | 114 | 115 | gmat = [[0, 0, 3, 4], 116 | [2, 0, 0, 0], 117 | [4, 1, 0, 0], 118 | [2, 0, 1, 0]] 119 | 120 | gmat1 = [[0,1,1,0,0,0,0,0], 121 | [1,0,0,1,1,0,0,0], 122 | [1,0,0,0,0,1,1,0], 123 | [0,1,0,0,0,0,0,1], 124 | [0,1,0,0,0,0,0,1], 125 | [0,0,1,0,0,0,0,0], 126 | [0,0,1,0,0,0,0,0], 127 | [0,0,0,1,1,0,0,0]] 128 | 129 | gmat2 = [[0,1,0,1,1,1,0], 130 | [0,0,1,0,0,0,0], 131 | [0,0,0,0,0,1,0], 132 | [0,0,1,0,0,0,0], 133 | [0,0,0,0,0,0,1], 134 | [0,0,0,0,0,0,0], 135 | [0,0,1,0,0,1,0]] 136 | 137 | gmat4 = [[ 0, 50, 10,inf, 45,inf], 138 | [inf, 0, 15,inf, 5,inf], 139 | [ 20,inf, 0, 15,inf,inf], 140 | [inf, 16,inf, 0, 35,inf], 141 | [inf,inf,inf, 30, 0,inf], 142 | [inf,inf,inf, 3,inf, 0]] 143 | 144 | gmat5 = [[ 0, 10,inf,inf, 19, 21], 145 | [ 10, 0, 5, 6,inf, 11], 146 | [inf, 5, 0, 6,inf,inf], 147 | [inf, 6, 6, 0, 18, 14], 148 | [ 19,inf,inf, 18, 0, 7], 149 | [ 21, 11,inf, 14, 7, 0]] 150 | 151 | gmat6 = [[ 0, 10,inf,inf, 19, 21], 152 | [ 10, 0, 5, 6,inf, 11], 153 | [inf, 5, 0, 6,inf,inf], 154 | [inf, 6, 6, 0, 18, 14], 155 | [ 19,inf,inf, 18, 0, 33], 156 | [ 21, 11,inf, 14, 33, 0]] 157 | 158 | gmat7 = [[0,0,1,0,0,0,0,1,0], 159 | [0,0,1,1,1,0,0,0,0], 160 | [0,0,0,1,0,0,0,0,0], 161 | [0,0,0,0,0,1,1,0,0], 162 | [0,0,0,0,0,1,0,0,0], 163 | [0,0,0,0,0,0,0,0,0], 164 | [0,0,0,0,0,0,0,0,0], 165 | [0,0,0,0,0,0,0,0,1], 166 | [0,0,0,0,0,0,1,0,0]] 167 | 168 | gmat8 = [[inf, 6, 4, 5,inf,inf,inf,inf,inf], 169 | [inf,inf,inf,inf, 1,inf,inf,inf,inf], 170 | [inf,inf,inf,inf, 1,inf,inf,inf,inf], 171 | [inf,inf,inf,inf,inf, 2,inf,inf,inf], 172 | [inf,inf,inf,inf,inf,inf, 9, 7,inf], 173 | [inf,inf,inf,inf,inf,inf,inf, 4,inf], 174 | [inf,inf,inf,inf,inf,inf,inf,inf, 2], 175 | [inf,inf,inf,inf,inf,inf,inf,inf, 4], 176 | [inf,inf,inf,inf,inf,inf,inf,inf,inf]] 177 | 178 | 179 | if __name__ == '__main__': 180 | 181 | 182 | # g1 = Graph(gmat, 0) 183 | # print(str(g1), '\n') 184 | 185 | g3 = GraphAL(gmat, 0) 186 | print(str(g3)) 187 | g3.add_edge(0, 3, 5) 188 | g3.add_edge(1, 3, 6) 189 | g3.add_edge(3, 1, 9) 190 | x = g3.add_vertex() 191 | print(x) 192 | g3.add_edge(x, 1, 5) 193 | g3.add_edge(2, x, 6) 194 | print(str(g3)) 195 | -------------------------------------------------------------------------------- /Reference/progs/graph_basic.py: -------------------------------------------------------------------------------- 1 | """ 图的DFS序列和DFS生成树的函数 2 | """ 3 | 4 | from graph import * 5 | 6 | from stack_list import SStack 7 | #from queue_list import SQueue, QueueUnderflow 8 | 9 | # Generate the DFS sequence of reachable vertices from v0 10 | 11 | 12 | def DFS_seq(graph, v0): 13 | vnum = graph.vertex_num() 14 | visited = [0]*vnum 15 | visited[v0] = 1 16 | dfs_seq = [v0] 17 | st = SStack() 18 | st.push((0, graph.out_edges(v0))) 19 | while not st.is_empty(): 20 | i, edges = st.pop() 21 | if i < len(edges): 22 | v, e = edges[i] 23 | st.push((i+1, edges)) 24 | if visited[v] == 0: # unvisited node 25 | dfs_seq.append(v) 26 | visited[v] = 1 27 | st.push((0, graph.out_edges(v))) 28 | return dfs_seq 29 | 30 | # Generate span-forest of a graph, recursive definition 31 | 32 | 33 | def DFS_span_forest(graph): 34 | vnum = graph.vertex_num() 35 | span_forest = [None] * vnum 36 | 37 | def dfs(gr, v): 38 | nonlocal span_forest 39 | for u, w in gr.out_edges(v): 40 | if span_forest[u] is None: 41 | span_forest[u] = (v, w) 42 | dfs(gr, u) 43 | 44 | for v in range(vnum): 45 | if span_forest[v] is None: 46 | span_forest[v] = (v, 0) 47 | dfs(graph, v) 48 | return span_forest 49 | 50 | 51 | if __name__ == '__main__': 52 | g1 = GraphAL(gmat1, 0) 53 | dfs1 = DFS_seq(g1, 0) 54 | print(dfs1) 55 | 56 | g2 = GraphAL(gmat2, 0) 57 | dfs2 = DFS_seq(g2, 0) 58 | print(dfs2, "\n") 59 | 60 | dfs_tree = DFS_span_forest(g1) 61 | print(dfs_tree) 62 | dfs_tree = DFS_span_forest(g2) 63 | print(dfs_tree) 64 | -------------------------------------------------------------------------------- /Reference/progs/graph_shortestpath.py: -------------------------------------------------------------------------------- 1 | """ 单源点最短路径算法,所有顶点对之间的最短路径算法 2 | """ 3 | 4 | from prioqueue import PrioQueue # , PrioQueueError 5 | from graph import * 6 | 7 | 8 | # Find nearest pathes from a single vertex to other reachable 9 | # vertices using Dijkstra algorithm, with priority queue. 10 | def dijkstra_shortest_paths(graph, v0): 11 | vnum = graph.vertex_num() 12 | assert 0 <= v0 < vnum 13 | count, paths = 0, [None]*vnum 14 | cands = PrioQueue([(0, v0, v0)]) 15 | while count < vnum and not cands.is_empty(): 16 | plen, u, vmin = cands.dequeue() 17 | if paths[vmin]: 18 | continue 19 | paths[vmin] = (u, plen) 20 | for v, w in graph.out_edges(vmin): 21 | if not paths[v]: 22 | cands.enqueue((plen + w, vmin, v)) 23 | count += 1 24 | return paths 25 | 26 | 27 | # Find all nearset pathes using Floyd-Warshall algorithm 28 | def all_shortest_paths(graph): 29 | vnum = graph.vertex_num() 30 | a = [[graph.get_edge(i, j) for j in range(vnum)] 31 | for i in range(vnum)] # create a copy the adjacent matrix 32 | nvertex = [[-1 if a[i][j] == inf else j 33 | for j in range(vnum)] 34 | for i in range(vnum)] 35 | for k in range(vnum): 36 | for i in range(vnum): 37 | for j in range(vnum): 38 | if a[i][j] > a[i][k] + a[k][j]: 39 | a[i][j] = a[i][k] + a[k][j] 40 | nvertex[i][j] = nvertex[i][k] 41 | return a, nvertex 42 | 43 | 44 | def test_dijkstra(): 45 | paths0 = dijkstra_shortest_paths(g1, 0) 46 | paths1 = dijkstra_shortest_paths(g1, 1) 47 | paths2 = dijkstra_shortest_paths(g1, 2) 48 | paths3 = dijkstra_shortest_paths(g1, 3) 49 | paths4 = dijkstra_shortest_paths(g1, 4) 50 | paths5 = dijkstra_shortest_paths(g1, 5) 51 | 52 | if (paths0 != [(0, 0), (3, 41), (0, 10), (2, 25), (0, 45), None] or 53 | paths1 != [(2, 35), (1, 0), (1, 15), (2, 30), (1, 5), None] or 54 | paths2 != [(2, 20), (3, 31), (2, 0), (2, 15), (1, 36), None] or 55 | paths3 != [(2, 51), (3, 16), (1, 31), (3, 0), (1, 21), None] or 56 | paths4 != [(2, 81), (3, 46), (1, 61), (4, 30), (4, 0), None] or 57 | paths5 != [(2, 54), (3, 19), (1, 34), (5, 3), (1, 24), (5, 0)]): 58 | 59 | print("Some result are not correct.") 60 | 61 | print("start v0:", paths0) 62 | print("start v1:", paths1) 63 | print("start v2:", paths2) 64 | print("start v3:", paths3) 65 | print("start v4:", paths4) 66 | print("start v5:", paths5) 67 | # end test_dijkstra() 68 | 69 | 70 | def test_floyd(): 71 | paths = all_shortest_paths(g1) 72 | print("") 73 | print(paths[0]) 74 | print(paths[1]) 75 | 76 | 77 | if __name__ == '__main__': 78 | 79 | g1 = GraphAL(gmat4, inf) 80 | 81 | test_dijkstra() 82 | test_floyd() 83 | -------------------------------------------------------------------------------- /Reference/progs/graph_shortestpath_1.py: -------------------------------------------------------------------------------- 1 | """ 单源点最短路径算法,所有顶点对之间的最短路径算法 2 | """ 3 | 4 | from prioqueue import PrioQueue # , PrioQueueError 5 | from graph import * 6 | 7 | 8 | # Find nearest pathes from a single vertex to other reachable 9 | # vertices using Dijkstra algorithm. 10 | # Use a loop to find next nearest vertex, time O(V^2), space O(V) 11 | def dijkstra_shortest_paths(graph, v0): 12 | vnum = graph.vertex_num() 13 | assert vnum > 0 and 0 <= v0 < vnum 14 | count, paths = 0, [None]*vnum 15 | cands = [(inf, v0, i) for i in range(graph.vertex_num())] 16 | cands[v0] = (0, v0, v0) 17 | vmin = v0 18 | while vmin > -1: 19 | plen = cands[vmin][0] 20 | paths[vmin] = (cands[vmin][1], plen) 21 | cands[vmin] = None 22 | count += 1 23 | for v, w in graph.out_edges(vmin): 24 | if cands[v] and plen + w < cands[v][0]: # Shorter path, update 25 | cands[v] = (plen + w, vmin, v) 26 | vmin, plen = -1, inf 27 | for i in range(vnum): 28 | if cands[i] and cands[i][0] < plen: 29 | vmin, plen = i, cands[i][0] 30 | return paths 31 | 32 | # Find nearest pathes from a single vertex to other reachable 33 | # vertices using Dijkstra algorithm. 34 | # Use an priority queue to fine the nearest vertex, time O(E), space O(E) 35 | def dijkstra_shortest_paths_0(graph, v0): 36 | vnum = graph.vertex_num() 37 | assert 0 <= v0 < vnum 38 | count, paths = 0, [None]*vnum 39 | cands = PrioQueue([(0, v0, v0)]) 40 | while count < vnum and not cands.is_empty(): 41 | plen, u, vmin = cands.dequeue() 42 | if paths[vmin]: 43 | continue 44 | paths[vmin] = (u, plen) 45 | for v, w in graph.out_edges(vmin): 46 | if not paths[v]: 47 | cands.enqueue((plen + w, vmin, v)) 48 | count += 1 49 | return paths 50 | 51 | 52 | def test_dijkstra(): 53 | paths00 = dijkstra_shortest_paths(g1, 0) 54 | paths01 = dijkstra_shortest_paths_0(g1, 0) 55 | # paths10 = dijkstra_shortest_paths(g1, 1) 56 | # paths11 = dijkstra_shortest_paths_0(g1, 1) 57 | # paths2 = dijkstra_shortest_paths(g1, 2) 58 | # paths3 = dijkstra_shortest_paths(g1, 3) 59 | # paths4 = dijkstra_shortest_paths(g1, 4) 60 | # paths5 = dijkstra_shortest_paths(g1, 5) 61 | 62 | # if (paths0 != [(0, 0), (3, 41), (0, 10), (2, 25), (0, 45), None] or 63 | # paths1 != [(2, 35), (1, 0), (1, 15), (2, 30), (1, 5), None] or 64 | # paths2 != [(2, 20), (3, 31), (2, 0), (2, 15), (1, 36), None] or 65 | # paths3 != [(2, 51), (3, 16), (1, 31), (3, 0), (1, 21), None] or 66 | # paths4 != [(2, 81), (3, 46), (1, 61), (4, 30), (4, 0), None] or 67 | # paths5 != [(2, 54), (3, 19), (1, 34), (5, 3), (1, 24), (5, 0)]): 68 | # 69 | # print("Some result are not correct.") 70 | 71 | print("start v0:", paths00) 72 | print("start v0:", paths01) 73 | # print("start v1:", paths10) 74 | # print("start v1:", paths11) 75 | # print("start v4:", paths4) 76 | # print("start v5:", paths5) 77 | # end test_dijkstra() 78 | 79 | 80 | if __name__ == '__main__': 81 | 82 | g1 = GraphAL(gmat4, inf) 83 | 84 | test_dijkstra() 85 | -------------------------------------------------------------------------------- /Reference/progs/graph_spantree-1.py: -------------------------------------------------------------------------------- 1 | """ 最小生成树的 Prim 算法 2 | """ 3 | 4 | from dec_prioheap import * 5 | from graph import * 6 | 7 | 8 | def Prim(graph): 9 | vnum = graph.vertex_num() 10 | wv_seq = [[graph.get_edge(0, v), v, 0] for v in range(vnum)] 11 | connects = DecPrioHeap(wv_seq) # record vertices 12 | mst = [None]*vnum 13 | while not connects.is_empty(): 14 | w, mv, u = connects.getmin() # take nearest vertex and edge 15 | if w == inf: 16 | break 17 | mst[mv] = ((u, mv), w) # new MST edge and vertex vmin 18 | for v, w in graph.out_edges(mv): # edge is in form (v, w) 19 | if not mst[v] and w < connects.weight(v): 20 | connects.dec_weight(v, w, mv) 21 | return mst 22 | 23 | if __name__ == '__main__': 24 | 25 | pass 26 | -------------------------------------------------------------------------------- /Reference/progs/graph_spantree.py: -------------------------------------------------------------------------------- 1 | """ 最小生成树的 Kruskal 算法 2 | """ 3 | 4 | from prioqueue import PrioQueue # , PrioQueueError 5 | from graph import * 6 | 7 | 8 | def Kruskal(graph): 9 | vnum = graph.vertex_num() 10 | reps = [i for i in range(vnum)] 11 | mst, edges = [], [] 12 | for vi in range(vnum): # put all edges into a list 13 | for v, w in graph.out_edges(vi): 14 | edges.append((w, vi, v)) 15 | edges.sort() # sort, O(n log n) time 16 | for w, vi, vj in edges: 17 | if reps[vi] != reps[vj]: 18 | mst.append(((vi, vj), w)) 19 | if len(mst) == vnum-1: 20 | break 21 | rep, orep = reps[vi], reps[vj] 22 | for i in range(vnum): 23 | if reps[i] == orep: 24 | reps[i] = rep 25 | return mst 26 | 27 | 28 | def Prim(graph): 29 | """ Assume that graph is a network, a connected undirect 30 | graph. This function implements Prim algorithm to build its 31 | minimal span tree. A list mst to store the resulting 32 | span tree, where each element takes the form ((i, j), w). 33 | A representing array reps is used to record the representive 34 | vertics of each of the connective parts. 35 | """ 36 | vnum = graph.vertex_num() 37 | mst = [None]*vnum 38 | cands = PrioQueue([(0, 0, 0)]) # record cand-edges (w, vi, wj) 39 | count = 0 40 | while count < vnum and not cands.is_empty(): 41 | w, u, v = cands.dequeue() # take minimal candidate edge 42 | if mst[v]: # vmin is already in mst 43 | continue 44 | mst[v] = ((u, v), w) # record new MST edge and vertex 45 | count += 1 46 | for vi, w in graph.out_edges(v): # for adjacents of vmin 47 | if not mst[vi]: # when v is not in mst yet 48 | cands.enqueue((w, v, vi)) 49 | return mst 50 | 51 | inf = float("inf") 52 | 53 | if __name__ == '__main__': 54 | 55 | g3 = GraphAL(gmat5, inf) 56 | g4 = GraphAL(gmat6, inf) 57 | 58 | spt1 = Kruskal(g3) 59 | print(spt1) 60 | 61 | spt2 = Prim(g3) 62 | print(spt2) 63 | 64 | spt3 = Kruskal(g4) 65 | print(spt3) 66 | 67 | spt4 = Prim(g4) 68 | print(spt4) 69 | -------------------------------------------------------------------------------- /Reference/progs/graph_toposort.py: -------------------------------------------------------------------------------- 1 | """ 有向图的拓扑排序算法 2 | """ 3 | 4 | from graph import * 5 | 6 | 7 | # We suppose that A[i][i] = unconn value 8 | def toposort(graph): 9 | vnum = graph.vertex_num() 10 | indegree, toposeq = [0]*vnum, [] 11 | zerov = -1 12 | for vi in range(vnum): 13 | for v, w in graph.out_edges(vi): 14 | indegree[v] += 1 15 | for vi in range(vnum): 16 | if indegree[vi] == 0: 17 | indegree[vi] = zerov 18 | zerov = vi 19 | for n in range(vnum): 20 | if zerov == -1: # There is no topo-seq 21 | return False 22 | toposeq.append(zerov) 23 | vi = zerov 24 | zerov = indegree[zerov] 25 | for v, w in graph.out_edges(vi): 26 | indegree[v] -= 1 27 | if indegree[v] == 0: 28 | indegree[v] = zerov 29 | zerov = v 30 | return toposeq 31 | 32 | """ generate critical path of AOE 33 | """ 34 | 35 | 36 | # graph 里无边用 inf 表示 37 | def critical_path(graph): 38 | toposeq = toposort(graph) 39 | if not toposeq: # no topo-sequence, cannot continue 40 | return False 41 | vnum = graph.vertex_num() 42 | crt_actions = [] 43 | ee = event_earliest_time(vnum, graph, toposeq) 44 | le = event_latest_time(vnum, graph, toposeq, ee[vnum-1]) 45 | for i in range(vnum): 46 | for j, w in graph.out_edges(i): 47 | if ee[i] == le[j] - w: # a critical action 48 | crt_actions.append([i, j, ee[i]]) 49 | return crt_actions # return the critical actions 50 | 51 | 52 | def event_earliest_time(vnum, graph, toposeq): 53 | ee = [0]*vnum 54 | for k in range(vnum-1): # 最后一个顶点不必做 55 | i = toposeq[k] 56 | for j, w in graph.out_edges(i): 57 | if ee[i] + w > ee[j]: # 事件 j 还更晚结束? 58 | ee[j] = ee[i] + w 59 | return ee 60 | 61 | 62 | def event_latest_time(vnum, graph, toposeq, eelast): 63 | le = [eelast]*vnum 64 | for k in range(vnum-2, -1, -1): # 逆拓扑顺序, 两端顶点都不必做 65 | i = toposeq[k] 66 | for j, w in graph.out_edges(i): 67 | if le[j] - w < le[i]: # 事件 i 应更早开始? 68 | le[i] = le[j] - w 69 | return le 70 | 71 | 72 | def critical_paths(graph): 73 | def events_earliest_time(vnum, graph, toposeq): 74 | ee = [0]*vnum 75 | for i in toposeq: 76 | for j, w in graph.out_edges(i): 77 | if ee[i] + w > ee[j]: # 事件j还更晚结束? 78 | ee[j] = ee[i] + w 79 | return ee 80 | 81 | def event_latest_time(vnum, graph, toposeq, eelast): 82 | le = [eelast]*vnum 83 | for k in range(vnum-2, -1, -1): # 逆拓扑顺序 84 | i = toposeq[k] 85 | for j, w in graph.out_edges(i): 86 | if le[j] - w < le[i]: # 事件i应更早开始? 87 | le[i] = le[j] - w 88 | return le 89 | 90 | def crt_paths(vnum, graph, ee, le): 91 | crt_actions = [] 92 | for i in range(vnum): 93 | for j, w in graph.out_edges(i): 94 | if ee[i] == le[j] - w: # 关键活动 95 | crt_actions.append((i, j, ee[i])) 96 | return crt_actions 97 | 98 | toposeq = toposort(graph) 99 | if not toposeq: # 没有拓扑序列,失败结束 100 | return False 101 | vnum = graph.vertex_num() 102 | ee = events_earliest_time(vnum, graph, toposeq) 103 | le = event_latest_time(vnum, graph, toposeq, ee[vnum-1]) 104 | return crt_paths(vnum, graph, ee, le) 105 | 106 | 107 | if __name__ == '__main__': 108 | 109 | 110 | g7 = GraphAL(gmat7) 111 | 112 | 113 | g8 = GraphAL(gmat8, inf) 114 | 115 | ## toposeq = toposort(g7) 116 | ## print(toposeq) 117 | 118 | cp = critical_path(g8) 119 | print(cp) 120 | cp = critical_paths(g8) 121 | print(cp) 122 | 123 | pass 124 | -------------------------------------------------------------------------------- /Reference/progs/list_circlinked.py: -------------------------------------------------------------------------------- 1 | """ 循环单链表类 """ 2 | 3 | from list_node import LNode, LinkedListUnderflow 4 | 5 | 6 | class LCList: # class of Circular Linked List 7 | def __init__(self): 8 | self._rear = None 9 | 10 | def is_empty(self): 11 | return self._rear is None 12 | 13 | def prepend(self, elem): # add element in the front end 14 | p = LNode(elem) 15 | if self._rear is None: 16 | p.next = p # initiates circle 17 | self._rear = p 18 | else: 19 | p.next = self._rear.next 20 | self._rear.next = p 21 | 22 | def append(self, elem): # add element in the rear end 23 | self.prepend(elem) 24 | self._rear = self._rear.next 25 | 26 | def pop(self): # pop out head element 27 | if self._rear is None: 28 | raise LinkedListUnderflow("in pop of CLList") 29 | p = self._rear.next 30 | if self._rear is p: 31 | self._rear = None 32 | else: 33 | self._rear.next = p.next 34 | return p.elem 35 | 36 | def printall(self): 37 | if self.is_empty(): 38 | return 39 | p = self._rear.next 40 | while True: 41 | print(p.elem) 42 | if p is self._rear: 43 | break 44 | p = p.next 45 | 46 | 47 | if __name__ == '__main__': 48 | from random import randint 49 | 50 | mlist = LCList() 51 | for i in range(10): 52 | mlist.prepend(randint(i, 20)) 53 | for i in range(11, 20): 54 | mlist.append(randint(i, 30)) 55 | #mlist1.printall() 56 | 57 | while not mlist.is_empty(): 58 | print(mlist.pop()) 59 | -------------------------------------------------------------------------------- /Reference/progs/list_doublinked.py: -------------------------------------------------------------------------------- 1 | """ 双链表结点类,带尾结点引用的双链表类 """ 2 | 3 | from list_node import LNode, LinkedListUnderflow 4 | from list_linked1 import LList1 5 | 6 | 7 | class DLNode(LNode): # class of Double Linked Nodes 8 | def __init__(self, elem, prev=None, next_=None): 9 | LNode.__init__(self, elem, next_) 10 | self.prev = prev 11 | 12 | 13 | class DLList(LList1): # class of Double Linked List 14 | def __init__(self): 15 | LList1.__init__(self) 16 | 17 | def prepend(self, elem): 18 | p = DLNode(elem, None, self._head) 19 | if self._head is None: 20 | self._rear = p 21 | else: # otherwise, create the prev reference 22 | p.next.prev = p 23 | self._head = p 24 | 25 | def append(self, elem): 26 | p = DLNode(elem, self._rear, None) 27 | if self._head is None: # insert in empty list 28 | self._head = p 29 | else: # otherwise, create the next reference 30 | p.prev.next = p 31 | self._rear = p 32 | 33 | def pop(self): 34 | if self._head is None: 35 | raise LinkedListUnderflow("in pop of LDList") 36 | e = self._head.elem 37 | self._head = self._head.next 38 | if self._head: 39 | self._head.prev = None 40 | return e 41 | 42 | def pop_last(self): 43 | if self._head is None: 44 | raise LinkedListUnderflow("in pop_last of LDList") 45 | e = self._rear.elem 46 | self._rear = self._rear.prev 47 | if self._rear is None: 48 | self._head = None # it is empty now 49 | else: 50 | self._rear.next = None 51 | return e 52 | 53 | if __name__ == '__main__': 54 | from random import randint 55 | mlist = DLList() 56 | for i in range(10): 57 | mlist.prepend(randint(i, 30)) 58 | for i in range(11, 20): 59 | mlist.append(randint(1, 50)) 60 | 61 | mlist.printall() 62 | 63 | for x in mlist.filter(lambda y: y % 2 == 1): 64 | print(x) 65 | 66 | while not mlist.is_empty(): 67 | print(mlist.pop()) 68 | if not mlist.is_empty(): 69 | print(mlist.pop_last()) 70 | -------------------------------------------------------------------------------- /Reference/progs/list_josephus.py: -------------------------------------------------------------------------------- 1 | from list_circlinked import LCList 2 | """ 使用链表实现的几个 Josenphus 求解程序 """ 3 | 4 | def josephus_A(n, k, m): 5 | people = list(range(1, n+1)) 6 | 7 | s = 0 8 | 9 | i = k-1 10 | for num in range(n): 11 | count = 0 12 | while count < m: 13 | s += 1 14 | if people[i] > 0: 15 | count += 1 16 | if count == m: 17 | print(people[i], end="") 18 | people[i] = 0 19 | i = (i+1) % n 20 | if num < n-1: 21 | print(", ", end="") 22 | else: 23 | print("") 24 | print(s) 25 | return 26 | 27 | 28 | def josephus_L(n, k, m): 29 | people = list(range(1, n+1)) 30 | 31 | num, i = n, k-1 32 | for num in range(n, 0, -1): 33 | i = (i + m-1) % num 34 | print(people.pop(i), 35 | end=(", " if num > 1 else "\n")) 36 | return 37 | 38 | 39 | class Josephus(LCList): 40 | def turn(self, m): 41 | for i in range(m): 42 | self._rear = self._rear.next 43 | 44 | def __init__(self, n, k, m): 45 | LCList.__init__(self) 46 | for i in range(n): 47 | self.append(i+1) 48 | self.turn(k-1) 49 | while not self.is_empty(): 50 | self.turn(m-1) 51 | print(self.pop(), 52 | end=("\n" if self.is_empty() else ", ")) 53 | # end class Josephus 54 | 55 | 56 | if __name__ == '__main__': 57 | for i in range(10): 58 | s = input("Josephus parameters (n k m): ") 59 | n, k, m = map(int, s.split()) 60 | josephus_A(n, k, m) 61 | # josephus_L(n, k, m) 62 | # Josephus(n, k, m) 63 | 64 | -------------------------------------------------------------------------------- /Reference/progs/list_linked.py: -------------------------------------------------------------------------------- 1 | """ 链接表类,其中使用链接表结点类 """ 2 | 3 | from list_node import LNode, LinkedListUnderflow 4 | 5 | 6 | class LList: 7 | def __init__(self): 8 | self._head = None 9 | 10 | def is_empty(self): 11 | return self._head is None 12 | 13 | def prepend(self, elem): 14 | self._head = LNode(elem, self._head) 15 | 16 | def pop(self): 17 | if self._head is None: 18 | raise LinkedListUnderflow("in pop") 19 | e = self._head.elem 20 | self._head = self._head.next 21 | return e 22 | 23 | def append(self, elem): 24 | if self._head is None: 25 | self._head = LNode(elem) 26 | return 27 | p = self._head 28 | while p.next: 29 | p = p.next 30 | p.next = LNode(elem) 31 | 32 | def pop_last(self): 33 | if self._head is None: # empty list 34 | raise LinkedListUnderflow("in pop_last") 35 | p = self._head 36 | if p.next is None: # list with only one element 37 | e = p.elem 38 | self._head = None 39 | return e 40 | while p.next.next: # till p.next be last node 41 | p = p.next 42 | e = p.next.elem 43 | p.next = None 44 | return e 45 | 46 | def find(self, pred): 47 | p = self._head 48 | while p: 49 | if pred(p.elem): 50 | return p.elem 51 | p = p.next 52 | 53 | def printall(self): 54 | p = self._head 55 | while p: 56 | print(p.elem, end='') 57 | if p.next: 58 | print(', ', end='') 59 | p = p.next 60 | print('') 61 | 62 | def rev(self): 63 | p = None 64 | while self._head: 65 | q = self._head 66 | self._head = q.next 67 | q.next = p 68 | p = q 69 | self._head = p 70 | 71 | def sort(self): 72 | p = self._head 73 | if p is None or p.next is None: 74 | return 75 | 76 | rem = p.next 77 | p.next = None 78 | while rem: 79 | p = self._head 80 | q = None 81 | while p and p.elem <= rem.elem: 82 | q = p 83 | p = p.next 84 | if q is None: 85 | self._head = rem 86 | else: 87 | q.next = rem 88 | q = rem 89 | rem = rem.next 90 | q.next = p 91 | 92 | # def sort(self): 93 | # if self.head is None: 94 | # return 95 | # last = self.head 96 | # crt = last.next # 初始,排序段只有一个结点 97 | # while crt: # 循环,一次处理一个结点 98 | # p = self.head 99 | # q = None # 设置扫描指针初值 100 | # while p is not crt and p.elem <= crt.elem: 101 | # q = p 102 | # p = p.next # 顺序更新两个扫描指针 103 | # if p is crt: # p 是 crt 时不用修改链接,设置 last 到下一结点 crt 104 | # last = crt 105 | # else: 106 | # last.next = crt.next # 取下当前结点 107 | # crt.next = p # 接好后链接 108 | # if q is None: 109 | # self.head = crt # 作为新的首结点 110 | # else: 111 | # q.next = crt # 或者接在表中间 112 | # crt = last.next # 无论什么情况,crt 总是 last 的下一结点 113 | 114 | def for_each(self, proc): 115 | p = self._head 116 | while p: 117 | proc(p.elem) 118 | p = p.next 119 | 120 | def elements(self): 121 | p = self._head 122 | while p: 123 | yield p.elem 124 | p = p.next 125 | 126 | def filter(self, pred): 127 | p = self._head 128 | while p: 129 | if pred(p.elem): 130 | yield p.elem 131 | p = p.next 132 | 133 | #end of class LList 134 | 135 | 136 | def list_sort(lst): 137 | for i in range(1, len(lst)): # seg [0:0] is sorted 138 | x = lst[i] 139 | j = i 140 | while j > 0 and lst[j-1] > x: # moving one by one 141 | lst[j] = lst[j-1] # in reversed-order 142 | j -= 1 143 | lst[j] = x 144 | 145 | 146 | import random 147 | 148 | if __name__ == '__main__': 149 | mlist1 = LList() 150 | 151 | for i in range(10): 152 | mlist1.prepend(i) 153 | 154 | for i in range(11, 20): 155 | mlist1.append(i) 156 | 157 | mlist1.printall() 158 | for i in range(5): 159 | print(mlist1.pop()) 160 | print(mlist1.pop_last()) 161 | 162 | print('remained:') 163 | mlist1.printall() 164 | mlist1.rev() 165 | print('\nreversed:') 166 | mlist1.printall() 167 | 168 | mlist1.sort() 169 | print('\nsorted:') 170 | mlist1.printall() 171 | for x in mlist1.elements(): 172 | print(x) 173 | print('\n') 174 | 175 | list1 = [random.randint(1, 50) for i in range(20)] 176 | print(list1, '\n') 177 | list_sort(list1) 178 | print(list1) 179 | -------------------------------------------------------------------------------- /Reference/progs/list_linked1.py: -------------------------------------------------------------------------------- 1 | """ 带尾结点引用的单链表类 """ 2 | 3 | from list_node import LNode, LinkedListUnderflow 4 | from list_linked import LList 5 | 6 | 7 | class LList1(LList): 8 | def __init__(self): 9 | LList.__init__(self) 10 | self._rear = None 11 | 12 | # def prepend(self, elem): 13 | # self._head = LNode(elem, self._head) 14 | # if self._rear is None: # empty list 15 | # self._rear = self._head 16 | 17 | def prepend(self, elem): 18 | if self._head is None: 19 | self._head = LNode(elem, self._head) 20 | self._rear = self._head 21 | else: 22 | self._head = LNode(elem, self._head) 23 | 24 | def append(self, elem): 25 | if self._head is None: # empty list 26 | self._head = LNode(elem, self._head) 27 | self._rear = self._head 28 | else: 29 | self._rear.next = LNode(elem) 30 | self._rear = self._rear.next 31 | 32 | def pop_last(self): 33 | if self._head is None: # empty list 34 | raise LinkedListUnderflow("in pop_last of LList1") 35 | p = self._head 36 | if p.next is None: # list with only one element 37 | e = p.elem 38 | self._head = None 39 | self._rear = None 40 | return e 41 | while p.next.next: # till p.next be last node 42 | p = p.next 43 | e = p.next.elem 44 | p.next = None 45 | self._rear = p 46 | return e 47 | 48 | 49 | if __name__ == '__main__': 50 | mlist1 = LList1() 51 | mlist1.prepend(99) 52 | 53 | for i in range(11, 20): 54 | mlist1.append(i) 55 | 56 | for x in mlist1.filter(lambda y: y % 2 == 0): 57 | print(x) 58 | 59 | ss = 0 60 | while not mlist1.is_empty(): 61 | ss += mlist1.pop() 62 | print(ss) 63 | 64 | for i in range(10): 65 | mlist1.prepend(i) 66 | 67 | mlist1.printall() 68 | -------------------------------------------------------------------------------- /Reference/progs/list_node.py: -------------------------------------------------------------------------------- 1 | """ 链接表结点类和一个使用结点的简单函数 """ 2 | 3 | class LinkedListUnderflow(ValueError): 4 | pass 5 | 6 | 7 | class LNode: 8 | def __init__(self, elem, next_=None): 9 | self.elem = elem 10 | self.next = next_ 11 | 12 | 13 | def length(head): 14 | p, n = head, 0 15 | while p: 16 | n += 1 17 | p = p.next 18 | return n 19 | 20 | if __name__ == '__main__': 21 | 22 | llist1 = LNode(1) 23 | p = llist1 24 | 25 | for i in range(2, 11): 26 | p.next = LNode(i) 27 | p = p.next 28 | 29 | print(length(llist1)) 30 | 31 | p = llist1 32 | while p: 33 | print(p.elem) 34 | p = p.next 35 | -------------------------------------------------------------------------------- /Reference/progs/prioqueue.py: -------------------------------------------------------------------------------- 1 | """ 优先队列的两个实现:连续表实现和堆实现 """ 2 | 3 | # This file contains two 4 | # implementations of priority queues: 5 | # 1, as sorted list 6 | # 2, as heap stored in a list 7 | # and in addition, 8 | # an implementation of heap sort function 9 | 10 | 11 | class PrioQueueError(ValueError): 12 | pass 13 | 14 | 15 | class PrioQue: 16 | """ Implementing binary trees as sorted list 17 | """ 18 | def __init__(self, elist=[]): 19 | self._elems = list(elist) 20 | self._elems.sort(reverse=True) 21 | 22 | def is_empty(self): 23 | return not self._elems 24 | 25 | def peek(self): 26 | if self.is_empty(): 27 | raise PrioQueueError("in top") 28 | return self._elems[-1] 29 | 30 | def dequeue(self): 31 | if self.is_empty(): 32 | raise PrioQueueError("in pop") 33 | return self._elems.pop() 34 | 35 | def enqueue(self, e): 36 | i = len(self._elems) - 1 37 | while i >= 0: 38 | if self._elems[i] <= e: 39 | i -= 1 40 | else: 41 | break 42 | self._elems.insert(i+1, e) 43 | 44 | def show(self): # only for test 45 | print(",".join(map(str, self._elems))) 46 | 47 | 48 | class PrioQueue: 49 | """ Implementing priority queues using heaps 50 | """ 51 | def __init__(self, elist=[]): 52 | self._elems = list(elist) 53 | if elist: 54 | self.buildheap() 55 | 56 | def is_empty(self): 57 | return not self._elems 58 | 59 | def peek(self): 60 | if self.is_empty(): 61 | raise PrioQueueError("in peek") 62 | return self._elems[0] 63 | 64 | def enqueue(self, e): 65 | self._elems.append(None) # add a dummy element 66 | self.siftup(e, len(self._elems)-1) 67 | 68 | def siftup(self, e, last): 69 | elems, i, j = self._elems, last, (last-1)//2 70 | while i > 0 and e < elems[j]: 71 | elems[i] = elems[j] 72 | i, j = j, (j-1)//2 73 | elems[i] = e 74 | 75 | def dequeue(self): 76 | if self.is_empty(): 77 | raise PrioQueueError("in dequeue") 78 | elems = self._elems 79 | e0 = elems[0] 80 | e = elems.pop() 81 | if len(elems) > 0: 82 | self.siftdown(e, 0, len(elems)) 83 | return e0 84 | 85 | def siftdown(self, e, begin, end): 86 | elems, i, j = self._elems, begin, begin*2+1 87 | while j < end: # invariant: j == 2*i+1 88 | if j+1 < end and elems[j+1] < elems[j]: 89 | j += 1 # elems[j] <= its brother 90 | if e < elems[j]: # e is the smallest of the three 91 | break 92 | elems[i] = elems[j] # elems[j] is the smallest, move it up 93 | i, j = j, 2*j+1 94 | elems[i] = e 95 | 96 | def buildheap(self): 97 | end = len(self._elems) 98 | for i in range(end//2, -1, -1): 99 | self.siftdown(self._elems[i], i, end) 100 | 101 | 102 | def heap_sort(elems): 103 | def siftdown(elems, e, begin, end): 104 | i, j = begin, begin*2+1 105 | while j < end: # invariant: j == 2*i+1 106 | if j+1 < end and elems[j+1] < elems[j]: 107 | j += 1 # elems[j] <= its brother 108 | if e < elems[j]: # e is the smallest of the three 109 | break 110 | elems[i] = elems[j] # elems[j] is the smallest, move it up 111 | i, j = j, 2*j+1 112 | elems[i] = e 113 | 114 | end = len(elems) 115 | for i in range(end//2, -1, -1): 116 | siftdown(elems, elems[i], i, end) 117 | for i in range((end-1), 0, -1): 118 | e = elems[i] 119 | elems[i] = elems[0] 120 | siftdown(elems, e, 0, i) 121 | 122 | 123 | from random import randint 124 | 125 | 126 | def test1(): 127 | print("Test class PrioQue:") 128 | pq = PrioQue([11, 22, 33]) 129 | pq.show() 130 | for i in range(12): 131 | pq.enqueue(randint(0, 30)) 132 | pq.show() 133 | ## while not pq.is_empty(): 134 | ## print(pq.dequeue()) 135 | 136 | 137 | def test2(): 138 | print("Test class PrioQueue:") 139 | pq = PrioQueue([1, 2, 3]) 140 | for i in range(12): 141 | pq.enqueue(randint(0, 30)) 142 | while not pq.is_empty(): 143 | print(pq.dequeue()) 144 | 145 | 146 | def test3(): 147 | print("Test function heap_sort:") 148 | lst = [randint(i, 30) for i in range(15)] 149 | print(lst) 150 | heap_sort(lst) 151 | print(lst) 152 | 153 | if __name__ == '__main__': 154 | test1() 155 | 156 | test2() 157 | 158 | test3() 159 | 160 | pass 161 | -------------------------------------------------------------------------------- /Reference/progs/queue_list.py: -------------------------------------------------------------------------------- 1 | """ 基于 Python list 实现的队列类(循环顺序表队列) 2 | """ 3 | 4 | 5 | class QueueUnderflow(ValueError): 6 | pass 7 | 8 | 9 | class SQueue(): 10 | def __init__(self, init_len=8): 11 | self._len = init_len # length of mem-block 12 | self._elems = [0]*init_len 13 | self._head = 0 # index of head element 14 | self._num = 0 # number of elements 15 | 16 | def is_empty(self): 17 | return self._num == 0 18 | 19 | def peek(self): 20 | if self._num == 0: 21 | raise QueueUnderflow 22 | return self._elems[self._head] 23 | 24 | def dequeue(self): 25 | if self._num == 0: 26 | raise QueueUnderflow 27 | e = self._elems[self._head] 28 | self._head = (self._head+1) % self._len 29 | self._num -= 1 30 | return e 31 | 32 | def enqueue(self, elem): 33 | if self._num == self._len: 34 | self.__extend() 35 | self._elems[(self._head+self._num) % self._len] = elem 36 | self._num += 1 37 | 38 | def __extend(self): 39 | old_len = self._len 40 | self._len *= 2 41 | new_elems = [0]*self._len 42 | for i in range(old_len): 43 | new_elems[i] = self._elems[(self._head + i) % old_len] 44 | self._elems, self._head = new_elems, 0 45 | 46 | from random import randint 47 | 48 | if __name__ == '__main__': 49 | q = SQueue() 50 | for j in range(20): 51 | for i in range(randint(1, 20)): 52 | q.enqueue(i*3) 53 | for i in range(randint(1, 20)): 54 | if not q.is_empty(): 55 | print(q.dequeue()) 56 | 57 | q.enqueue(100) 58 | while not q.is_empty(): 59 | print(q.dequeue()) 60 | 61 | -------------------------------------------------------------------------------- /Reference/progs/rational.py: -------------------------------------------------------------------------------- 1 | """ 一个较好的有理数类实现 """ 2 | # Module rational defines a class (a type) Rational, for rational numbers. 3 | # More operations (methods) can be added. 4 | 5 | 6 | class Rational: 7 | @staticmethod 8 | def _gcd(m, n): 9 | if n == 0: 10 | m, n = n, m 11 | while m != 0: 12 | m, n = n % m, m 13 | return n 14 | 15 | def __init__(self, num, den=1): 16 | if not isinstance(num, int) or not isinstance(den, int): 17 | raise TypeError 18 | if den == 0: 19 | raise ZeroDivisionError 20 | sign = 1 21 | if num < 0: 22 | num, sign = -num, -sign 23 | if den < 0: 24 | den, sign = -den, -sign 25 | g = Rational._gcd(num, den) 26 | # call function gcd defined in this class. 27 | self._num = sign * (num//g) 28 | self._den = den//g 29 | 30 | def num(self): 31 | return self._num 32 | 33 | def den(self): 34 | return self._den 35 | 36 | def __add__(self, another): # mimic + operator 37 | den = self._den * another.den() 38 | num = (self._num * another.den() + 39 | self._den * another.num()) 40 | return Rational(num, den) 41 | 42 | def __mul__(self, another): # mimic * operator 43 | return Rational(self._num * another.num(), 44 | self._den * another.den()) 45 | 46 | def __floordiv__(self, another): # mimic // operator 47 | if another.num() == 0: 48 | raise ZeroDivisionError 49 | return Rational(self._num * another.den(), 50 | self._den * another.num()) 51 | 52 | # ... ... 53 | # Other operators can be defined similarly: 54 | # -:__sub__, /:__truediv__, %:__mod__, etc. 55 | 56 | def __eq__(self, another): 57 | return self._num * another.den() == self._den * another.num() 58 | 59 | def __lt__(self, another): 60 | return self._num * another.den() < self._den * another.num() 61 | 62 | # Other comparison operators can be defined similarly: 63 | # !=:__ne__, <=:__le__, >:__gt__, >=:__ge__ 64 | 65 | def __str__(self): 66 | return str(self._num) + "/" + str(self._den) 67 | 68 | def print(self): 69 | print(self._num, "/", self._den) 70 | 71 | 72 | if __name__ == '__main__': 73 | x = Rational(3, 8) 74 | x = x + Rational(7, 6) 75 | x.print() 76 | 77 | # Rational(17, 0) 78 | -------------------------------------------------------------------------------- /Reference/progs/rational0.py: -------------------------------------------------------------------------------- 1 | # Module rational defines a primitive Rational class (a type). 2 | # More operations (methods) can be defined. 3 | 4 | 5 | class Rational0: 6 | def __init__(self, num, den=1): 7 | self.num = num 8 | self.den = den 9 | 10 | def plus(self, another): 11 | den = self.den * another.den 12 | num = (self.num * another.den + 13 | self.den * another.num) 14 | return Rational0(num, den) 15 | 16 | def print(self): 17 | print(str(self.num)+"/"+str(self.den)) 18 | 19 | 20 | if __name__ == '__main__': 21 | r1 = Rational0(3,5) 22 | r2 = r1.plus(Rational0(7,15)) 23 | r2.print() 24 | -------------------------------------------------------------------------------- /Reference/progs/regex.py: -------------------------------------------------------------------------------- 1 | """ 简单正则表达式的匹配 """ 2 | 3 | __author__ = 'Qiu Zongyan' 4 | #### 简化正则表达式匹配函数 5 | ## 模式语言: 6 | ## 字符 c 与其自身匹配 7 | ## ^ 与字符串开头匹配(匹配前缀) 8 | ## $ 与字符串结束匹配(匹配后缀) 9 | ## . 与任何字符匹配 10 | ## * 和其前一字符一起,与该字符的0次或任意次出现匹配 11 | 12 | ## 限制:字符串中不能出现上述元字符(不支持换意序列) 13 | 14 | 15 | def match(re, text): 16 | rlen, tlen = len(re), len(text) 17 | 18 | def match_here(re, i, text, j): 19 | """检查从text[j]开始的正文是否与re[i]开始的模式匹配""" 20 | while True: 21 | if i == rlen: 22 | return True 23 | if re[i] == '$': 24 | return i+1 == rlen and j == tlen 25 | if i+1 < rlen and re[i+1] == '*': 26 | return match_star(re[i], re, i+2, text, j) 27 | if j == tlen or (re[i] != '.' and re[i] != text[j]): 28 | return False 29 | i, j = i+1, j+1 30 | 31 | def match_star(c, re, i, text, j): 32 | """在text里跳过0个或多个c后检查匹配""" 33 | for n in range(j, tlen): 34 | # print(c, n, i, j) 35 | if match_here(re, i, text, n): 36 | return True 37 | if text[n] != c and c != '.': 38 | break 39 | return False 40 | 41 | if re[0] == '^': 42 | if match_here(re, 1, text, 0): # 只匹配前缀 43 | return 0 44 | return -1 # 匹配前缀不成功 45 | for n in range(tlen): # 检查各个位置的匹配 46 | if match_here(re, 0, text, n): 47 | return n 48 | return -1 49 | 50 | p1 = "a*b.*" 51 | p2 = "^ab*c.$" 52 | p3 = "a*bc.*bc" 53 | p4 = "aab*c$" 54 | 55 | print(match(p1, "bccdabaaabcbbabcccbc")) # 0 56 | print(match(p1, "cccdabaaabcbbabcccbc")) # 4 57 | print(match(p1, "cccdadcccbcbaabcccbc")) # 9 58 | print(match(p2, "abbbbbca")) # 0 59 | print(match(p2, "^^^^abbcd")) # -1 60 | print(match(p2, "abbbbbcda")) # -1 61 | print(match(p3, "bccdabaaabcbbabcccbc")) # 0 62 | print(match(p3, "dccdabaaabcbbabcccbc")) # 6 63 | print(match(p4, "hfahfjkhhaabbc")) # 9 64 | print(match(p4, "hfahfjkhhaabbcd")) # -1 65 | 66 | 67 | -------------------------------------------------------------------------------- /Reference/progs/simulation-customs.py: -------------------------------------------------------------------------------- 1 | """ 公路收费站模拟,队列和优先队列的应用 2 | """ 3 | 4 | from random import randint 5 | from prioqueue import PrioQueue # , PrioQueueError 6 | from queue_list import SQueue # , QueueUnderflow 7 | 8 | 9 | class Simulation: 10 | def __init__(self, duration): 11 | self._eventq = PrioQueue() 12 | self._time = 0 13 | self._duration = duration 14 | 15 | def run(self): 16 | while not self._eventq.is_empty(): 17 | event = self._eventq.dequeue() 18 | self._time = event.time() 19 | if self._time > self._duration: 20 | break 21 | event.run() # may cause new event(s) 22 | 23 | def add_event(self, event): 24 | self._eventq.enqueue(event) 25 | 26 | def cur_time(self): 27 | return self._time 28 | 29 | class Event: 30 | def __init__(self, event_time, host): 31 | self._ctime = event_time 32 | self._host = host 33 | 34 | def __lt__(self, other_event): 35 | return self._ctime < other_event._ctime 36 | 37 | def __le__(self, other_event): 38 | return self._ctime <= other_event._ctime 39 | 40 | def host(self): 41 | return self._host 42 | 43 | def time(self): 44 | return self._ctime 45 | 46 | def run(self): 47 | pass 48 | 49 | 50 | 51 | class Customs: 52 | def __init__(self, gate_num, duration, 53 | arrive_interval, check_interval): 54 | self.simulation = Simulation(duration) 55 | self.waitline = SQueue() 56 | self.duration = duration 57 | self.gates = [0]*gate_num 58 | self.total_wait_time = 0 59 | self.total_used_time = 0 60 | self.car_num = 0 61 | self.arrive_interval = arrive_interval 62 | self.check_interval = check_interval 63 | 64 | def wait_time_acc(self, n): 65 | self.total_wait_time += n 66 | 67 | def total_time_acc(self, n): 68 | self.total_used_time += n 69 | 70 | def car_count_1(self): 71 | self.car_num += 1 72 | 73 | def add_event(self, event): 74 | self.simulation.add_event(event) 75 | 76 | def cur_time(self): 77 | return self.simulation.cur_time() 78 | 79 | def enqueue(self, car): 80 | self.waitline.enqueue(car) 81 | 82 | def has_queued_car(self): 83 | return not self.waitline.is_empty() 84 | 85 | def next_car(self): 86 | return self.waitline.dequeue() 87 | 88 | def find_gate(self): 89 | for i in range(len(self.gates)): 90 | if self.gates[i] == 0: 91 | self.gates[i] = 1 92 | return i 93 | return None 94 | 95 | def free_gate(self, i): 96 | if self.gates[i] == 1: 97 | self.gates[i] = 0 98 | else: 99 | raise ValueError("Clear gate error.") 100 | 101 | def simulate(self): 102 | Arrive(0, self) # initially generate one car 103 | self.simulation.run() 104 | self.statistics() 105 | 106 | def statistics(self): 107 | print("Simulate " + str(self.duration) + " minutes, for " 108 | + str(len(self.gates)) + " gates") 109 | print(self.car_num, "cars pass the customs") 110 | print("Average waiting time:", 111 | self.total_wait_time/self.car_num) 112 | print("Average passing time:", 113 | self.total_used_time/self.car_num) 114 | i = 0 115 | while not self.waitline.is_empty(): 116 | self.waitline.dequeue() 117 | i += 1 118 | print(i, "cars are in waiting line.") 119 | 120 | 121 | class Car: 122 | def __init__(self, arrive_time): 123 | self.time = arrive_time 124 | 125 | def arrive_time(self): 126 | return self.time 127 | 128 | 129 | def event_log(time, name): 130 | print("Event: " + name + ", happens at " + str(time)) 131 | pass 132 | 133 | 134 | 135 | # a run of coming event will enter the next coming event and 136 | # maybe a check and leave event 137 | 138 | class Arrive(Event): 139 | def __init__(self, arrive_time, customs): 140 | Event.__init__(self, arrive_time, customs) 141 | customs.add_event(self) 142 | 143 | def run(self): 144 | time, customs = self.time(), self.host() 145 | # event_log(time, "car arrive") 146 | # genarate the next Arrive event 147 | Arrive(time + randint(*customs.arrive_interval), customs) 148 | # deal with current Arrive car 149 | car = Car(time) 150 | if customs.has_queued_car(): 151 | customs.enqueue(car) 152 | return 153 | i = customs.find_gate() 154 | if i is not None: 155 | # event_log(time, "car check") 156 | Leave(time + randint(*customs.check_interval), 157 | i, car, customs) 158 | else: 159 | customs.enqueue(car) 160 | 161 | 162 | # a run of leaving event will cause some calculations 163 | 164 | class Leave(Event): 165 | def __init__(self, leave_time, gate_num, car, customs): 166 | Event.__init__(self, leave_time, customs) 167 | self.car = car 168 | self.gate_num = gate_num 169 | customs.add_event(self) 170 | 171 | def run(self): 172 | time, customs = self.time(), self.host() 173 | # event_log(time, "car leave") 174 | customs.free_gate(self.gate_num) 175 | customs.car_count_1() 176 | customs.total_time_acc(time - self.car.arrive_time()) 177 | if customs.has_queued_car(): 178 | car = customs.next_car() 179 | i = customs.find_gate() 180 | # event_log(time, "car check") 181 | customs.wait_time_acc(time - car.arrive_time()) 182 | Leave(time + randint(*customs.check_interval), 183 | i, car, customs) 184 | 185 | if __name__ == '__main__': 186 | 187 | car_arrive_interval = (1, 2) 188 | car_check_time = (3, 5) 189 | 190 | cus = Customs(3, 120, car_arrive_interval, car_check_time) 191 | cus.simulate() 192 | pass 193 | -------------------------------------------------------------------------------- /Reference/progs/sort_algs.py: -------------------------------------------------------------------------------- 1 | """ 排序算法 2 | """ 3 | 4 | from random import randrange 5 | 6 | 7 | class Record: 8 | def __init__(self, key, datum): 9 | self.key = key 10 | self.datum = datum 11 | 12 | def __str__(self): 13 | return "R("+str(self.key)+", "+str(self.datum)+")" 14 | 15 | 16 | def printR(lst): 17 | print("["+", ".join(map(str, lst))+"]") 18 | 19 | 20 | #### 简单插入排序 21 | ##def insert_sort(lst) : 22 | ## for i in range(1, len(lst)): # 开始时片段[0:1]已排序 23 | ## x = lst[i] 24 | ## j = i 25 | ## while j > 0 and lst[j-1].key > x.key: 26 | ## lst[j] = lst[j-1] # 反序逐个后移元素至确定插入位置 27 | ## j -= 1 28 | ## lst[j] = x 29 | ## 30 | ## 31 | ##def test1(n): 32 | ## l1 = [Record(randint(1, 20), i) for i in range(n)] 33 | ## printR(l1) 34 | ## insert_sort(l1) 35 | ## printR(l1) 36 | 37 | 38 | #### 简单选择排序 39 | ##def select_sort(lst): 40 | ## for i in range(len(lst)-1): 41 | ## k = i 42 | ## for j in range(i, len(lst)): 43 | ## if lst[j].key < lst[k].key: 44 | ## k = j 45 | ## if i != k: 46 | ## lst[i], lst[k] = lst[k], lst[i] 47 | ## 48 | ## 49 | ##def test2(n): 50 | ## l1 = [Record(randint(1, 20), i) for i in range(n)] 51 | ## printR(l1) 52 | ## select_sort(l1) 53 | ## printR(l1) 54 | 55 | 56 | #### 简单起泡排序 57 | ##def bubble_sort(lst): 58 | ## for i in range(len(lst)): 59 | ## for j in range(1, len(lst)-i): 60 | ## if lst[j-1].key > lst[j].key: 61 | ## lst[j-1], lst[j] = lst[j], lst[j-1] 62 | 63 | 64 | #### 起泡排序,无逆序时提前结束 65 | ##def bubble_sort(lst): 66 | ## for i in range(len(lst)): 67 | ## found = False 68 | ## for j in range(1, len(lst)-i): 69 | ## if lst[j-1].key > lst[j].key: 70 | ## lst[j-1], lst[j] = lst[j], lst[j-1] 71 | ## found = True 72 | ## if not found: 73 | ## break 74 | ## 75 | ## 76 | ##def test3(n): 77 | ## l1 = [Record(randint(1, 20), i) for i in range(n)] 78 | ## printR(l1) 79 | ## bubble_sort(l1) 80 | ## printR(l1) 81 | 82 | 83 | #### 快速排序 84 | ##def quick_sort(lst): 85 | ## def qsort_rec(lst, l, r): 86 | ## if l >= r: 87 | ## return # 分段中无记录或只有一个记录 88 | ## i, j = l, r 89 | ## pivot = lst[i] 90 | ## while i < j: # 找 pivot 的最终位置 91 | ## while i < j and lst[j].key >= pivot.key: 92 | ## j -= 1 # 用 j 向左找小于 pivot 的记录移到左边 93 | ## if i < j: 94 | ## lst[i] = lst[j] 95 | ## i += 1 96 | ## while i < j and lst[i].key <= pivot.key: 97 | ## i += 1 # 用 i 向右找大于 pivot 的记录移到右边 98 | ## if i < j: 99 | ## lst[j] = lst[i] 100 | ## j -= 1 101 | ## lst[i] = pivot # 将 pivot 存入其最终位置 102 | ## qsort_rec(lst, l, i-1) # 递归处理左半区间 103 | ## qsort_rec(lst, i+1, r) # 递归处理右半区间 104 | ## 105 | ## qsort_rec(lst, 0, len(lst)-1) # 主函数调用 qsort_rec 106 | ## 107 | ## 108 | ##def test4(n): 109 | ## l1 = [Record(randint(1, 20), i) for i in range(n)] 110 | ## printR(l1) 111 | ## quick_sort1(l1) 112 | ## printR(l1) 113 | 114 | 115 | #### 快速排序的另一种实现 116 | ##def quick_sort1(lst): 117 | ## def qsort(lst, begin, end): 118 | ## if begin >= end: 119 | ## return 120 | ## pivot = lst[begin].key 121 | ## i = begin 122 | ## for j in range(begin + 1, end + 1): 123 | ## if lst[j].key < pivot: # 发现一个小元素 124 | ## i += 1 125 | ## lst[i], lst[j] = lst[j], lst[i] # 小元素交换到前面 126 | ## lst[begin], lst[i] = lst[i], lst[begin] # 枢轴元素就位 127 | ## qsort(lst, begin, i - 1) 128 | ## qsort(lst, i + 1, end) 129 | ## 130 | ## qsort(lst, 0, len(lst) - 1) 131 | ## 132 | ## 133 | ##def test4(n): 134 | ## l1 = [Record(randint(1, 20), i) for i in range(n)] 135 | ## printR(l1) 136 | ## quick_sort1(l1) 137 | ## printR(l1) 138 | 139 | 140 | #### 归并排序 141 | ##def merge_sort(lst): 142 | ## slen, llen = 1, len(lst) 143 | ## templst = [None] * llen 144 | ## while slen <= llen: 145 | ## merge_pass(lst, templst, llen, slen) 146 | ## slen *= 2 147 | ## merge_pass(templst, lst, llen, slen) # 结果存回原位 148 | ## slen *= 2 149 | ## 150 | ## 151 | ##def merge_pass(lfrom, lto, llen, slen): 152 | ## i = 0 153 | ## while i + 2 * slen < llen: # 归并长slen的两段 154 | ## merge(lfrom, lto, i, i + slen, i + 2 * slen) 155 | ## i += 2 * slen 156 | ## if i + slen < llen: # 剩下两段,后段长度小于slen 157 | ## merge(lfrom, lto, i, i + slen, llen) 158 | ## else: # 只剩下一段,复制到表lto 159 | ## for j in range(i, llen): 160 | ## lto[j] = lfrom[j] 161 | ## 162 | ## 163 | ##def merge(lfrom, lto, low, m, high): 164 | ## i, j, k = low, m, low 165 | ## while i < m and j < high: # 反复复制两段首记录中较小的 166 | ## if lfrom[i].key <= lfrom[j].key: 167 | ## lto[k] = lfrom[i] 168 | ## i += 1 169 | ## else: 170 | ## lto[k] = lfrom[j] 171 | ## j += 1 172 | ## k += 1 173 | ## while i < m: # 复制第一段剩余记录 174 | ## lto[k] = lfrom[i] 175 | ## i += 1 176 | ## k += 1 177 | ## while j < high: # 复制第二段剩余记录 178 | ## lto[k] = lfrom[j] 179 | ## j += 1 180 | ## k += 1 181 | ## 182 | ## 183 | ##def test5(n): 184 | ## l1 = [Record(randint(1, 20), i) for i in range(n)] 185 | ## printR(l1) 186 | ## merge_sort(l1) 187 | ## printR(l1) 188 | 189 | 190 | #### 基数排序 191 | #### 假设被排序仍是以记录类型 Record 为元素的表,其中 192 | #### 关键码是数字 0 到 9 的序列(元组),长度 r 为参数 193 | #### 排序中用 10 个 list 存储各关键码元素对应的序列 194 | #### 一遍分配后收集回到原表,r 遍分配和收集完成排序工作 195 | 196 | def radix_sort(lst, r): 197 | rlists = [[] for i in range(10)] 198 | llen = len(lst) 199 | for d in range(-1, -r-1, -1): 200 | for j in range(llen): 201 | rlists[lst[j].key[d]].append(lst[j]) 202 | j = 0 203 | for i in range(10): 204 | tmp = rlists[i] 205 | for k in range(len(tmp)): 206 | lst[j] = tmp[k] 207 | j += 1 208 | rlists[i].clear() 209 | 210 | 211 | def test6(n): 212 | lst = [Record(tuple((randrange(10) for j in range(3))), 213 | i) for i in range(n)] 214 | printR(lst) 215 | radix_sort(lst, 3) 216 | printR(lst) 217 | print() 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | if __name__ == '__main__': 229 | test6(15) 230 | 231 | test6(19) 232 | -------------------------------------------------------------------------------- /Reference/progs/stack_app3.py: -------------------------------------------------------------------------------- 1 | """ 栈应用3:背包问题 """ 2 | # from stack_list import * 3 | # from queue_list import * 4 | 5 | ########## Functions for Knap ########## 6 | 7 | weight_list = (1, 4, 19, 14, 7, 9, 17, 29) 8 | 9 | 10 | def knap_rec(weight, wlist, n): 11 | if weight == 0: 12 | return True 13 | if weight < 0 or (weight > 0 and n < 1): 14 | return False 15 | if knap_rec(weight - wlist[n-1], wlist, n-1): 16 | print("Item " + str(n) + ":", wlist[n-1]) 17 | return True 18 | if knap_rec(weight, wlist, n-1): 19 | return True 20 | else: 21 | return False 22 | 23 | if __name__ == "__main__": 24 | for w in range(6, 100, 17): 25 | print("Weight: ", w) 26 | ok = knap_rec(w, weight_list, len(weight_list)) 27 | if not ok: 28 | print("No solution.") 29 | print("") 30 | -------------------------------------------------------------------------------- /Reference/progs/stack_linked.py: -------------------------------------------------------------------------------- 1 | """ 基于链接表概念(链接结点)实现的栈类 """ 2 | 3 | from list_node import LNode 4 | 5 | 6 | class StackUnderflow(ValueError): 7 | pass 8 | 9 | 10 | class LStack(): # stack implemented as a linked node list 11 | def __init__(self): 12 | self._top = None 13 | 14 | def is_empty(self): 15 | return self._top is None 16 | 17 | def top(self): 18 | if self._top is None: 19 | raise StackUnderflow 20 | return self._top.elem 21 | 22 | def push(self, elem): 23 | self._top = LNode(elem, self._top) 24 | 25 | def pop(self): 26 | if self._top is None: 27 | raise StackUnderflow 28 | e = self._top.elem 29 | self._top = self._top.next 30 | return e 31 | 32 | 33 | if __name__ == '__main__': 34 | from random import randint 35 | st1 = LStack() 36 | 37 | for i in range(10): 38 | st1.push(randint(1, 20)) 39 | 40 | print(st1.pop()) 41 | st1.pop() 42 | st1.push(20) 43 | st1.push(100) 44 | while not st1.is_empty(): 45 | print(st1.pop()) 46 | -------------------------------------------------------------------------------- /Reference/progs/stack_list.py: -------------------------------------------------------------------------------- 1 | """ 基于 Python list 实现的栈类 2 | """ 3 | 4 | 5 | class StackUnderflow(ValueError): 6 | pass 7 | 8 | 9 | class SStack(): 10 | def __init__(self): 11 | self._elems = [] 12 | 13 | def is_empty(self): 14 | return not self._elems 15 | 16 | def top(self): 17 | if not self._elems: 18 | raise StackUnderflow 19 | return self._elems[-1] 20 | 21 | def push(self, elem): 22 | self._elems.append(elem) 23 | 24 | def pop(self): 25 | if not self._elems: 26 | raise StackUnderflow 27 | return self._elems.pop() 28 | 29 | 30 | if __name__ == '__main__': 31 | st = SStack() 32 | st.push(1) 33 | st.push(5) 34 | print(st.pop()) 35 | print(st.top()) 36 | print(st.pop()) 37 | print(st.is_empty()) 38 | st.top() 39 | -------------------------------------------------------------------------------- /Reference/progs/strings.py: -------------------------------------------------------------------------------- 1 | """ 字符串匹配算法 """ 2 | 3 | # Naive string matching 4 | 5 | 6 | def naive_matching(t, p): 7 | m, n = len(p), len(t) 8 | i, j = 0, 0 9 | while i < m and j < n: # i==m means a matching 10 | if p[i] == t[j]: # ok! consider next char in p 11 | i, j = i + 1, j + 1 12 | else: # no! consider next position in t 13 | i, j = 0, j - i + 1 14 | if i == m: # find a matching, return its index 15 | return j - i 16 | return -1 # no matching, return special value 17 | 18 | 19 | ## KMP string matching 20 | 21 | 22 | def gen_pnext0(p): 23 | """生成针对p中各位置i的下一检查位置表,用于KMP算法""" 24 | i, k, m = 0, -1, len(p) 25 | pnext = [-1] * m 26 | while i < m-1: # generate pnext[i+1] 27 | if k == -1 or p[i] == p[k]: 28 | i, k = i+1, k+1 29 | pnext[i] = k # set a pnext entry 30 | else: 31 | k = pnext[k] 32 | return pnext 33 | 34 | 35 | def gen_pnext(p): 36 | """生成针对p中各位置i的下一检查位置表,用于KMP算法, 37 | 有稍许修改的优化版本. 38 | """ 39 | i, k, m = 0, -1, len(p) 40 | pnext = [-1] * m 41 | while i < m-1: # 生成下一个pnext元素 42 | if k == -1 or p[i] == p[k]: 43 | i, k = i+1, k+1 44 | if p[i] == p[k] : 45 | pnext[i] = pnext[k] 46 | else: 47 | pnext[i] = k 48 | else: 49 | k = pnext[k] 50 | return pnext 51 | 52 | 53 | def matching_KMP(t, p, pnext): 54 | """ KMP字符串匹配, 主函数.""" 55 | j, i = 0, 0 56 | n, m = len(t), len(p) 57 | while j < n and i < m: # i==m说明找到匹配 58 | if i == -1 or t[j] == p[i]: # 考虑p中下一字符 59 | j, i = j+1, i+1 60 | else: # 失败! 考虑由pnext确定的字符 61 | i = pnext[i] 62 | if i == m: # 找到匹配, 返回其下标 63 | return j-i 64 | return -1 # 不存在匹配, 返回特殊值 65 | 66 | 67 | def KMP_matching(t, p): 68 | """ KMP字符串匹配的另一个版本, 稍许修改(非本质). 69 | 将gen_pnext定义为局部函数. 70 | """ 71 | def gen_pnext(p): 72 | """生成p中各i的下一检查位置表,稍许优化版本.""" 73 | i, k, m = 0, -1, len(p) 74 | pnext = [-1] * m 75 | while i < m-1: # generate pnext[i+1] 76 | if k == -1 or p[i] == p[k]: 77 | i, k = i+1, k+1 78 | if p[i] == p[k] : 79 | pnext[i] = pnext[k] 80 | else: 81 | pnext[i] = k 82 | else: 83 | k = pnext[k] 84 | return pnext 85 | 86 | j, i = 0, 0 87 | n, m = len(t), len(p) 88 | pnext = gen_pnext(p) 89 | while j < n and i < m: # i==m means a matching 90 | while i >= 0 and t[j] != p[i]: 91 | i = pnext[i] 92 | j, i = j+1, i+1 93 | if i == m: # 找到匹配, 返回其下标 94 | return j-i 95 | return -1 # 不存在匹配, 返回特殊值 96 | 97 | 98 | def matching(t, p): 99 | return matching_KMP(t, p, gen_pnext0(p)) 100 | 101 | t = "aabababababbbbaababaaaababababbab" 102 | p = "abbab" 103 | 104 | t1 = "aabcbabcaabcabcaababcabbcaab" 105 | p1 = "abcaababc" 106 | 107 | p2 = "abcabcaaa" 108 | t2 = "abcabcaababcaababcabbabbcabcabcaaabccabccab" 109 | -------------------------------------------------------------------------------- /Reference/progs/tree-list.py: -------------------------------------------------------------------------------- 1 | """ 树实现为嵌套的 Python list 2 | """ 3 | 4 | 5 | class SubtreeIndexError(ValueError): 6 | pass 7 | 8 | 9 | def Tree(data, *subtrees): 10 | return [data].extend(subtrees) 11 | 12 | 13 | def is_empty_Tree(tree): 14 | return tree is None 15 | 16 | 17 | def root(tree): 18 | return tree[0] 19 | 20 | 21 | def subtree(tree, i): 22 | if i < 1 or i > len(tree): 23 | raise SubtreeIndexError 24 | return tree[i + 1] 25 | 26 | 27 | def set_root(tree, data): 28 | tree[0] = data 29 | 30 | 31 | def set_subtree(tree, i, subtree): 32 | if i < 1 or i > len(tree): 33 | raise SubtreeIndexError 34 | tree[i+1] = subtree 35 | 36 | 37 | 38 | if __name__ == '__main__': 39 | tree1 = Tree('+', 1, 2, 3) 40 | tree2 = Tree('*', tree1, 6, 8) 41 | 42 | print(tree1) 43 | print(tree2) 44 | 45 | -------------------------------------------------------------------------------- /assets/1553052998478.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553052998478.png -------------------------------------------------------------------------------- /assets/1553053035687.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553053035687.png -------------------------------------------------------------------------------- /assets/1553053092365.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553053092365.png -------------------------------------------------------------------------------- /assets/1553053098758.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553053098758.png -------------------------------------------------------------------------------- /assets/1553053131963.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553053131963.png -------------------------------------------------------------------------------- /assets/1553053168498.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553053168498.png -------------------------------------------------------------------------------- /assets/1553053196554.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/1553053196554.png -------------------------------------------------------------------------------- /assets/2011010219003441.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/2011010219003441.jpg -------------------------------------------------------------------------------- /assets/251730074203156-1553230031367.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/251730074203156-1553230031367.jpg -------------------------------------------------------------------------------- /assets/251730074203156.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/251730074203156.jpg -------------------------------------------------------------------------------- /assets/8394323_1307440587b6WG.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/8394323_1307440587b6WG.jpg -------------------------------------------------------------------------------- /assets/8394323_13074405906V6Q.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/8394323_13074405906V6Q.jpg -------------------------------------------------------------------------------- /assets/849589-20171015223238449-2146169197.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/849589-20171015223238449-2146169197.gif -------------------------------------------------------------------------------- /assets/849589-20171015224719590-1433219824.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/849589-20171015224719590-1433219824.gif -------------------------------------------------------------------------------- /assets/849589-20171015225645277-1151100000.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/849589-20171015225645277-1151100000.gif -------------------------------------------------------------------------------- /assets/849589-20171015230557043-37375010.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/849589-20171015230557043-37375010.gif -------------------------------------------------------------------------------- /assets/849589-20171015230936371-1413523412.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/849589-20171015230936371-1413523412.gif -------------------------------------------------------------------------------- /assets/849589-20180331170017421-364506073.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/849589-20180331170017421-364506073.gif -------------------------------------------------------------------------------- /assets/adjMat-1553007345454.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/adjMat-1553007345454.png -------------------------------------------------------------------------------- /assets/adjMat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/adjMat.png -------------------------------------------------------------------------------- /assets/adjlist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/adjlist.png -------------------------------------------------------------------------------- /assets/bfderive.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/bfderive.png -------------------------------------------------------------------------------- /assets/bst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/bst.png -------------------------------------------------------------------------------- /assets/bst_worstcase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/bst_worstcase.png -------------------------------------------------------------------------------- /assets/complete_binary_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/complete_binary_tree.png -------------------------------------------------------------------------------- /assets/digraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/digraph.png -------------------------------------------------------------------------------- /assets/full_binary_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/full_binary_tree.png -------------------------------------------------------------------------------- /assets/function_growth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/function_growth.png -------------------------------------------------------------------------------- /assets/partitionA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/partitionA.png -------------------------------------------------------------------------------- /assets/partitionB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/partitionB.png -------------------------------------------------------------------------------- /assets/perfect_binary_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/perfect_binary_tree.png -------------------------------------------------------------------------------- /assets/routeGraph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/waynerv/data-structures-and-algorithms-in-python/fa78eca47d8f6d29382172873cd0922c09fd9c5e/assets/routeGraph.png --------------------------------------------------------------------------------