├── 001快速排序.md ├── 002动态规划.md ├── 003树.md ├── 004二分查询.md ├── 005链表.md ├── 006栈.md ├── 007队列.md ├── 008背包问题.md ├── 009哈希.md ├── 010布隆过滤器.md ├── 011动态规划解决最大连续乘积子数组.md ├── 012归并排序.md ├── 013Trie树.md ├── 014LRU缓存.md ├── 015图.md ├── 016最少矩阵乘法次数.md ├── 017最长公共子字符串.md ├── 018堆排序.md ├── 019插入排序.md ├── LICENSE ├── README.md ├── arrange_combination └── arrange.py ├── breath_first_search ├── bidirectional_breath_first_search.py └── breath_first_search.py ├── code ├── queue.py └── quicksort.py └── lru.md /001快速排序.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现快速排序 3 | 4 | ## 什么是快速排序 5 | 6 | 快速排序是排序算法中的一种,排序效率比较高,是很常见的算法。 7 | 8 | 算法基本思路是分而治之,先拿第一个数,比它小的数放在左边新的数组,比它大的数放在右边新的数组,然后左右数组继续进行快速排序,最终可以得到排序后的数组。 9 | 10 | ## 如何实现快速排序 11 | 12 | 无论是C、Java还是Python,网上都有大量实现代码,主要是通过代码实现上述的逻辑。 13 | 14 | 但最终代码是比较复杂的,例如创建左指针和右指针,左右挪,如果这边有大数和右边有小数就交换,很不直观。 15 | 16 | 因为Python支持列表表达式,可以轻易创建符合条件的数组,因此用Python实现与自然语言描述比较接近。 17 | 18 | 19 | ## 用Python实现快速排序 20 | 21 | 首先我们用iPython一步步实现快速排序。 22 | 23 | ``` 24 | ipython 25 | ``` 26 | 27 | 创建一个普通数组并打印出来,预期结果应该是[1,2,3,4,5]。 28 | 29 | ``` 30 | a = [3, 2, 1, 5, 4] 31 | print(a) 32 | ``` 33 | 34 | 实现一个普通函数,能够接受一个数组,最后返回一个数组,为了简化我们先将输入直接返回出去。 35 | 36 | ``` 37 | def quicksort(a): 38 | return a 39 | 40 | quicksort(a) 41 | # Print [3, 2, 1, 5, 4] 42 | ``` 43 | 44 | 按照快速排序算法描述,我们先取第一个数,然后利用列表表达式获取比这个数小的数组和比这个数大的数组。 45 | 46 | ``` 47 | def quicksort(a): 48 | first = a[0] 49 | 50 | left_array = [x for x in a[1:] if x <= a[0]] 51 | print(left_array) 52 | 53 | right_array = [x for x in a[1:] if x > a[0]] 54 | print(right_array) 55 | 56 | return a 57 | 58 | quicksort(a) 59 | # [3, 2, 1, 5, 4] 60 | ``` 61 | 62 | 然后第一轮,我们就可以组装成新的数组,这个数组中间的元素是a[0],左边都比它小,右边都比它大。注意这里是a[1:]而不是a。 63 | 64 | ``` 65 | def quicksort(a): 66 | return [x for x in a[1:] if x < a[0]] + [a[0]] + [x for x in a[1:] if x > a[0]] 67 | 68 | quicksort(a) 69 | # Print [2, 1, 3, 5, 4] 70 | ``` 71 | 72 | 算法其实是对左边和右边的数组再进行快排调用quicksort()即可,但这样没有结束条件,因此我们考虑如果数组长度为1时就直接返回,因为有可能为0这个边界条件也需要考虑。 73 | 74 | ``` 75 | def quicksort(a): 76 | if len(a) <=1: 77 | return a 78 | else: 79 | return quicksort([x for x in a[1:] if x < a[0]]) + [a[0]] + quicksort([x for x in a[1:] if x > a[0]]) 80 | 81 | quicksort(a) 82 | # Print [1, 2, 3, 4, 5] 83 | ``` 84 | 85 | 这是本章内容,希望对你有所帮助。[进入下一章](./002动态规划.md) 86 | -------------------------------------------------------------------------------- /002动态规划.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现动态规划 3 | 4 | ## 什么是动态规划 5 | 6 | 动态规划其实是分治法的一种,例如一个大问题可以分解为很多个小问题,如果小问题有重复的,我们就可以复用以前的结果避免重复计算,这样就称为动态规划。 7 | 8 | ## 什么场景可以用动态规划 9 | 10 | 动态规划要求问题能分而治之,而且有相同子问题,因此求最大连续子数组和就是其中之一。 11 | 12 | 这个问题表示给一个数组,如[1, -2, 3, 10, -4, 7, 2, -5],通过算法得到最大的子数组是[3, 10, -4, 7, 2],从而得到结果是18。 13 | 14 | ## 整理算法实现思路 15 | 16 | 算法思路是这样的,假设我们知道第i-1个结果是什么,那么第i也能推倒出来,无非只有下面两种情况。 17 | 18 | ``` 19 | b[i] = b[i-1] + a[i],当b[i-1]>0时,这时候的b[i]中包含a[i]。 20 | 21 | b[i] = a[i],当b[i-1]<=0,这时候以a[i]重新作为b[i]的起点。 22 | 23 | b[i]不包含a[i]的情况,这种情况在计算b[i]之前已经计算处结果,保存在b[0~i-1]中。最后计算max{b[i]}时会考虑到。 24 | ``` 25 | 26 | 因此其实b[i] = max{b[i-1]+a[i],a[i]},也就是我们一直记录子数组和最大值,如果来了一个新的数,要么选择加上这个数,要么从这个数从新开始,在这个过程中需要一直记录全局最大值和当前最大值b[i]。 27 | 28 | ## 通过代码实现算法 29 | 30 | 首先进入ipython,并构建一个数组。 31 | 32 | ``` 33 | a = [1, -2, 3, 10, -4, 7, 2, -5] 34 | ``` 35 | 36 | 然后定义函数,来实现这个算法,初始化最终的结果,还有一个currentSum记录以当前元素结尾的最大值(要么是前面的b[i-1] + a[i],要么是a[i])。 37 | 38 | ``` 39 | def max_subarray(a): 40 | result = a[0] 41 | currentSum = a[0] 42 | 43 | return result 44 | 45 | a = [1, -2, 3, 10, -4, 7, 2, -5] 46 | max_subarray(a) 47 | 48 | # Print 1 49 | ``` 50 | 51 | 接着我们写for循环,注意第一个变量直接跳过。 52 | 53 | ``` 54 | def max_subarray(a): 55 | result = a[0] 56 | currentSum = a[0] 57 | 58 | for i in range(1, len(a)): 59 | print(i) 60 | 61 | return result 62 | 63 | a = [1, -2, 3, 10, -4, 7, 2, -5] 64 | max_subarray(a) 65 | 66 | # Print 1 67 | ``` 68 | 69 | 记住我们的算法就是当前值如果加上大就用加上的,否则就用新的,然后与全局最终结果比较即可。 70 | 71 | 72 | ``` 73 | def max_subarray(a): 74 | result = a[0] 75 | currentSum = a[0] 76 | 77 | for i in range(1, len(a)): 78 | 79 | currentSum = max(currentSum + a[i], a[i]) 80 | result = max(result, currentSum) 81 | 82 | return result 83 | 84 | a = [1, -2, 3, 10, -4, 7, 2, -5] 85 | max_subarray(a) 86 | 87 | # Print 18 88 | ``` 89 | 90 | 91 | 这是本章内容,希望对你有所帮助。[进入下一章](./003树.md) -------------------------------------------------------------------------------- /003树.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现树的数据结构 3 | 4 | ## 什么是树 5 | 6 | 树是计算机里非常重要的数据机构,与数组、链表类似,类比于现实中的树,树的数据结构有根节点、叶子节点等。树其实是很大的抽象,我们根据结构还可以分为二叉树、红黑树等,这里我们将介绍和时间较常见和易入门的几种。 7 | 8 | 9 | ## 如何实现树的数据结构 10 | 11 | 任何通用编程语言都可以实现树,不过不同语言实现难度不同,但基本思想是一样的。 12 | 13 | 在面向对象的设计中,首先要定义Node类,然后可以定义Tree类并实现遍历等函数。 14 | 15 | ## 使用Python实现通用树 16 | 17 | 首先进入ipython开始编写代码,按之前讨论先定义一个类。 18 | 19 | ``` 20 | class Node(ojbect): 21 | 22 | def __init__(self): 23 | print("Init the clas") 24 | 25 | node = Node() 26 | ``` 27 | 28 | 我们设计一个节点,首先应该包含一个数据,然后还包含一系列子节点,用数组来存储。 29 | 30 | ``` 31 | class Node(object): 32 | 33 | def __init__(self, data): 34 | self.data = data 35 | self.children = [] 36 | 37 | def add_child(self, Node): 38 | self.children.append(Node) 39 | 40 | node = Node("node1") 41 | node2 = Node("node2") 42 | 43 | node.add_child(node2) 44 | ``` 45 | 46 | 接着我们可以考虑实现Tree类,也是用数组来保存一系列Node。 47 | 48 | ``` 49 | class Tree(object): 50 | 51 | def __init__(self): 52 | self.nodes = [] 53 | 54 | tree = Tree() 55 | ``` 56 | 57 | ## 如何实现遍历二叉树 58 | 59 | 遍历树的方法有多种,深度优先或广度优先,为了简化代码我们通过实现二叉树和二叉树的遍历来演示。 60 | 61 | 首先每个二叉树都有两个节点,因此我们定义的Node类与前面有些差异,而Tree类暂时只需要记录root的节点。 62 | 63 | ``` 64 | class BinaryNode(object): 65 | 66 | def __init__(self, data, left=None, right=None): 67 | self.data = data 68 | self.left = left 69 | self.right = right 70 | 71 | 72 | class BinaryTree(object): 73 | 74 | def __init__(self): 75 | self.root = None 76 | 77 | 78 | tree = BinaryTree() 79 | ``` 80 | 81 | 然后我们考虑支持插入数据,这是要保证数据比较小的在左节点,数据比较大的在右节点。注意插入的时候我们是递归实现,因此其实需要多加一个root参数的。 82 | 83 | ``` 84 | class BinaryTree(object): 85 | 86 | def __init__(self): 87 | self.root = None 88 | 89 | def add_node(self, root, data): 90 | if self.root == None: 91 | self.root = BinaryNode(data) 92 | else: 93 | if root is None: 94 | root = BinaryNode(data) 95 | elif data <= root.data: 96 | root.left = self.add_node(root.left, data) 97 | elif data > root.data: 98 | root.right = self.add_node(root.right, data) 99 | return root 100 | 101 | tree = BinaryTree() 102 | tree.add_node(tree.root, 10) 103 | tree.add_node(tree.root, 5) 104 | tree.add_node(tree.root, 15) 105 | tree.add_node(tree.root, 20) 106 | ``` 107 | 108 | 然后要实现遍历,从左节点到右节点深度遍历,这样就比较简单了。 109 | 110 | ``` 111 | def pre_travel(self, root): 112 | if root != None: 113 | if root.left != None: 114 | self.pre_travel(root.left) 115 | print(root.data) 116 | if root.right != None: 117 | self.pre_travel(root.right) 118 | 119 | 120 | tree = BinaryTree() 121 | tree.add_node(tree.root, 10) 122 | tree.add_node(tree.root, 5) 123 | tree.add_node(tree.root, 15) 124 | tree.add_node(tree.root, 20) 125 | 126 | tree.pre_travel(tree.root) 127 | # Print [5, 10, 15, 20] 128 | ``` 129 | 130 | 这是本章内容,希望对你有所帮助。[进入下一章](./004二分查询.md) -------------------------------------------------------------------------------- /004二分查询.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现二分查询 3 | 4 | ## 什么是二分查询 5 | 6 | 二分查询是比遍历查询更高效的查询方法,类比现实中的猜数字游戏,如果每次都从中间猜起可以尽可能快速地定位到数据。 7 | 8 | 注意二分查询要求数据已经排好序。 9 | 10 | ## 如何实现二分查询 11 | 12 | 实现二分查询的原理比较简单,先与中间的数比较,如果比它小就在左边查询,如果比它大就在右边查询,而查询方法是一样的,知道无法划分左右。 13 | 14 | 这种原理可以最直观的是通过递归实现,稍微不太直观的可以用移动数组的左右边界达到同样的目的。 15 | 16 | ## 使用递归实现 17 | 18 | 首先创建测试数组和测试数据,并且创建好二分查询的函数。 19 | 20 | ``` 21 | def binary_search(a, x): 22 | return x 23 | 24 | 25 | a = [1, 2, 3, 5, 55, 66, 77, 88, 99, 100] 26 | x = 2 27 | binary_search(a, x) 28 | ``` 29 | 30 | 由于我们要递归实现,因此binary_search的函数其实还需要start和end两个参数,然后就是递归调用。 31 | 32 | ``` 33 | def binary_search(a, x, start, end): 34 | if end < start: 35 | return -1 36 | 37 | middle = start + int((end - start) / 2) 38 | 39 | if x > a[middle]: 40 | binary_search(a, x, middle + 1, end) 41 | elif x < a[middle]: 42 | binary_search(a, x, start, middle - 1) 43 | else: 44 | return middle 45 | 46 | a = [1, 2, 3, 5, 55, 66, 77, 88, 99, 100] 47 | x = 2 48 | 49 | binary_search(a, x, 0, len(a)) 50 | # Print 1 51 | ``` 52 | 53 | ## 使用循环实现 54 | 55 | 拓展一下,如果想用循环来实现,只需要记录start和end即可,首先初始化这些变量。 56 | 57 | ``` 58 | def binary_search(a, x, start, end): 59 | while start <= end: 60 | middle = start + int((end - start)/2) 61 | if x > a[middle]: 62 | start = middle + 1 63 | elif x < a[middle]: 64 | end = middle - 1 65 | else: 66 | return middle 67 | 68 | a = [1, 2, 3, 5, 55, 66, 77, 88, 99, 100] 69 | x = 2 70 | binary_search(a, x, 0, len(a)) 71 | # Print 1 72 | ``` 73 | 74 | 这是本章内容,希望对你有所帮助。[进入下一章](./005链表.md) 75 | -------------------------------------------------------------------------------- /005链表.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现链表数据结构 3 | 4 | ## 什么是链表 5 | 6 | 链表是常用的数据机构之一,其种类也非常多,包括单向链表、双向链表等。这里会使用Python实现简单或通用的链表数据结构。 7 | 8 | ## 如何实现单向链表 9 | 10 | 首先是定义链表的Node,我们设计链表除了自己的数据外,还包含一个next对象。 11 | 12 | ``` 13 | class Node(object): 14 | 15 | def __init__(self, data, next=None): 16 | self.data = data 17 | self.next = next 18 | 19 | 20 | node = Node("node1") 21 | ``` 22 | 23 | 然后我们就可以实现List类,主要是包含head的节点,顺便实现遍历的方法。 24 | 25 | ``` 26 | class List(object): 27 | 28 | def __init__(self, head=None): 29 | self.head = head 30 | 31 | def add_node(self, node, data): 32 | if node != None: 33 | new_node = Node(data) 34 | node.next = new_node 35 | return new_node 36 | 37 | def travel(self, node): 38 | if node != None: 39 | print(node.data) 40 | if node.next != None: 41 | self.travel(node.next) 42 | 43 | head = Node("node1") 44 | list = List(head) 45 | node2 = list.add_node(head, "node2") 46 | node3 = list.add_node(node2, "node3") 47 | list.travel(head) 48 | ``` 49 | 50 | 51 | 这是本章内容,希望对你有所帮助。[进入下一章](./006栈.md) 52 | -------------------------------------------------------------------------------- /006栈.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现栈数据结构 3 | 4 | ## 什么是栈 5 | 6 | 栈也是非常常见的数据结构,类比现实中的木桶,特点是先进后出,也就是每次插入都在最上面,每次取都取最上面的。 7 | 8 | 因此栈的操作比较特别,只需要实现push和pop的接口即可。 9 | 10 | ## 如何实现栈 11 | 12 | 实际上,Python已经提供了数组,已经提供append和pop接口,能够满足栈的使用场景,用法也简单介绍下。 13 | 14 | ``` 15 | a = [1, 2, 3] 16 | 17 | a.append(4) 18 | 19 | print(a) 20 | # Print [1, 2, 3, 4] 21 | 22 | a.pop() 23 | 24 | print(a) 25 | # Print [1, 2, 3] 26 | ``` 27 | 28 | 但我们希望自己实现栈这个数据结构,首先是定义Node类和Stack类。 29 | 30 | ``` 31 | class Node(object): 32 | 33 | def __init__(self, data, next=None): 34 | self.data = data 35 | self.next = next 36 | 37 | class Stack(object): 38 | 39 | def __init__(self, top=None): 40 | self.top = top 41 | 42 | def is_empty(self): 43 | pass 44 | 45 | def push(self, data): 46 | pass 47 | 48 | def pop(self): 49 | pass 50 | 51 | stack = Stack() 52 | ``` 53 | 54 | 然后我们需要去实现push、pop和is_empty这几个函数。其中push和pop需要注意如果top为空的情况。 55 | 56 | ``` 57 | class Stack(object): 58 | 59 | def __init__(self, top=None): 60 | self.top = top 61 | 62 | def is_empty(self): 63 | if self.top != None: 64 | return True 65 | else: 66 | return False 67 | 68 | def push(self, data): 69 | if self.top == None: 70 | self.top = Node(data) 71 | else: 72 | new_node = Node(data) 73 | new_node.next = self.top 74 | self.top = new_node 75 | 76 | def pop(self): 77 | if self.top == None: 78 | print("No node in stack") 79 | return 80 | else: 81 | top = self.top 82 | self.top = self.top.next 83 | return top 84 | 85 | stack = Stack() 86 | stack.push("node1") 87 | stack.push("node2") 88 | stack.push("node3") 89 | print(stack.pop().data) 90 | # Print "node3" 91 | ``` 92 | 93 | 这是本章内容,希望对你有所帮助。[进入下一章](./007队列.md) -------------------------------------------------------------------------------- /007队列.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现队列数据结构 3 | 4 | ## 什么是队列 5 | 6 | 队列和栈类似都是很常见的数据结构,和现实中排队对应,都先进先出的模型。 7 | 8 | 因此队列这个数据结构,提供的接口是enqueue和dequeue。 9 | 10 | ## 如何实现 11 | 12 | 阅读前建议先看看[006栈](./006栈.md)的数据结构介绍,我们首先要定义的Node类和前面定义的是一样的,并且要定义Queue类的基本接口,注意这时应该有left和right两个节点引用(假设是左进右出)。 13 | 14 | ``` 15 | class Node(object): 16 | 17 | def __init__(self, data, next=None): 18 | self.data = data 19 | self.next = next 20 | 21 | class Queue(object): 22 | 23 | def __init__(self, left=None, right=None): 24 | self.left = left 25 | self.right = right 26 | 27 | def is_empty(self): 28 | pass 29 | 30 | def enqueue(self, data): 31 | pass 32 | 33 | def dequeue(self): 34 | pass 35 | ``` 36 | 37 | 然后实现这些函数,发现如果每个node之前前后的node会更容易实现些,当然也是牺牲空间换时间。 38 | 39 | ``` 40 | class Node(object): 41 | 42 | def __init__(self, data, next=None): 43 | self.data = data 44 | self.next = next 45 | 46 | class Queue(object): 47 | 48 | def __init__(self, left=None, right=None): 49 | self.left = left 50 | self.right = right 51 | 52 | def is_empty(self): 53 | if self.left != None: 54 | return True 55 | else: 56 | return False 57 | 58 | def enqueue(self, data): 59 | if self.left == None and self.right == None: 60 | new_node = Node(data) 61 | self.left = new_node 62 | self.right = new_node 63 | else: 64 | new_node = Node(data) 65 | new_node.next = self.left 66 | self.left = new_node 67 | 68 | def dequeue(self): 69 | if self.left == None and self.right == None: 70 | print("No node in queue") 71 | return 72 | else: 73 | node = self.right 74 | # TODO: Need to find the right node for self.right 75 | return node 76 | 77 | queue = Queue() 78 | queue.enqueue("node1") 79 | queue.enqueue("node2") 80 | queue.enqueue("node3") 81 | print(queue.dequeue().data) 82 | # Print "node1" 83 | ``` 84 | 85 | 86 | 这是本章内容,希望对你有所帮助。[进入下一章](./008背包问题.md) 87 | -------------------------------------------------------------------------------- /008背包问题.md: -------------------------------------------------------------------------------- 1 | 2 | # 如何解决背包问题 3 | 4 | ## 什么是背包问题 5 | 6 | 背包问题来源于生活,例如我们有一个容量有限的背包,现在有价格和重量不同的物品,如何组合才能让背包包含的物品总价格最大。如果每种物品只能选0次或1次,这样归纳为0-1背包问题。 7 | 8 | 为了简化,我们只考虑无界背包问题,也就是不限制物品的数量,如果要限制就是有界背包问题了。 9 | 10 | ## 背包问题算法设计 11 | 12 | 这也是一个动态规划问题,是否放置第i个物品,需要取决于背包中已经考虑的i-1时的所有情况,而与前面最大子数组和不同的是,这需要考虑i和容量二维变量。 13 | 14 | 我们假设物品数量是number,背包总容量是capacity,第i件物体价值(value)和重量(weight)分别是Vi、Wi,然后第i件物理的价值(result)是Ri。 15 | 16 | 唯一的算法就用数学表达就是R[i,w] = max{ R[i-1,w], R[i-i,w-Wi] + Vi },因此我们需要定义变量来记录每个i和每个剩余容量v时的值,这些子集结果可以复用因此适合动态规划的场景。 17 | 18 | 算法应该是这样,定义一个二维数组保存中间结果,for循环遍历物品i,for循环遍历剩余容量v,里面记录背包当时的最大价值。 19 | 20 | ## 如何编码解决背包问题 21 | 22 | 首先我们来定义这些题目的变量,物品数量是number,背包容量是capacity,重量的数组是weight_array,收益的数组是value_array。 23 | 24 | ``` 25 | number = 5 26 | capacity = 10 27 | weight_array=[2,2,6,5,4] 28 | value_array=[6,3,5,4,6] 29 | 30 | def zero_one_pack(n, c, w, v): 31 | # n is number 32 | # c is capacity 33 | # w is weight_array 34 | # v is value_array 35 | pass 36 | 37 | zero_one_pack(number, capacity, weight_array, value_array) 38 | # Print nothing 39 | ``` 40 | 41 | 然后开始两个循环,首先循环i个物品,然后循环w的剩余空间,注意需要判断是否还有空间,如果没有空间只能和以前一样,如果有剩余空间就max一下。最后的result其实是一个二维数组,第一位是第i个物品,第二是剩余空间w,而值是那种情况下的背包最大价值。 42 | 43 | 44 | ``` 45 | number = 5 46 | capacity = 10 47 | weight_array=[2,2,6,5,4] 48 | value_array=[6,3,5,4,6] 49 | 50 | def zero_one_pack(n, c, w, v): 51 | # n is number 52 | # c is capacity 53 | # w is weight_array 54 | # v is value_array 55 | 56 | result=[[0 for j in range(c+1)] for i in range(n+1)] 57 | 58 | for i in range(1, n+1): 59 | for j in range(1, c+1): 60 | 61 | if j < w[i-1]: 62 | result[i][j] = result[i-1][j] 63 | else: 64 | result[i][j] = max(result[i-1][j], result[i-1][j-w[i-1]] + v[i-1]) 65 | 66 | return result 67 | 68 | zero_one_pack(number, capacity, weight_array, value_array) 69 | # Print the matrix of max value 70 | ''' 71 | [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 72 | [0, 0, 6, 6, 6, 6, 6, 6, 6, 6, 6], 73 | [0, 0, 6, 6, 9, 9, 9, 9, 9, 9, 9], 74 | [0, 0, 6, 6, 9, 9, 9, 9, 11, 11, 14], 75 | [0, 0, 6, 6, 9, 9, 9, 10, 11, 13, 14], 76 | [0, 0, 6, 6, 9, 9, 12, 12, 15, 15, 15]] 77 | ''' 78 | ``` 79 | 80 | 最后还遗留一个问题,怎样才知道放了什么东西进去,这个必须是逆推的。其实可以通过判断,如果因为这个i导致与i-1的不同,也就是相同capacity下背包的最大值不同,也就是这个i-1被选中了。这里需要一个循环遍历所有item,从头开始或者从最后开始都可以。 81 | 82 | ``` 83 | # Get the selected objects 84 | selection_array = [False for x in range(n)] 85 | capacity = c 86 | for i in range(1, n+1): 87 | if result[i][capacity] > result[i-1][capacity]: 88 | selection_array[i-1] = True 89 | capacity -= w[i-1] 90 | print(i-1) 91 | ``` 92 | 93 | 94 | 这是本章内容,希望对你有所帮助。[进入下一章](./009哈希.md) -------------------------------------------------------------------------------- /009哈希.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现哈希 3 | 4 | ## 什么是Hash 5 | 6 | 哈希值应该是一个对象里不变的属性,如果对对象的哈希可以快速比较两个对象是否是一样的,一般都是转变成数字然后才能高效得比较。 7 | 8 | ## 怎么用哈希 9 | 10 | Python自带了build-in的hash函数,可以对字符串进行哈希,自己实现的类也可以用,不过建议实现__hash__函数否则用内置的。 11 | 12 | ``` 13 | object = "abc" 14 | hash(object) 15 | ``` 16 | 17 | 当然如果感兴趣,一定要看看Python字符串是如何实现哈希函数的。 18 | 19 | ``` 20 | class string: 21 | def __hash__(self): 22 | if not self: 23 | return 0 # empty 24 | value = ord(self[0]) << 7 25 | for char in self: 26 | value = c_mul(1000003, value) ^ ord(char) 27 | value = value ^ len(self) 28 | if value == -1: 29 | value = -2 30 | return value 31 | ``` 32 | 33 | ## 如何实现自己的哈希 34 | 35 | 我们很多算法也用到哈希,可以实现自己的字符串到数字的哈希,如下。 36 | 37 | ``` 38 | def my_hash(s): 39 | result = 0 40 | for i in s: 41 | result += ord(i) 42 | 43 | return result 44 | 45 | print(my_hash("abc")) 46 | # Print 294 47 | ``` 48 | 49 | 50 | 这是本章内容,希望对你有所帮助。[进入下一章](./010布隆过滤器.md) -------------------------------------------------------------------------------- /010布隆过滤器.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现布隆过滤器 3 | 4 | ## 什么是布隆过滤器 5 | 6 | 布隆过滤器是一直支持快速查询的数据结构。 7 | 8 | ## 如何实现布隆过滤器 9 | 10 | 首先是定义多个hash函数,可以自己实现,主要是为了让字符串可以分配到不同的bit上。 11 | 12 | ``` 13 | def hash_one(s): 14 | result = 0 15 | for char in s: 16 | result += ord(char) % 1024 17 | return result 18 | 19 | def hash_two(s): 20 | result = 0 21 | for char in s: 22 | result += ord(char) ** 2 / 1024 23 | return result 24 | 25 | print(hash_one("foo")) 26 | # Print 324 27 | print(hash_two("324")) 28 | # Print 34 29 | ``` 30 | 31 | 然后初始化bitmap,当我们加入“foo”这个字符串时,就更新bitmap。 32 | 33 | ``` 34 | bitmap = [False for x in range(1024)] 35 | 36 | word = "foo" 37 | word_hash_one = hash_one(word) 38 | word_hash_two = hash_two(word) 39 | 40 | bitmap[word_hash_one] = True 41 | bitmap[word_hash_two] = True 42 | ``` 43 | 44 | 然后查询的话,同样使用相同的hash函数,对bitmap相应的bit进行判断即可。 45 | 46 | ``` 47 | def check_in_bloom_filter(bitmap, word): 48 | # bitmap is the bit map 49 | # word is the string 50 | 51 | hashs = [] 52 | hashs.append(hash_one(word)) 53 | hashs.append(hash_two(word)) 54 | 55 | for i in hashs: 56 | if bitmap[i] == False: 57 | return False 58 | return True 59 | 60 | print(check_in_bloom_filter(bitmap, "foo")) 61 | # Print True 62 | print(check_in_bloom_filter(bitmap, "bar")) 63 | # Print False 64 | ``` 65 | 66 | 这是本章内容,希望对你有所帮助。[进入下一章](./010布隆过滤器.md) 67 | 68 | -------------------------------------------------------------------------------- /011动态规划解决最大连续乘积子数组.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # 动态规划解决最大连续乘积子数组 4 | 5 | ## 动态规划介绍 6 | 7 | 这是承接之前[002动态规划](002动态规划.md)的章节,继续使用动态规划解决实际问题。 8 | 9 | ## 问题描述 10 | 11 | 最大连续乘积子数组问题,表示求一个数组的子数组,保证子数组的乘积最大。 12 | 13 | 与最大子数组和最大不同的是,乘积需要考虑正负,因此动态规划时需要把正负数的最大值都保存下来。 14 | 15 | 动态规划的设计思路是这样的,虽然背包问题也用动态规划会把中间结果保存到数组,但连续子字符串因为是连续的只需要保留最后的结果即可,通过一个for循环一直扫到尾,记录max和min,还有全局的一个result。 16 | 17 | ## 如何使用动态规划 18 | 19 | 首先第一步还是定义函数和变量,然后给一个测试用例准备。 20 | 21 | ``` 22 | def max_multiple_substring(a): 23 | # a for array data 24 | result = a[0] 25 | max = a[0] 26 | min = a[0] 27 | 28 | a = [-2.5, 4, 0, 3, 0.5, 8, -1] 29 | max_multiple_substring(a) 30 | ``` 31 | 32 | 然后可以写for循环,主要是判断max、min还有最后的result,唯一的算法就是要么是乘以后面的要么是新的。 33 | 34 | ``` 35 | def max_multiple_substring(a): 36 | # a for array data 37 | result = a[0] 38 | currentMax = a[0] 39 | currentMin = a[0] 40 | 41 | for i in range(1, len(a)): 42 | currentMax = max(currentMax * a[i], currentMin * a[i], a[i]) 43 | currentMin = min(currentMax * a[i], currentMin * a[i], a[i]) 44 | result = max(currentMax, currentMin, result) 45 | 46 | return result 47 | 48 | a = [-2.5, 4, 0, 3, 0.5, 8, -1] 49 | max_multiple_substring(a) 50 | # Print 12 51 | ``` 52 | 53 | 这是本章内容,希望对你有所帮助。[进入下一章](./012归并排序.md) -------------------------------------------------------------------------------- /012归并排序.md: -------------------------------------------------------------------------------- 1 | 2 | # 归并排序 3 | 4 | ## 什么是归并排序 5 | 6 | 归并排序是诸多排序算法中的一种,主要特点是能用于外部排序,也就是说如果内存不能承载全部排序数据时,可以用归并排序来实现。为了简单我们主要介绍二路归并。 7 | 8 | ## 如何实现归并排序 9 | 10 | 首先还是准备测试的数组,然后用递归来调用merge_sort函数。 11 | 12 | ``` 13 | def merge_sort(a): 14 | # a is the array 15 | 16 | if len(a) <= 1: 17 | return a 18 | 19 | middle = len(a) / 2 20 | left_array = merge_sort(a[0:middle]) 21 | right_array = merge_sort(a[middle:]) 22 | return merge(left_array, right_array) 23 | 24 | def merge(left_array, right_array): 25 | # left_array is the array 26 | # right_array is the array 27 | pass 28 | 29 | a = [3, 2, 2, 1, 5, 4] 30 | merge_sort(a) 31 | ``` 32 | 33 | 然后最重要的是merge函数,也就是如何合并两个已经排序的数组,这也是外部排序需要解决的。 34 | 35 | ``` 36 | def merge(left_array, right_array): 37 | # left_array is the array 38 | # right_array is the array 39 | 40 | result = [] 41 | left = 0 42 | right = 0 43 | 44 | while left < len(left_array) and right < len(ri\ 45 | ght_array): 46 | if left_array[left] <= right_array[right]: 47 | result.append(left_array[left]) 48 | left += 1 49 | else: 50 | result.append(right_array[right]) 51 | right += 1 52 | 53 | result += left_array[left:] 54 | result += right_array[right:] 55 | return result 56 | 57 | a = [3, 2, 2, 1, 5, 4] 58 | print(merge_sort(a)) 59 | # Print [1, 2, 2, 3, 4, 5] 60 | ``` 61 | 62 | 63 | 这是本章内容,希望对你有所帮助。[进入下一章](./013Trie树.md) -------------------------------------------------------------------------------- /013Trie树.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现Trie树 3 | 4 | ## 什么是Trie树 5 | 6 | Trie树是一种树的数据结构,又被称为字典树,非常适用于Ajax自动补全等场景,因为它通过空间换时间能极大提高特别字符串的查询速度。 7 | 8 | Tire字典树每条路径都包含一个字母,给定一个单词如“abc”,就可以通过a、b、c三次查询找到是否存在对应的数据,如果给定a、b就可以找到以“ab”开头剩下的字符串,而在叶子节点中包含特殊标志表示存在是否这个单词,因为它也可能只是另一个单词的一部分。 9 | 10 | ## 如何实现Trie树 11 | 12 | 首先是定义Trie树的类,内部数据结果其实可以用Python的字典来实现了。 13 | 14 | ``` 15 | class TrieTree(object): 16 | 17 | def __init__(self): 18 | self.tree = {} 19 | 20 | def add(self, word): 21 | pass 22 | 23 | def search(self, word): 24 | pass 25 | 26 | tree = TrieTree() 27 | tree.add("abc") 28 | tree.add("bcd") 29 | # Print nothing 30 | print(tree.search("ab")) 31 | # Print None 32 | print(tree.search("abc")) 33 | # Print None 34 | print(tree.search("abcd")) 35 | # Print None 36 | ``` 37 | 38 | 然后我们需要实现add函数,首先我们复制一个字典出来,然后是遍历word的所有字母,如果这个字母在字典中有,那么就继续下一个(通过移动字典),如果这个字母在字典中没有那么就加一个,同样继续下一个。注意最后还需要给叶子节点一些特殊的标记,例如多加一个key value。 39 | 40 | ``` 41 | class TrieTree(object): 42 | 43 | def __init__(self): 44 | self.tree = {} 45 | 46 | def add(self, word): 47 | tree = self.tree 48 | 49 | for char in word: 50 | if char in tree: 51 | tree = tree[char] 52 | else: 53 | tree[char] = {} 54 | tree = tree[char] 55 | 56 | tree['exist'] = True 57 | 58 | def search(self, word): 59 | pass 60 | 61 | tree = TrieTree() 62 | tree.add("abc") 63 | tree.add("bcd") 64 | # Print {'a': {'b': {'c': {'exist': True}}}, 'b': {'c': {'d': {'exist': True}}}} 65 | print(tree.search("abc")) 66 | # Print None 67 | print(tree.search("abc")) 68 | # Print None 69 | print(tree.search("abcd")) 70 | # Print None 71 | ``` 72 | 73 | 然后我们还要实现search函数,和前面add非常类似,这时只需要for循环找到那个标志即可。 74 | 75 | ``` 76 | class TrieTree(object): 77 | 78 | def __init__(self): 79 | self.tree = {} 80 | 81 | def add(self, word): 82 | tree = self.tree 83 | 84 | for char in word: 85 | if char in tree: 86 | tree = tree[char] 87 | else: 88 | tree[char] = {} 89 | tree = tree[char] 90 | 91 | tree['exist'] = True 92 | 93 | def search(self, word): 94 | tree = self.tree 95 | 96 | for char in word: 97 | if char in tree: 98 | tree = tree[char] 99 | else: 100 | return False 101 | 102 | if "exist" in tree and tree["exist"] == True: 103 | return True 104 | else: 105 | return False 106 | 107 | tree = TrieTree() 108 | tree.add("abc") 109 | tree.add("bcd") 110 | print(tree.tree) 111 | # Print {'a': {'b': {'c': {'exist': True}}}, 'b': {'c': {'d': {'exist': True}}}} 112 | print(tree.search("ab")) 113 | # Print False 114 | print(tree.search("abc")) 115 | # Print True 116 | print(tree.search("abcd")) 117 | # Print False 118 | ``` 119 | 120 | 这是本章内容,希望对你有所帮助。[进入下一章](./014LRU缓存.md) -------------------------------------------------------------------------------- /014LRU缓存.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现LRUN缓存 3 | 4 | ## 什么是LRU缓存 5 | 6 | LRU全称是Least Recently Used,也就是淘汰最晚使用的缓存技术,根据局部性原理,认为很久以前不用的数据最近也不会被使用。 7 | 8 | 我们可以用不同的数据结构如数组、链表来实现LRU缓存, 9 | 10 | ## 如何实现LRU缓存 11 | 12 | 要实现LRU,其实只需要实现set()和get()两个方法即可,并且保证数组容量超过时按照LRU的策略进行移除对象,这里有个trick就是get或update对象时先移除对象再加入到队列中。 13 | 14 | Python的collections库中包含OrderedDict,可以更容易得实现LRU缓存,但为了避免高级特性我们还是使用array和dictionary来实现。 15 | 16 | ``` 17 | class LRUCache(object): 18 | 19 | def __init__(self, capacity): 20 | """ 21 | :type capacity: int 22 | """ 23 | 24 | self.capacity = capacity 25 | # Example: ["k1", "k2"] 26 | self.keys = [] 27 | # Example: {"k1": "v1"}, {"k2": "v2"} 28 | self.data = {} 29 | 30 | 31 | def get(self, key): 32 | """ 33 | :rtype: int 34 | """ 35 | 36 | # If match, update the index and return the data 37 | if key in self.keys: 38 | self.keys.remove(key) 39 | self.keys.insert(0, key) 40 | return self.data[key] 41 | 42 | # If no match, just return -1 43 | else: 44 | return -1 45 | 46 | 47 | def set(self, key, value): 48 | """ 49 | :type key: int 50 | :type value: int 51 | :rtype: nothing 52 | """ 53 | 54 | # If there is the key 55 | if key in self.keys: 56 | self.data[key] = value 57 | self.keys.remove(key) 58 | self.keys.insert(0, key) 59 | 60 | # If there is no key and there's no capacity 61 | elif len(self.keys) >= self.capacity: 62 | self.keys.pop() 63 | self.data[key] = value 64 | self.keys.insert(0, key) 65 | 66 | # If there is no key and there is some capacity 67 | else: 68 | self.data[key] = value 69 | self.keys.insert(0, key) 70 | ``` 71 | 72 | 这是本章内容,希望对你有所帮助。[进入下一章](./015图.md) -------------------------------------------------------------------------------- /015图.md: -------------------------------------------------------------------------------- 1 | 2 | # 实现图数据结构 3 | 4 | ## 什么是图 5 | 6 | 图是类似树这样的数据结构,与树不同的是它可以没有根节点,而且根据类型不同可分为有向图和无向图等。 7 | 8 | 在现实中可能存在这样的图,满足以下条件。 9 | 10 | ``` 11 | A -> B 12 | A -> C 13 | B -> C 14 | B -> D 15 | C -> D 16 | D -> C 17 | E -> F 18 | F -> C 19 | ``` 20 | 21 | 这样的图用Python可以使用字典来表达这样的数据结构,如{A: [B, C], B: [C, D], C: [D], D: [C], E: [F], F: [C]}。 22 | 23 | ## 如何实现图 24 | 25 | 要实现图的遍历等功能,以上面的图为例,我们首先要定义数据结构和函数。因为要递归实现,我们需要把start、end传进去,而且还要给历史走过的path。 26 | 27 | ``` 28 | def find_path(graph, start, end, path=[]): 29 | pass 30 | 31 | def find_all_paths(graph, start, end, path=[]): 32 | pass 33 | 34 | def find_shortest_path(graph, start, end, path=[]): 35 | pass 36 | 37 | graph = {'a':['b', 'c'], 'b': ['c', 'd'], 'c': ['d'], 'd': ['c'], 'e': ['f'], 'f': ['c']} 38 | ``` 39 | 40 | 首先我们来实现find_path函数,用到了回溯法,从起点开始,如果起点就是终点那先返回,然后遍历每个子节点,注意要确保不是环路而且找了非空的路径才返回。 41 | 42 | ``` 43 | def find_path(graph, start, end, path=[]): 44 | path = path + [start] 45 | 46 | if start == end: 47 | return [path] 48 | 49 | for node in graph[start]: 50 | if node not in path: 51 | new_path = find_path(graph, node, end, path) 52 | if new_path: 53 | return new_path 54 | 55 | graph = {'a':['b', 'c'], 'b': ['c', 'd'], 'c': ['d'], 'd': ['c'], 'e': ['f'], 'f': ['c']} 56 | print(find_path(graph, 'a', 'd')) 57 | # Print [['a', 'b', 'c', 'd']] 58 | ``` 59 | 60 | 然后我们来实现find_all_paths函数, 61 | 62 | ``` 63 | def find_all_paths(graph, start, end, path=[]): 64 | path = path + [start] 65 | 66 | if start == end: 67 | return [path] 68 | 69 | all_paths = [] 70 | for node in graph[start]: 71 | if node not in path: 72 | new_paths = find_all_paths(graph, node, end, path) 73 | for new_path in new_paths: 74 | all_paths.append(new_path) 75 | 76 | return all_paths 77 | 78 | graph = {'a':['b', 'c'], 'b': ['c', 'd'], 'c': ['d'], 'd': ['c'], 'e': ['f'], 'f': ['c']} 79 | print(find_all_paths(graph, 'a', 'd')) 80 | # Print [['a', 'b', 'c', 'd'], ['a', 'b', 'd'], ['a', 'c', 'd']] 81 | ``` 82 | 83 | 最后来实现获取最短路径。 84 | 85 | ``` 86 | def find_shortest_path(graph, start, end, path=[]): 87 | path = path + [start] 88 | 89 | if start == end: 90 | return path 91 | 92 | result_path = None 93 | for node in graph[start]: 94 | if node not in path: 95 | new_path = find_shortest_path(graph, node, end, path) 96 | if result_path == None: 97 | result_path = new_path 98 | elif len(result_path) > len(new_path): 99 | result_path = new_path 100 | 101 | return result_path 102 | 103 | graph = {'a':['b', 'c'], 'b': ['c', 'd'], 'c': ['d'], 'd': ['c'], 'e': ['f'], 'f': ['c']} 104 | print(find_shortest_path(graph, 'a', 'd')) 105 | # Print ['a', 'b', 'd'] 106 | ``` 107 | 108 | 这是本章内容,希望对你有所帮助。[进入下一章](./015图.md) -------------------------------------------------------------------------------- /016最少矩阵乘法次数.md: -------------------------------------------------------------------------------- 1 | 2 | # 最少矩阵乘法次数 3 | 4 | 5 | 6 | ``` 7 | def multiple_matrix(): 8 | 9 | data = [6, 30, 35, 15, 5, 10, 20, 25] 10 | result = [[0 for x in data] for x in data] 11 | 12 | 13 | for r in range(2, len(data)): 14 | for i in range(1, len(data) - r + 2): 15 | j = i + r -1 16 | 17 | import sys 18 | result[i][j] = sys.maxint 19 | for k in (i, j): 20 | result[i][j] = min(result[i][j], result[i][k] + result[k+1][j] + data[i-1] * data[k] * data[\ 21 | j]) 22 | 23 | print(result) 24 | return result 25 | ``` -------------------------------------------------------------------------------- /017最长公共子字符串.md: -------------------------------------------------------------------------------- 1 | 2 | # 最长公共子字符串 3 | 4 | ## 算法核心 5 | 6 | 这是动态规划的常见场景。 7 | 8 | 如果a[i] = b[j],那么他们的的最长公共子字符串就是result[i-1, j-1] + 1,否则就是max(result[i-1, j], result[i, j-1]),如果i = 0或j = 0那么result[i, j]就是0。 9 | 10 | 因此算法也比较好理解,也很好实现。 11 | 12 | ## 实现最长公共字符串 13 | 14 | ``` 15 | def max_sub_string(a, b): 16 | # a, the first string 17 | # b, the second string 18 | 19 | result = [[0 for x in range(len(a)+1)] for x in range(len(b)+1)] 20 | 21 | for i in range(len(a)): 22 | for j in range(len(b)): 23 | 24 | if i == 0 or j == 0: 25 | result[i][j] = 0 26 | elif a[i] == b[j]: 27 | result[i+1][j+1] = result[i][j] + 1 28 | else: 29 | result[i+1][j+1] = max(result[i][j+1], result[i+1][j]) 30 | 31 | print(result) 32 | return result 33 | 34 | a = 'ABCBDAB' 35 | b = 'BDCABA' 36 | max_sub_string(a, b) 37 | ``` -------------------------------------------------------------------------------- /018堆排序.md: -------------------------------------------------------------------------------- 1 | 2 | # 堆排序 3 | 4 | ## 什么是堆 5 | 6 | 堆是一种树形数据结构,而且数据保存在数组中。而且堆非常适用于实现优先队列。 7 | 8 | ## 什么是堆排序 9 | 10 | 堆排序利用建堆等操作,可以实现较为高效的排序。 11 | 12 | 实现堆排序需要实现shift down、建堆等操作。 13 | 14 | ## 如何实现建堆 15 | 16 | 首先是从后往前,首先找到第n / 2这位,它肯定是树的倒数第二排,然后开始交换,把最大数换过来,如果交换了的话下面那个节点也得考虑再交换一次。这样一步步往前推就可以实现建堆了。 17 | 18 | 首先定义创建堆的函数,就是传入个数组。注意中间值,也就是开始的节点是middle,而且要倒叙执行。 19 | 20 | ``` 21 | def build_heap(a): 22 | # a, the array 23 | 24 | middle = len(a) / 2 25 | for i in range(middle)[::-1]: 26 | print(i) 27 | ``` 28 | 29 | 然后是交换位置,而且要继续排序,这就需要传入数组和位置i了。 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /019插入排序.md: -------------------------------------------------------------------------------- 1 | 2 | # 如何实现插入排序 3 | 4 | 5 | ## 什么是插入排序 6 | 7 | 插入排序和我们玩扑克牌的策略差不多,先拿第一个数,然后后面每个数都调位置加入,如果找到位置后,后面的书都相应往前移。 8 | 9 | ## 如何实现插入排序 10 | 11 | 12 | ``` 13 | 14 | ``` -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Tobe Algorithm Manual 3 | 4 | 通过Python记录各种算法的实现原理,由浅入深一步步分析编码步骤。 5 | 6 | [开始学习](./001快速排序.md) 7 | -------------------------------------------------------------------------------- /arrange_combination/arrange.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def range(input_list, step): 4 | 5 | if step == 3: 6 | print(input_list) 7 | return 8 | 9 | for i in range(step, len(input_list)): 10 | input_list[step], input_list[i] = input_list[i], input_list[step] 11 | range(input_list, step+1) 12 | input_list[step], input_list[i] = input_list[i], input_list[step] 13 | 14 | 15 | def main(): 16 | import ipdb;ipdb.set_trace() 17 | input_list = ["a", "b", "c"] 18 | range(input_list, 0) 19 | 20 | if __name__ == "__main__": 21 | main() 22 | -------------------------------------------------------------------------------- /breath_first_search/bidirectional_breath_first_search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from collections import deque 4 | 5 | 6 | class BreathFirstSearchGame(object): 7 | def __init__(self): 8 | # The node index are from 0 to 7, such as 0, 1, 2, 3, 4 9 | self.node_number = 8 10 | 11 | # The edges to connect each node 12 | self.edges = [(0, 1), (0, 3), (1, 2), (1, 5), (2, 7), (3, 4), (3, 6), 13 | (4, 5), (5, 7)] 14 | 15 | # The 8 * 8 matrix of boolean values, only updated by the edges 16 | self.graph = [[False for j in range(self.node_number)] 17 | for i in range(self.node_number)] 18 | #print(self.graph) 19 | 20 | # The queue of open set, which is an array 21 | self.source_open_set = deque() 22 | self.destination_open_set = deque() 23 | 24 | # The source and destination nodes for this game 25 | self.source_node = 0 26 | self.destination_node = 7 27 | 28 | # The 8 array of boolean which means this node is visited 29 | self.source_is_visit_node_array = [False for i in range(self.node_number)] 30 | self.destination_is_visit_node_array = [ 31 | False for i in range(self.node_number) 32 | ] 33 | 34 | # The 8 array of int which means this node's best parent node id 35 | self.source_best_parent_node_array = [-1 for i in range(self.node_number)] 36 | self.destination_best_parent_node_array = [ 37 | -1 for i in range(self.node_number) 38 | ] 39 | 40 | # Record the step index of collision 41 | self.collision_step = 0 42 | self.collision_node = -1 43 | 44 | self.initialize_internal_variables() 45 | #print(self.graph) 46 | 47 | self.travel_and_update_variables() 48 | 49 | print("The output for the forward:") 50 | self.source_travel_desination_path(self.source_node, self.collision_node) 51 | print("Inverse the output for the backward:") 52 | self.destination_travel_desination_path(self.destination_node, 53 | self.collision_node) 54 | 55 | def initialize_internal_variables(self): 56 | # Update the graph with edges 57 | for i, j in self.edges: 58 | self.graph[i][j] = True 59 | self.graph[j][i] = True 60 | 61 | # Update the open set with the source nodes 62 | self.source_open_set.append(self.source_node) 63 | self.source_is_visit_node_array[self.source_node] = True 64 | self.source_best_parent_node_array[self.source_node] = self.source_node 65 | 66 | # Update the open set with the destination nodes 67 | self.destination_open_set.append(self.destination_node) 68 | self.destination_is_visit_node_array[self.destination_node] = True 69 | self.destination_best_parent_node_array[ 70 | self.destination_node] = self.destination_node 71 | 72 | def travel_and_update_variables(self): 73 | 74 | is_collision = False 75 | 76 | # Travel if some nodes in open set 77 | while len(self.source_open_set) > 0 or len(self.destination_open_set) > 0: 78 | 79 | # Run forward 80 | if len(self.source_open_set) > 0: 81 | current_node = self.source_open_set.popleft() 82 | 83 | for other_node in range(self.node_number): 84 | 85 | # Check if these two nodes are connected 86 | if self.graph[current_node][other_node]: 87 | 88 | # Check if the other node is visited 89 | if self.source_is_visit_node_array[other_node] == False: 90 | 91 | # Update the open set and visited array 92 | self.source_open_set.append(other_node) 93 | self.source_best_parent_node_array[other_node] = current_node 94 | self.source_is_visit_node_array[other_node] = True 95 | 96 | # Run backward 97 | if len(self.destination_open_set) > 0: 98 | current_node = self.destination_open_set.popleft() 99 | 100 | for other_node in range(self.node_number): 101 | 102 | # Check if these two nodes are connected 103 | if self.graph[current_node][other_node]: 104 | 105 | # Check if the other node is visited 106 | if self.destination_is_visit_node_array[other_node] == False: 107 | 108 | # Update the open set and visited array 109 | self.destination_open_set.append(other_node) 110 | self.destination_best_parent_node_array[ 111 | other_node] = current_node 112 | self.destination_is_visit_node_array[other_node] = True 113 | 114 | # Check collision 115 | #print(self.source_is_visit_node_array) 116 | #print(self.destination_is_visit_node_array) 117 | self.collision_step += 1 118 | if is_collision: 119 | return 120 | else: 121 | for i in range(self.node_number): 122 | if self.source_is_visit_node_array[i] == True and self.destination_is_visit_node_array[i] == True: 123 | is_collision = True 124 | self.collision_node = i 125 | print("Collision step: {}, collision node: {}".format( 126 | self.collision_step, self.collision_node)) 127 | return 128 | 129 | def source_travel_desination_path(self, source_node, destination_node): 130 | if destination_node == source_node: 131 | print(destination_node) 132 | elif destination_node == -1: 133 | print("Error, get node of -1 which is not updated") 134 | else: 135 | self.source_travel_desination_path( 136 | source_node, self.source_best_parent_node_array[destination_node]) 137 | print(destination_node) 138 | 139 | def destination_travel_desination_path(self, source_node, destination_node): 140 | if destination_node == source_node: 141 | print(destination_node) 142 | elif destination_node == -1: 143 | print("Error, get node of -1 which is not updated") 144 | else: 145 | self.destination_travel_desination_path( 146 | source_node, 147 | self.destination_best_parent_node_array[destination_node]) 148 | print(destination_node) 149 | 150 | 151 | def main(): 152 | print("Start bidirectional breath first search") 153 | 154 | game = BreathFirstSearchGame() 155 | 156 | 157 | if __name__ == "__main__": 158 | main() 159 | -------------------------------------------------------------------------------- /breath_first_search/breath_first_search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from collections import deque 4 | 5 | 6 | class BreathFirstSearchGame(object): 7 | def __init__(self): 8 | # The node index are from 0 to 7, such as 0, 1, 2, 3, 4 9 | self.node_number = 8 10 | 11 | # The edges to connect each node 12 | self.edges = [(0, 1), (0, 3), (1, 2), (1, 5), (2, 7), (3, 4), (3, 6), 13 | (4, 5), (5, 7)] 14 | 15 | # The 8 * 8 matrix of boolean values, only updated by the edges 16 | self.graph = [[False for j in range(self.node_number)] 17 | for i in range(self.node_number)] 18 | #print(self.graph) 19 | 20 | # The queue of open set, which is an array 21 | self.open_set = deque() 22 | 23 | # The source and destination nodes for this game 24 | self.source_node = 0 25 | self.destination_node = 7 26 | 27 | # The 8 array of boolean which means this node is visited 28 | self.is_visit_node_array = [False for i in range(self.node_number)] 29 | 30 | # The 8 array of int which means this node's best parent node id 31 | self.best_parent_node_array = [-1 for i in range(self.node_number)] 32 | 33 | self.initialize_internal_variables() 34 | #print(self.graph) 35 | 36 | self.travel_and_update_variables() 37 | 38 | # 0 1 2 7 39 | self.travel_desination_path(self.destination_node) 40 | 41 | # [0, 0, 1, 0, 3, 1, 3, 2] 42 | # print(self.best_parent_node_array) 43 | 44 | def initialize_internal_variables(self): 45 | # Update the graph with edges 46 | for i, j in self.edges: 47 | self.graph[i][j] = True 48 | self.graph[j][i] = True 49 | 50 | # Update the open set with the source nodes 51 | self.open_set.append(self.source_node) 52 | self.is_visit_node_array[self.source_node] = True 53 | self.best_parent_node_array[self.source_node] = self.source_node 54 | 55 | def travel_and_update_variables(self): 56 | # Travel if some nodes in open set 57 | while len(self.open_set) > 0: 58 | 59 | current_node = self.open_set.popleft() 60 | 61 | for other_node in range(self.node_number): 62 | 63 | #import ipdb;ipdb.set_trace() 64 | # Check if these two nodes are connected 65 | if self.graph[current_node][other_node]: 66 | 67 | # Check if the other node is visited 68 | if self.is_visit_node_array[other_node] == False: 69 | 70 | # Update the open set and visited array 71 | self.open_set.append(other_node) 72 | self.best_parent_node_array[other_node] = current_node 73 | self.is_visit_node_array[other_node] = True 74 | 75 | def travel_desination_path(self, destination_node): 76 | 77 | if destination_node == self.source_node: 78 | print(destination_node) 79 | 80 | else: 81 | self.travel_desination_path( 82 | self.best_parent_node_array[destination_node]) 83 | print(destination_node) 84 | 85 | 86 | def main(): 87 | print("Start breath first search") 88 | 89 | game = BreathFirstSearchGame() 90 | 91 | 92 | if __name__ == "__main__": 93 | main() 94 | -------------------------------------------------------------------------------- /code/queue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | class Node(object): 4 | def __init__(self, data, next=None): 5 | self.data = data 6 | self.next = next 7 | 8 | class Queue(object): 9 | def __init__(self): 10 | # Left is head and right is tail, enqueue to tail and dequeue from head 11 | self.left = None 12 | self.right = None 13 | 14 | def is_empty(self): 15 | if self.left == None: 16 | return True 17 | else: 18 | return False 19 | 20 | def enqueue(self, data): 21 | node = Node(data) 22 | if self.left == None: 23 | self.left = node 24 | self.right = node 25 | else: 26 | self.right.next = node 27 | self.right = node 28 | 29 | def dequeue(self): 30 | if self.left == None: 31 | print("No item to dequeue, exit") 32 | exit(1) 33 | elif self.left == self.right: 34 | return_node = self.left 35 | self.left = None 36 | self.right = None 37 | return return_node 38 | else: 39 | return_node = self.left 40 | self.left = return_node.next 41 | return return_node 42 | 43 | def display(self): 44 | if self.left == None: 45 | print("[ ]") 46 | else: 47 | node = self.left 48 | result = "[ " 49 | while node != self.right: 50 | result += self.left.data 51 | node = node.next 52 | result += self.right.data 53 | result += " ]" 54 | print(result) 55 | 56 | def main(): 57 | queue = Queue() 58 | queue.enqueue("node1") 59 | queue.display() 60 | queue.enqueue("node2") 61 | queue.display() 62 | queue.enqueue("node3") 63 | queue.display() 64 | 65 | queue.dequeue() 66 | queue.display() 67 | queue.dequeue() 68 | queue.display() 69 | 70 | queue.enqueue("node4") 71 | queue.display() 72 | 73 | queue.dequeue() 74 | queue.display() 75 | queue.dequeue() 76 | queue.display() 77 | 78 | if __name__ == "__main__": 79 | main() 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /code/quicksort.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | def main(): 4 | print("Start") 5 | 6 | a = [2, 9, 1, 19, 16] 7 | print("Original array: {}".format(a)) 8 | 9 | first = a[0] 10 | left = [i for i in a[1:] if i < first] 11 | right = [i for i in a[1:] if i > first] 12 | print("Left array: {}".format(left)) 13 | print("Right array: {}".format(right)) 14 | 15 | result = quick_sort(a) 16 | print("Quick sorted array: {}".format(result)) 17 | 18 | print("End") 19 | 20 | def quick_sort(a): 21 | if len(a) <= 1: 22 | return a 23 | else: 24 | first = a[0] 25 | left = [i for i in a[1:] if i < first] 26 | right = [i for i in a[1:] if i > first] 27 | return quick_sort(left) + [first] + quick_sort(right) 28 | 29 | if __name__ == "__main__": 30 | main() 31 | -------------------------------------------------------------------------------- /lru.md: -------------------------------------------------------------------------------- 1 | 2 | ``` 3 | class LRUCache(object): 4 | 5 | def __init__(self, capacity): 6 | """ 7 | :type capacity: int 8 | """ 9 | 10 | self.capacity = capacity 11 | # Example: [{"k1": "v1"}, {"k2": "v2"}] 12 | self.items = [] 13 | 14 | 15 | def get(self, key): 16 | """ 17 | :rtype: int 18 | """ 19 | 20 | for i, kv in enumerate(self.items): 21 | if kv.keys()[0] == key: 22 | del(self.items[i]) 23 | self.items.insert(0, kv) 24 | return kv.values()[0] 25 | 26 | # Return -1 if no match 27 | return -1 28 | 29 | 30 | def set(self, key, value): 31 | """ 32 | :type key: int 33 | :type value: int 34 | :rtype: nothing 35 | """ 36 | 37 | if len(self.items) == 0: 38 | self.items.insert(0, {key: value}) 39 | 40 | for i, kv in enumerate(self.items): 41 | # If there is the key, just remove and insert in the head 42 | if kv.keys()[0] == key: 43 | del(self.items[i]) 44 | self.items.insert(0, {key: value}) 45 | # If there is no key and there's no capacity 46 | elif len(self.items) >= self.capacity: 47 | # Remove one 48 | self.items.pop() 49 | 50 | # Insert one 51 | self.items.insert(0, {key: value}) 52 | # If there is no key and there is some capacity 53 | else: 54 | # Insert one 55 | self.items.insert(0, {key: value}) 56 | ``` 57 | --------------------------------------------------------------------------------