├── res ├── map.png ├── maze.png ├── plot.png ├── cover.jpg ├── magic.png ├── table1.png ├── table2.png ├── Fulladder.gif ├── Halfadder.gif ├── adj_mat.jpg ├── left_90.png ├── min_heap.png ├── mountain.png ├── my_tree.png ├── triangle.png ├── Shell-Sort.png ├── basic_tree.png ├── my_hilbert.png ├── turtle_init.png └── knight_moves.jpg ├── pythonds ├── __init__.pyc ├── trees │ ├── bst.pyc │ ├── __init__.pyc │ ├── balance.pyc │ ├── binheap.pyc │ ├── binaryTree.pyc │ ├── __init__.py │ ├── binheap.py │ ├── binaryTree.py │ ├── balance.py │ └── bst.py ├── basic │ ├── deque.pyc │ ├── queue.pyc │ ├── stack.pyc │ ├── __init__.pyc │ ├── __init__.py │ ├── queue.py │ ├── stack.py │ └── deque.py ├── graphs │ ├── __init__.pyc │ ├── adjGraph.pyc │ ├── priorityQueue.pyc │ ├── __init__.py │ ├── adjGraph.py │ └── priorityQueue.py └── __init__.py ├── maze2.txt ├── README.md ├── sort.ipynb ├── String.ipynb ├── book_learning_7.ipynb ├── book_learning_2.ipynb ├── book_learning_5.ipynb ├── book_learning_1.ipynb ├── book_learning_6.ipynb └── book_learning_4.ipynb /res/map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/map.png -------------------------------------------------------------------------------- /res/maze.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/maze.png -------------------------------------------------------------------------------- /res/plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/plot.png -------------------------------------------------------------------------------- /res/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/cover.jpg -------------------------------------------------------------------------------- /res/magic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/magic.png -------------------------------------------------------------------------------- /res/table1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/table1.png -------------------------------------------------------------------------------- /res/table2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/table2.png -------------------------------------------------------------------------------- /res/Fulladder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/Fulladder.gif -------------------------------------------------------------------------------- /res/Halfadder.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/Halfadder.gif -------------------------------------------------------------------------------- /res/adj_mat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/adj_mat.jpg -------------------------------------------------------------------------------- /res/left_90.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/left_90.png -------------------------------------------------------------------------------- /res/min_heap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/min_heap.png -------------------------------------------------------------------------------- /res/mountain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/mountain.png -------------------------------------------------------------------------------- /res/my_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/my_tree.png -------------------------------------------------------------------------------- /res/triangle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/triangle.png -------------------------------------------------------------------------------- /res/Shell-Sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/Shell-Sort.png -------------------------------------------------------------------------------- /res/basic_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/basic_tree.png -------------------------------------------------------------------------------- /res/my_hilbert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/my_hilbert.png -------------------------------------------------------------------------------- /res/turtle_init.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/turtle_init.png -------------------------------------------------------------------------------- /pythonds/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/__init__.pyc -------------------------------------------------------------------------------- /pythonds/trees/bst.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/trees/bst.pyc -------------------------------------------------------------------------------- /res/knight_moves.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/res/knight_moves.jpg -------------------------------------------------------------------------------- /pythonds/basic/deque.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/basic/deque.pyc -------------------------------------------------------------------------------- /pythonds/basic/queue.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/basic/queue.pyc -------------------------------------------------------------------------------- /pythonds/basic/stack.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/basic/stack.pyc -------------------------------------------------------------------------------- /pythonds/basic/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/basic/__init__.pyc -------------------------------------------------------------------------------- /pythonds/trees/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/trees/__init__.pyc -------------------------------------------------------------------------------- /pythonds/trees/balance.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/trees/balance.pyc -------------------------------------------------------------------------------- /pythonds/trees/binheap.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/trees/binheap.pyc -------------------------------------------------------------------------------- /pythonds/graphs/__init__.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/graphs/__init__.pyc -------------------------------------------------------------------------------- /pythonds/graphs/adjGraph.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/graphs/adjGraph.pyc -------------------------------------------------------------------------------- /pythonds/trees/binaryTree.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/trees/binaryTree.pyc -------------------------------------------------------------------------------- /pythonds/graphs/priorityQueue.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/applenob/algorithm_note/HEAD/pythonds/graphs/priorityQueue.pyc -------------------------------------------------------------------------------- /pythonds/graphs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from .adjGraph import Graph 4 | from .adjGraph import Vertex 5 | from .priorityQueue import PriorityQueue 6 | -------------------------------------------------------------------------------- /pythonds/basic/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | #__all__ = ["stack"] 3 | 4 | 5 | from .stack import Stack 6 | from .queue import Queue 7 | from .deque import Deque 8 | 9 | 10 | -------------------------------------------------------------------------------- /pythonds/trees/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | from .balance import AVLTree 5 | from .bst import BinarySearchTree 6 | from .binheap import BinHeap 7 | from .binaryTree import BinaryTree 8 | -------------------------------------------------------------------------------- /maze2.txt: -------------------------------------------------------------------------------- 1 | ++++++++++++++++++++++ 2 | + + ++ ++ + 3 | + + + +++ + ++ 4 | + + + ++ ++++ + ++ 5 | +++ ++++++ +++ + + 6 | + ++ ++ + 7 | +++++ ++++++ +++++ + 8 | + + +++++++ + + 9 | + +++++++ S + + 10 | + + +++ 11 | ++++++++++++++++++ +++ -------------------------------------------------------------------------------- /pythonds/__init__.py: -------------------------------------------------------------------------------- 1 | from .trees import BinaryTree, AVLTree, BinarySearchTree, BinHeap, BinaryTree 2 | from .basic import Stack, Queue, Deque 3 | from .graphs import Graph, Vertex, PriorityQueue 4 | 5 | __ALL__ = [Stack, Queue, Deque, Graph, Vertex, PriorityQueue, BinaryTree, AVLTree, BinarySearchTree, BinHeap, BinaryTree] 6 | -------------------------------------------------------------------------------- /pythonds/basic/queue.py: -------------------------------------------------------------------------------- 1 | # Bradley N. Miller, David L. Ranum 2 | # Introduction to Data Structures and Algorithms in Python 3 | # Copyright 2005 4 | # 5 | #queue.py 6 | 7 | class Queue: 8 | def __init__(self): 9 | self.items = [] 10 | 11 | def isEmpty(self): 12 | return self.items == [] 13 | 14 | def enqueue(self, item): 15 | self.items.insert(0,item) 16 | 17 | def dequeue(self): 18 | return self.items.pop() 19 | 20 | def size(self): 21 | return len(self.items) 22 | -------------------------------------------------------------------------------- /pythonds/basic/stack.py: -------------------------------------------------------------------------------- 1 | # Bradley N. Miller, David L. Ranum 2 | # Introduction to Data Structures and Algorithms in Python 3 | # Copyright 2005 4 | # 5 | #stack.py 6 | 7 | class Stack: 8 | def __init__(self): 9 | self.items = [] 10 | 11 | def isEmpty(self): 12 | return self.items == [] 13 | 14 | def push(self, item): 15 | self.items.append(item) 16 | 17 | def pop(self): 18 | return self.items.pop() 19 | 20 | def peek(self): 21 | return self.items[len(self.items)-1] 22 | 23 | def size(self): 24 | return len(self.items) 25 | 26 | -------------------------------------------------------------------------------- /pythonds/basic/deque.py: -------------------------------------------------------------------------------- 1 | # Bradley N. Miller, David L. Ranum 2 | # Introduction to Data Structures and Algorithms in Python 3 | # Copyright 2005 4 | # 5 | #deque.py 6 | 7 | 8 | class Deque: 9 | def __init__(self): 10 | self.items = [] 11 | 12 | def isEmpty(self): 13 | return self.items == [] 14 | 15 | def addFront(self, item): 16 | self.items.append(item) 17 | 18 | def addRear(self, item): 19 | self.items.insert(0,item) 20 | 21 | def removeFront(self): 22 | return self.items.pop() 23 | 24 | def removeRear(self): 25 | return self.items.pop(0) 26 | 27 | def size(self): 28 | return len(self.items) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 算法和数据结构学习笔记 2 | 3 | ## 基础学习 4 | 5 | - **《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业**: 6 | - [book_learning_1.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_1.ipynb) 7 | - [book_learning_2.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_2.ipynb) 8 | - [book_learning_3.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_3.ipynb) 9 | - [book_learning_4.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_4.ipynb) 10 | - [book_learning_5.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_5.ipynb) 11 | - [book_learning_6.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_6.ipynb) 12 | - [book_learning_7.ipynb](https://github.com/applenob/algorithm_note/blob/master/book_learning_7.ipynb) 13 | 14 | ## LeetCode 总结: 15 | 16 | - **字符串String**:[String.ipynb](https://github.com/applenob/algorithm_note/blob/master/String.ipynb) 17 | -------------------------------------------------------------------------------- /sort.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# 排序算法总结" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "collapsed": true 22 | }, 23 | "outputs": [], 24 | "source": [ 25 | "" 26 | ] 27 | } 28 | ], 29 | "metadata": { 30 | "anaconda-cloud": {}, 31 | "kernelspec": { 32 | "display_name": "Python [default]", 33 | "language": "python", 34 | "name": "python2" 35 | }, 36 | "language_info": { 37 | "codemirror_mode": { 38 | "name": "ipython", 39 | "version": 2.0 40 | }, 41 | "file_extension": ".py", 42 | "mimetype": "text/x-python", 43 | "name": "python", 44 | "nbconvert_exporter": "python", 45 | "pygments_lexer": "ipython2", 46 | "version": "2.7.12" 47 | } 48 | }, 49 | "nbformat": 4, 50 | "nbformat_minor": 0 51 | } -------------------------------------------------------------------------------- /pythonds/graphs/adjGraph.py: -------------------------------------------------------------------------------- 1 | # 2 | # adjGraph 3 | # 4 | # Created by Brad Miller on 2005-02-24. 5 | # Copyright (c) 2005 Brad Miller, David Ranum, Luther College. All rights reserved. 6 | # 7 | 8 | import sys 9 | import os 10 | import unittest 11 | 12 | class Graph: 13 | def __init__(self): 14 | self.vertices = {} 15 | self.numVertices = 0 16 | 17 | def addVertex(self,key): 18 | self.numVertices = self.numVertices + 1 19 | newVertex = Vertex(key) 20 | self.vertices[key] = newVertex 21 | return newVertex 22 | 23 | def getVertex(self,n): 24 | if n in self.vertices: 25 | return self.vertices[n] 26 | else: 27 | return None 28 | 29 | def __contains__(self,n): 30 | return n in self.vertices 31 | 32 | def addEdge(self,f,t,cost=0): 33 | if f not in self.vertices: 34 | nv = self.addVertex(f) 35 | if t not in self.vertices: 36 | nv = self.addVertex(t) 37 | self.vertices[f].addNeighbor(self.vertices[t],cost) 38 | 39 | def getVertices(self): 40 | return list(self.vertices.keys()) 41 | 42 | def __iter__(self): 43 | return iter(self.vertices.values()) 44 | 45 | class Vertex: 46 | def __init__(self,num): 47 | self.id = num 48 | self.connectedTo = {} 49 | self.color = 'white' 50 | self.dist = sys.maxsize 51 | self.pred = None 52 | self.disc = 0 53 | self.fin = 0 54 | 55 | # def __lt__(self,o): 56 | # return self.id < o.id 57 | 58 | def addNeighbor(self,nbr,weight=0): 59 | self.connectedTo[nbr] = weight 60 | 61 | def setColor(self,color): 62 | self.color = color 63 | 64 | def setDistance(self,d): 65 | self.dist = d 66 | 67 | def setPred(self,p): 68 | self.pred = p 69 | 70 | def setDiscovery(self,dtime): 71 | self.disc = dtime 72 | 73 | def setFinish(self,ftime): 74 | self.fin = ftime 75 | 76 | def getFinish(self): 77 | return self.fin 78 | 79 | def getDiscovery(self): 80 | return self.disc 81 | 82 | def getPred(self): 83 | return self.pred 84 | 85 | def getDistance(self): 86 | return self.dist 87 | 88 | def getColor(self): 89 | return self.color 90 | 91 | def getConnections(self): 92 | return self.connectedTo.keys() 93 | 94 | def getWeight(self,nbr): 95 | return self.connectedTo[nbr] 96 | 97 | def __str__(self): 98 | return str(self.id) + ":color " + self.color + ":disc " + str(self.disc) + ":fin " + str(self.fin) + ":dist " + str(self.dist) + ":pred \n\t[" + str(self.pred)+ "]\n" 99 | 100 | def getId(self): 101 | return self.id 102 | 103 | class adjGraphTests(unittest.TestCase): 104 | def setUp(self): 105 | self.tGraph = Graph() 106 | 107 | def testMakeGraph(self): 108 | gFile = open("test.dat") 109 | for line in gFile: 110 | fVertex, tVertex = line.split('|') 111 | fVertex = int(fVertex) 112 | tVertex = int(tVertex) 113 | self.tGraph.addEdge(fVertex,tVertex) 114 | for i in self.tGraph: 115 | adj = i.getAdj() 116 | for k in adj: 117 | print(i, k) 118 | 119 | 120 | if __name__ == '__main__': 121 | unittest.main() 122 | 123 | -------------------------------------------------------------------------------- /pythonds/graphs/priorityQueue.py: -------------------------------------------------------------------------------- 1 | # Bradley N. Miller, David L. Ranum 2 | # Introduction to Data Structures and Algorithms in Python 3 | # Copyright 2005 4 | # 5 | import unittest 6 | 7 | # this implementation of binary heap takes key value pairs, 8 | # we will assume that the keys are all comparable 9 | 10 | class PriorityQueue: 11 | def __init__(self): 12 | self.heapArray = [(0,0)] 13 | self.currentSize = 0 14 | 15 | def buildHeap(self,alist): 16 | self.currentSize = len(alist) 17 | self.heapArray = [(0,0)] 18 | for i in alist: 19 | self.heapArray.append(i) 20 | i = len(alist) // 2 21 | while (i > 0): 22 | self.percDown(i) 23 | i = i - 1 24 | 25 | def percDown(self,i): 26 | while (i * 2) <= self.currentSize: 27 | mc = self.minChild(i) 28 | if self.heapArray[i][0] > self.heapArray[mc][0]: 29 | tmp = self.heapArray[i] 30 | self.heapArray[i] = self.heapArray[mc] 31 | self.heapArray[mc] = tmp 32 | i = mc 33 | 34 | def minChild(self,i): 35 | if i*2 > self.currentSize: 36 | return -1 37 | else: 38 | if i*2 + 1 > self.currentSize: 39 | return i*2 40 | else: 41 | if self.heapArray[i*2][0] < self.heapArray[i*2+1][0]: 42 | return i*2 43 | else: 44 | return i*2+1 45 | 46 | def percUp(self,i): 47 | while i // 2 > 0: 48 | if self.heapArray[i][0] < self.heapArray[i//2][0]: 49 | tmp = self.heapArray[i//2] 50 | self.heapArray[i//2] = self.heapArray[i] 51 | self.heapArray[i] = tmp 52 | i = i//2 53 | 54 | def add(self,k): 55 | self.heapArray.append(k) 56 | self.currentSize = self.currentSize + 1 57 | self.percUp(self.currentSize) 58 | 59 | def delMin(self): 60 | retval = self.heapArray[1][1] 61 | self.heapArray[1] = self.heapArray[self.currentSize] 62 | self.currentSize = self.currentSize - 1 63 | self.heapArray.pop() 64 | self.percDown(1) 65 | return retval 66 | 67 | def isEmpty(self): 68 | if self.currentSize == 0: 69 | return True 70 | else: 71 | return False 72 | 73 | def decreaseKey(self,val,amt): 74 | # this is a little wierd, but we need to find the heap thing to decrease by 75 | # looking at its value 76 | done = False 77 | i = 1 78 | myKey = 0 79 | while not done and i <= self.currentSize: 80 | if self.heapArray[i][1] == val: 81 | done = True 82 | myKey = i 83 | else: 84 | i = i + 1 85 | if myKey > 0: 86 | self.heapArray[myKey] = (amt,self.heapArray[myKey][1]) 87 | self.percUp(myKey) 88 | 89 | def __contains__(self,vtx): 90 | for pair in self.heapArray: 91 | if pair[1] == vtx: 92 | return True 93 | return False 94 | 95 | class TestBinHeap(unittest.TestCase): 96 | def setUp(self): 97 | self.theHeap = PriorityQueue() 98 | self.theHeap.add((2,'x')) 99 | self.theHeap.add((3,'y')) 100 | self.theHeap.add((5,'z')) 101 | self.theHeap.add((6,'a')) 102 | self.theHeap.add((4,'d')) 103 | 104 | 105 | def testInsert(self): 106 | assert self.theHeap.currentSize == 5 107 | 108 | def testDelmin(self): 109 | assert self.theHeap.delMin() == 'x' 110 | assert self.theHeap.delMin() == 'y' 111 | 112 | def testDecKey(self): 113 | self.theHeap.decreaseKey('d',1) 114 | assert self.theHeap.delMin() == 'd' 115 | 116 | if __name__ == '__main__': 117 | unittest.main() 118 | -------------------------------------------------------------------------------- /String.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# LeetCode 中涉及String的题目总结" 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "```\n", 17 | "本篇关于LeetCode中涉及String使用的题目总结\n", 18 | "```" 19 | ] 20 | }, 21 | { 22 | "cell_type": "markdown", 23 | "metadata": {}, 24 | "source": [ 25 | "## 目录\n", 26 | "\n", 27 | "* [32. Longest Valid Parentheses](#32.-Longest-Valid-Parentheses)\n" 28 | ] 29 | }, 30 | { 31 | "cell_type": "markdown", 32 | "metadata": {}, 33 | "source": [ 34 | "## 32. Longest Valid Parentheses\n", 35 | "\n", 36 | "[原题](https://leetcode.com/problems/longest-valid-parentheses)\n", 37 | "\n", 38 | "难度:Hard\n", 39 | "\n", 40 | "```\n", 41 | "Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.\n", 42 | "\n", 43 | "For \"(()\", the longest valid parentheses substring is \"()\", which has length = 2.\n", 44 | "\n", 45 | "Another example is \")()())\", where the longest valid parentheses substring is \"()()\", which has length = 4.\n", 46 | "\n", 47 | "\n", 48 | "```" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 1, 54 | "metadata": { 55 | "collapsed": false 56 | }, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "4\n", 63 | "4\n", 64 | "2\n", 65 | "2\n", 66 | "6\n" 67 | ] 68 | } 69 | ], 70 | "source": [ 71 | "def longestValidParentheses(s):\n", 72 | " \"\"\"\n", 73 | " :type s: str\n", 74 | " :rtype: int\n", 75 | " \"\"\"\n", 76 | " stack = []\n", 77 | " result = 0\n", 78 | " left_connect = None\n", 79 | " for i, token in enumerate(s):\n", 80 | " if token == '(':\n", 81 | " if left_connect != None:\n", 82 | " stack.append(left_connect)\n", 83 | " left_connect = None\n", 84 | " else:\n", 85 | " stack.append(i)\n", 86 | " else:\n", 87 | " if stack:\n", 88 | " last_unclosed= stack.pop()\n", 89 | " result = max(i-last_unclosed+1, result)\n", 90 | " left_connect = last_unclosed\n", 91 | " else:\n", 92 | " left_connect = None\n", 93 | " return result\n", 94 | " \n", 95 | "print(longestValidParentheses(\")()())\"))\n", 96 | "print(longestValidParentheses(\"(()()\"))\n", 97 | "print(longestValidParentheses(\"(()(()\"))\n", 98 | "print(longestValidParentheses(\")()((()\"))\n", 99 | "print(longestValidParentheses(\")(()())\"))" 100 | ] 101 | }, 102 | { 103 | "cell_type": "markdown", 104 | "metadata": {}, 105 | "source": [ 106 | "### 分析\n", 107 | "\n", 108 | "上面的算法时间复杂度是O(n),空间复杂度是O(k/2),k是最长有效括号的长度。\n", 109 | "\n", 110 | "最简单的检查括号是否匹配问题的栈是来存\"(\"的。但是这里的栈是存的是前一个没被关闭的\"(\"的下标。\n", 111 | "\n", 112 | "如果左边有连续的完整的子串,left_connect存的是最左边的下标;否则left_connect为None。\n", 113 | "\n", 114 | "在设计算法的时候,通过一个保存前一个没被关闭的\"(\"的下标的栈,来实现最长子串的长度计算;\n", 115 | "\n", 116 | "但如果仅仅是这样,则对\"()(())\"这样的串只能寻到\"(())\"当做最大合理子串;而题目的意思是紧挨着的合理的子串也要算进去。\n", 117 | "\n", 118 | "因此其他地方都不用变,当遍历到\"()(\"也就是第三个符号时,要多算进去两个长度;而遍历到第四个符号和后面的符号,就不用了,因为不是紧挨着。所以这里使用left_connect变量来解决这一问题。" 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": { 125 | "collapsed": true 126 | }, 127 | "outputs": [], 128 | "source": [] 129 | } 130 | ], 131 | "metadata": { 132 | "anaconda-cloud": {}, 133 | "kernelspec": { 134 | "display_name": "Python [conda env:py35]", 135 | "language": "python", 136 | "name": "conda-env-py35-py" 137 | }, 138 | "language_info": { 139 | "codemirror_mode": { 140 | "name": "ipython", 141 | "version": 3 142 | }, 143 | "file_extension": ".py", 144 | "mimetype": "text/x-python", 145 | "name": "python", 146 | "nbconvert_exporter": "python", 147 | "pygments_lexer": "ipython3", 148 | "version": "3.5.2" 149 | } 150 | }, 151 | "nbformat": 4, 152 | "nbformat_minor": 0 153 | } 154 | -------------------------------------------------------------------------------- /book_learning_7.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业答案(七. Graphs and Graph Algorithms)\n", 8 | "\n", 9 | "```\n", 10 | "对应本书第七章。\n", 11 | "```" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## 目录\n", 19 | "\n", 20 | "* [7.Graphs and Graph Algorithms](#7.Graphs-and-Graph-Algorithms)\n", 21 | " + [笔记](#笔记)\n", 22 | " + [基本术语和定义](#基本术语和定义)\n", 23 | " + [邻接矩阵](#邻接矩阵)\n", 24 | " + [广度优先遍历](#广度优先遍历)\n", 25 | " + [骑士旅行问题](#骑士旅行问题)\n", 26 | " + [二叉查找树](#二叉查找树)\n", 27 | " + [平衡二叉查找树](#平衡二叉查找树)\n", 28 | " + [讨论作业](#讨论作业)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": { 34 | "collapsed": true 35 | }, 36 | "source": [ 37 | "## 基本术语和定义\n", 38 | "\n", 39 | "**顶点(Vertex or Node)**:构成图的最基本的点,类比地图中一个个城市。\n", 40 | "\n", 41 | "**边(Edge or Arc)**:连接两个点,可以是有向的,也可以是无向的,类比一个城市到另一个城市的路线。\n", 42 | "\n", 43 | "**权重(Weight)**:可以理解成是边的距离,可以是一个城市到一个城市的距离,或者花费时间。\n", 44 | "\n", 45 | "![](http://interactivepython.org/courselib/static/pythonds/_images/digraph.png)\n", 46 | "\n", 47 | "上面这幅图,可以用如下方法表示:\n", 48 | "\n", 49 | "$V = \\left\\{ V0,V1,V2,V3,V4,V5 \\right\\}$\n", 50 | "\n", 51 | "$E = \\left\\{ \\begin{array}{l}(v0,v1,5), (v1,v2,4), (v2,v3,9), (v3,v4,7), (v4,v0,1), \\\\\n", 52 | " (v0,v5,2),(v5,v4,8),(v3,v5,3),(v5,v2,1)\n", 53 | " \\end{array} \\right\\}$\n" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "## 邻接矩阵\n", 61 | "\n", 62 | "![](https://github.com/applenob/algorithm_note/raw/master/res/min_heap.png)\n", 63 | "\n", 64 | "**邻接矩阵(Adjacency Matrix)**是最常用的拿来存储图的顶点和边的信息的结构。\n", 65 | "\n", 66 | "但一般邻接矩阵比较稀疏,直接用二维数组去存浪费空间,于是有了Adjacency List。\n", 67 | "\n", 68 | "![](http://interactivepython.org/courselib/static/pythonds/_images/adjlist.png)\n" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": {}, 74 | "source": [ 75 | "## 广度优先遍历\n", 76 | "\n", 77 | "图的广度优先遍历(),类似于树的层次遍历,借助一个队列可以轻松实现。\n", 78 | "\n", 79 | "不同的地方在于,图需要标记某个结点是否已经遍历过,下面通过设置颜色表示:\n", 80 | "\n", 81 | "```python\n", 82 | "from pythonds.graphs import Graph, Vertex\n", 83 | "from pythonds.basic import Queue\n", 84 | "\n", 85 | "def bfs(g,start):\n", 86 | " start.setDistance(0)\n", 87 | " start.setPred(None)\n", 88 | " vertQueue = Queue()\n", 89 | " vertQueue.enqueue(start)\n", 90 | " while (vertQueue.size() > 0):\n", 91 | " currentVert = vertQueue.dequeue()\n", 92 | " for nbr in currentVert.getConnections():\n", 93 | " # 白色代表没有遍历过的\n", 94 | " if (nbr.getColor() == 'white'):\n", 95 | " # 灰色代表已经在队列\n", 96 | " nbr.setColor('gray')\n", 97 | " nbr.setDistance(currentVert.getDistance() + 1)\n", 98 | " nbr.setPred(currentVert)\n", 99 | " vertQueue.enqueue(nbr)\n", 100 | " # 黑色代表已经遍历过\n", 101 | " currentVert.setColor('black')\n", 102 | "```" 103 | ] 104 | }, 105 | { 106 | "cell_type": "markdown", 107 | "metadata": {}, 108 | "source": [ 109 | "## 骑士旅行问题\n", 110 | "\n", 111 | "骑士旅行问题(The Knight’s Tour Problem):\n", 112 | "\n", 113 | "在国际象棋中8×8的格子中,有一个骑士(马,走日字),试寻找一种方法,让骑士可以遍历所有格子,并不重复。\n", 114 | "\n" 115 | ] 116 | }, 117 | { 118 | "cell_type": "code", 119 | "execution_count": null, 120 | "metadata": { 121 | "collapsed": true 122 | }, 123 | "outputs": [], 124 | "source": [] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "execution_count": null, 129 | "metadata": { 130 | "collapsed": true 131 | }, 132 | "outputs": [], 133 | "source": [] 134 | } 135 | ], 136 | "metadata": { 137 | "anaconda-cloud": {}, 138 | "kernelspec": { 139 | "display_name": "Python [default]", 140 | "language": "python", 141 | "name": "python2" 142 | }, 143 | "language_info": { 144 | "codemirror_mode": { 145 | "name": "ipython", 146 | "version": 2 147 | }, 148 | "file_extension": ".py", 149 | "mimetype": "text/x-python", 150 | "name": "python", 151 | "nbconvert_exporter": "python", 152 | "pygments_lexer": "ipython2", 153 | "version": "2.7.12" 154 | } 155 | }, 156 | "nbformat": 4, 157 | "nbformat_minor": 1 158 | } 159 | -------------------------------------------------------------------------------- /pythonds/trees/binheap.py: -------------------------------------------------------------------------------- 1 | # Bradley N. Miller, David L. Ranum 2 | # Introduction to Data Structures and Algorithms in Python 3 | # Copyright 2005 4 | # 5 | import unittest 6 | 7 | # this heap takes key value pairs, we will assume that the keys are integers 8 | class BinHeap: 9 | def __init__(self): 10 | self.heapList = [0] 11 | self.currentSize = 0 12 | 13 | 14 | def buildHeap(self,alist): 15 | i = len(alist) // 2 16 | self.currentSize = len(alist) 17 | self.heapList = [0] + alist[:] 18 | print(len(self.heapList), i) 19 | while (i > 0): 20 | print(self.heapList, i) 21 | self.percDown(i) 22 | i = i - 1 23 | print(self.heapList,i) 24 | 25 | def percDown(self,i): 26 | while (i * 2) <= self.currentSize: 27 | mc = self.minChild(i) 28 | if self.heapList[i] > self.heapList[mc]: 29 | tmp = self.heapList[i] 30 | self.heapList[i] = self.heapList[mc] 31 | self.heapList[mc] = tmp 32 | i = mc 33 | 34 | def minChild(self,i): 35 | if i * 2 + 1 > self.currentSize: 36 | return i * 2 37 | else: 38 | if self.heapList[i * 2] < self.heapList[i * 2 + 1]: 39 | return i * 2 40 | else: 41 | return i * 2 + 1 42 | 43 | def percUp(self,i): 44 | while i // 2 > 0: 45 | if self.heapList[i] < self.heapList[i//2]: 46 | tmp = self.heapList[i // 2] 47 | self.heapList[i // 2] = self.heapList[i] 48 | self.heapList[i] = tmp 49 | i = i // 2 50 | 51 | def insert(self,k): 52 | self.heapList.append(k) 53 | self.currentSize = self.currentSize + 1 54 | self.percUp(self.currentSize) 55 | 56 | def delMin(self): 57 | retval = self.heapList[1] 58 | self.heapList[1] = self.heapList[self.currentSize] 59 | self.currentSize = self.currentSize - 1 60 | self.heapList.pop() 61 | self.percDown(1) 62 | return retval 63 | 64 | def isEmpty(self): 65 | if currentSize == 0: 66 | return True 67 | else: 68 | return False 69 | 70 | class FooThing: 71 | def __init__(self,x,y): 72 | self.key = x 73 | self.val = y 74 | 75 | 76 | def __lt__(self,other): 77 | if self.key < other.key: 78 | return True 79 | else: 80 | return False 81 | 82 | def __gt__(self,other): 83 | if self.key > other.key: 84 | return True 85 | else: 86 | return False 87 | 88 | def __hash__(self): 89 | return self.key 90 | 91 | class TestBinHeap(unittest.TestCase): 92 | def setUp(self): 93 | self.theHeap = BinHeap() 94 | self.theHeap.insert(FooThing(5,'a')) 95 | self.theHeap.insert(FooThing(9,'d')) 96 | self.theHeap.insert(FooThing(1,'x')) 97 | self.theHeap.insert(FooThing(2,'y')) 98 | self.theHeap.insert(FooThing(3,'z')) 99 | 100 | def testInsert(self): 101 | assert self.theHeap.currentSize == 5 102 | 103 | def testDelmin(self): 104 | assert self.theHeap.delMin().val == 'x' 105 | assert self.theHeap.delMin().val == 'y' 106 | assert self.theHeap.delMin().val == 'z' 107 | assert self.theHeap.delMin().val == 'a' 108 | 109 | def testMixed(self): 110 | myHeap = BinHeap() 111 | myHeap.insert(9) 112 | myHeap.insert(1) 113 | myHeap.insert(5) 114 | assert myHeap.delMin() == 1 115 | myHeap.insert(2) 116 | myHeap.insert(7) 117 | assert myHeap.delMin() == 2 118 | assert myHeap.delMin() == 5 119 | 120 | def testDupes(self): 121 | myHeap = BinHeap() 122 | myHeap.insert(9) 123 | myHeap.insert(1) 124 | myHeap.insert(8) 125 | myHeap.insert(1) 126 | assert myHeap.currentSize == 4 127 | assert myHeap.delMin() == 1 128 | assert myHeap.delMin() == 1 129 | assert myHeap.delMin() == 8 130 | 131 | def testBuildHeap(self): 132 | myHeap = BinHeap() 133 | myHeap.buildHeap([9,5,6,2,3]) 134 | f = myHeap.delMin() 135 | print("f = ", f) 136 | assert f == 2 137 | assert myHeap.delMin() == 3 138 | assert myHeap.delMin() == 5 139 | assert myHeap.delMin() == 6 140 | assert myHeap.delMin() == 9 141 | 142 | if __name__ == '__main__': 143 | d = {} 144 | d[FooThing(1,'z')] = 10 145 | unittest.main() 146 | -------------------------------------------------------------------------------- /pythonds/trees/binaryTree.py: -------------------------------------------------------------------------------- 1 | # Bradley N. Miller, David L. Ranum 2 | # Introduction to Data Structures and Algorithms in Python 3 | # Copyright 2005 4 | # 5 | 6 | from __future__ import print_function 7 | 8 | class BinaryTree: 9 | """ 10 | A recursive implementation of Binary Tree 11 | Using links and Nodes approach. 12 | 13 | Modified to allow for trees to be constructed from other trees rather than always creating 14 | a new tree in the insertLeft or insertRight 15 | """ 16 | 17 | def __init__(self,rootObj): 18 | self.key = rootObj 19 | self.leftChild = None 20 | self.rightChild = None 21 | 22 | def insertLeft(self,newNode): 23 | 24 | if isinstance(newNode, BinaryTree): 25 | t = newNode 26 | else: 27 | t = BinaryTree(newNode) 28 | 29 | if self.leftChild is not None: 30 | t.left = self.leftChild 31 | 32 | self.leftChild = t 33 | 34 | def insertRight(self,newNode): 35 | if isinstance(newNode,BinaryTree): 36 | t = newNode 37 | else: 38 | t = BinaryTree(newNode) 39 | 40 | if self.rightChild is not None: 41 | t.right = self.rightChild 42 | self.rightChild = t 43 | 44 | def isLeaf(self): 45 | return ((not self.leftChild) and (not self.rightChild)) 46 | 47 | def getRightChild(self): 48 | return self.rightChild 49 | 50 | def getLeftChild(self): 51 | return self.leftChild 52 | 53 | def setRootVal(self,obj): 54 | self.key = obj 55 | 56 | def getRootVal(self,): 57 | return self.key 58 | 59 | def inorder(self): 60 | if self.leftChild: 61 | self.leftChild.inorder() 62 | print(self.key) 63 | if self.rightChild: 64 | self.rightChild.inorder() 65 | 66 | def postorder(self): 67 | if self.leftChild: 68 | self.leftChild.postorder() 69 | if self.rightChild: 70 | self.rightChild.postorder() 71 | print(self.key) 72 | 73 | 74 | def preorder(self): 75 | print(self.key) 76 | if self.leftChild: 77 | self.leftChild.preorder() 78 | if self.rightChild: 79 | self.rightChild.preorder() 80 | 81 | def printexp(self): 82 | if self.leftChild: 83 | print('(', end=' ') 84 | self.leftChild.printexp() 85 | print(self.key, end=' ') 86 | if self.rightChild: 87 | self.rightChild.printexp() 88 | print(')', end=' ') 89 | 90 | def postordereval(self): 91 | opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv} 92 | res1 = None 93 | res2 = None 94 | if self.leftChild: 95 | res1 = self.leftChild.postordereval() #// \label{peleft} 96 | if self.rightChild: 97 | res2 = self.rightChild.postordereval() #// \label{peright} 98 | if res1 and res2: 99 | return opers[self.key](res1,res2) #// \label{peeval} 100 | else: 101 | return self.key 102 | 103 | def inorder(tree): 104 | if tree != None: 105 | inorder(tree.getLeftChild()) 106 | print(tree.getRootVal()) 107 | inorder(tree.getRightChild()) 108 | 109 | def printexp(tree): 110 | if tree.leftChild: 111 | print('(', end=' ') 112 | printexp(tree.getLeftChild()) 113 | print(tree.getRootVal(), end=' ') 114 | if tree.rightChild: 115 | printexp(tree.getRightChild()) 116 | print(')', end=' ') 117 | 118 | def printexp(tree): 119 | sVal = "" 120 | if tree: 121 | sVal = '(' + printexp(tree.getLeftChild()) 122 | sVal = sVal + str(tree.getRootVal()) 123 | sVal = sVal + printexp(tree.getRightChild()) + ')' 124 | return sVal 125 | 126 | def postordereval(tree): 127 | opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv} 128 | res1 = None 129 | res2 = None 130 | if tree: 131 | res1 = postordereval(tree.getLeftChild()) #// \label{peleft} 132 | res2 = postordereval(tree.getRightChild()) #// \label{peright} 133 | if res1 and res2: 134 | return opers[tree.getRootVal()](res1,res2) #// \label{peeval} 135 | else: 136 | return tree.getRootVal() 137 | 138 | def height(tree): 139 | if tree == None: 140 | return -1 141 | else: 142 | return 1 + max(height(tree.leftChild),height(tree.rightChild)) 143 | 144 | if __name__ == '__main__': 145 | t = BinaryTree(7) 146 | t.insertLeft(3) 147 | t.insertRight(9) 148 | inorder(t) 149 | import operator 150 | x = BinaryTree('*') 151 | x.insertLeft('+') 152 | l = x.getLeftChild() 153 | l.insertLeft(4) 154 | l.insertRight(5) 155 | x.insertRight(7) 156 | print(printexp(x)) 157 | print(postordereval(x)) 158 | print(height(x)) 159 | -------------------------------------------------------------------------------- /pythonds/trees/balance.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3.1 2 | # Bradley N. Miller, David L. Ranum 3 | # Introduction to Data Structures and Algorithms in Python 4 | # Copyright 2005, 2010 5 | # 6 | 7 | import unittest 8 | from .bst import BinarySearchTree, TreeNode 9 | 10 | class AVLTree(BinarySearchTree): 11 | ''' 12 | Author: Brad Miller 13 | Date: 1/15/2005 14 | Description: Imlement a binary search tree with the following interface 15 | functions: 16 | __contains__(y) <==> y in x 17 | __getitem__(y) <==> x[y] 18 | __init__() 19 | __len__() <==> len(x) 20 | __setitem__(k,v) <==> x[k] = v 21 | clear() 22 | get(k) 23 | has_key(k) 24 | items() 25 | keys() 26 | values() 27 | put(k,v) 28 | ''' 29 | 30 | 31 | def _put(self,key,val,currentNode): 32 | if key < currentNode.key: 33 | if currentNode.hasLeftChild(): 34 | self._put(key,val,currentNode.leftChild) 35 | else: 36 | currentNode.leftChild = TreeNode(key,val,parent=currentNode) 37 | self.updateBalance(currentNode.leftChild) 38 | else: 39 | if currentNode.hasRightChild(): 40 | self._put(key,val,currentNode.rightChild) 41 | else: 42 | currentNode.rightChild = TreeNode(key,val,parent=currentNode) 43 | self.updateBalance(currentNode.rightChild) 44 | 45 | def updateBalance(self,node): 46 | if node.balanceFactor > 1 or node.balanceFactor < -1: 47 | self.rebalance(node) 48 | return 49 | if node.parent != None: 50 | if node.isLeftChild(): 51 | node.parent.balanceFactor += 1 52 | elif node.isRightChild(): 53 | node.parent.balanceFactor -= 1 54 | 55 | if node.parent.balanceFactor != 0: 56 | self.updateBalance(node.parent) 57 | 58 | def rebalance(self,node): 59 | if node.balanceFactor < 0: 60 | if node.rightChild.balanceFactor > 0: 61 | # Do an LR Rotation 62 | self.rotateRight(node.rightChild) 63 | self.rotateLeft(node) 64 | else: 65 | # single left 66 | self.rotateLeft(node) 67 | elif node.balanceFactor > 0: 68 | if node.leftChild.balanceFactor < 0: 69 | # Do an RL Rotation 70 | self.rotateLeft(node.leftChild) 71 | self.rotateRight(node) 72 | else: 73 | # single right 74 | self.rotateRight(node) 75 | 76 | def rotateLeft(self,rotRoot): 77 | newRoot = rotRoot.rightChild 78 | rotRoot.rightChild = newRoot.leftChild 79 | if newRoot.leftChild != None: 80 | newRoot.leftChild.parent = rotRoot 81 | newRoot.parent = rotRoot.parent 82 | if rotRoot.isRoot(): 83 | self.root = newRoot 84 | else: 85 | if rotRoot.isLeftChild(): 86 | rotRoot.parent.leftChild = newRoot 87 | else: 88 | rotRoot.parent.rightChild = newRoot 89 | newRoot.leftChild = rotRoot 90 | rotRoot.parent = newRoot 91 | rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0) 92 | newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0) 93 | 94 | 95 | def rotateRight(self,rotRoot): 96 | newRoot = rotRoot.leftChild 97 | rotRoot.leftChild = newRoot.rightChild 98 | if newRoot.rightChild != None: 99 | newRoot.rightChild.parent = rotRoot 100 | newRoot.parent = rotRoot.parent 101 | if rotRoot.isRoot(): 102 | self.root = newRoot 103 | else: 104 | if rotRoot.isRightChild(): 105 | rotRoot.parent.rightChild = newRoot 106 | else: 107 | rotRoot.parent.leftChild = newRoot 108 | newRoot.rightChild = rotRoot 109 | rotRoot.parent = newRoot 110 | rotRoot.balanceFactor = rotRoot.balanceFactor - 1 - max(newRoot.balanceFactor, 0) 111 | newRoot.balanceFactor = newRoot.balanceFactor - 1 + min(rotRoot.balanceFactor, 0) 112 | 113 | 114 | class BinaryTreeTests(unittest.TestCase): 115 | def setUp(self): 116 | self.bst = AVLTree() 117 | 118 | def testAuto1(self): 119 | self.bst.put(30,'a') 120 | self.bst.put(50,'b') 121 | self.bst.put(40,'c') 122 | assert self.bst.root.key == 40 123 | 124 | def testAuto2(self): 125 | self.bst.put(50,'a') 126 | self.bst.put(30,'b') 127 | self.bst.put(40,'c') 128 | assert self.bst.root.key == 40 129 | 130 | def testAuto3(self): 131 | self.bst.put(50,'a') 132 | self.bst.put(30,'b') 133 | self.bst.put(70,'c') 134 | self.bst.put(80,'c') 135 | self.bst.put(60,'d') 136 | self.bst.put(90,'e') 137 | assert self.bst.root.key == 70 138 | 139 | def testAuto3(self): 140 | self.bst.put(40,'a') 141 | self.bst.put(30,'b') 142 | self.bst.put(50,'c') 143 | self.bst.put(45,'d') 144 | self.bst.put(60,'e') 145 | self.bst.put(43,'f') 146 | assert self.bst.root.key == 45 147 | assert self.bst.root.leftChild.key == 40 148 | assert self.bst.root.rightChild.key == 50 149 | assert self.bst.root.balanceFactor == 0 150 | assert self.bst.root.leftChild.balanceFactor == 0 151 | assert self.bst.root.rightChild.balanceFactor == -1 152 | 153 | def testAuto4(self): 154 | self.bst.put(40,'a') 155 | self.bst.put(30,'b') 156 | self.bst.put(50,'c') 157 | self.bst.put(10,'d') 158 | self.bst.put(35,'e') 159 | self.bst.put(37,'f') 160 | assert self.bst.root.key == 35 161 | assert self.bst.root.leftChild.key == 30 162 | assert self.bst.root.rightChild.key == 40 163 | assert self.bst.root.balanceFactor == 0 164 | assert self.bst.root.leftChild.balanceFactor == 1 165 | assert self.bst.root.rightChild.balanceFactor == 0 166 | 167 | 168 | if __name__ == '__main__': 169 | import platform 170 | print(platform.python_version()) 171 | unittest.main() 172 | 173 | # Local Variables: 174 | # py-which-shell: "python3" 175 | # End: 176 | -------------------------------------------------------------------------------- /book_learning_2.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业答案(二. Analysis) \n", 8 | "\n", 9 | "```\n", 10 | "对应本书第二章。\n", 11 | "```\n", 12 | "\n", 13 | "## 目录\n", 14 | "\n", 15 | "* [2.Analysis](#2.Analysis)\n", 16 | " + [笔记](#笔记)\n", 17 | " + [一. 大O](#一.-大O)\n", 18 | " + [二. List](#二.-List)\n", 19 | " + [三. Dictionary](#三.-Dictionary)\n", 20 | " + [作业](#作业)\n", 21 | " + [q1 证明list的index操作是O(1)](#q1)\n", 22 | " + [q2 证明dict的get和set是O(1)](#q1)\n", 23 | " + [q3 对比list和dict的del操作](#q3)\n", 24 | " + [q4 给定一个list,返回第k小的数,O(nlogn)](#q4)\n", 25 | " + [q5 q4时间复杂度是O(n)的解法](#q5)" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "# 2.Analysis\n", 33 | "\n", 34 | "## 笔记\n", 35 | "\n", 36 | "这章主要讲了算法复杂度的分析。\n", 37 | "\n", 38 | "### 一. 大O\n", 39 | " \n", 40 | "名称源自于**Order of magnitude function $O(f(n))$** 描述了时间复杂度**$T(n)$**当中**增长速度最快**的部分。 \n", 41 | "\n", 42 | "例如:$T(n)=5n^2+27n+1005$的阶是$O(n^2)$。\n", 43 | "\n", 44 | "因为不同的机器计算速度的区别,和不同的编程语言的效率的不同,所以分析算法的时间复杂度使用这样的粗粒度就足够了。\n", 45 | "\n", 46 | "不同函数的增长速度:\n", 47 | " \n", 48 | "![](https://raw.githubusercontent.com/applenob/algorithm_note/master/res/plot.png)\n", 49 | "\n", 50 | "python自带的数据结构:\n", 51 | "\n", 52 | "### 二. List\n", 53 | "\n", 54 | "|Operation\t|Big-O Efficiency|\n", 55 | "|:-----------:|:----------------:|\n", 56 | "|index []\t|O(1)|\n", 57 | "|index assignment\t|O(1)|\n", 58 | "|append\t|O(1)|\n", 59 | "|pop()\t|O(1)|\n", 60 | "|pop(i)\t|O(n)|\n", 61 | "|insert(i,item)\t|O(n)|\n", 62 | "|del operator\t|O(n)|\n", 63 | "|iteration\t|O(n)|\n", 64 | "|contains (in)\t|O(n)|\n", 65 | "|get slice [x:y]\t|O(k)|\n", 66 | "|del slice\t|O(n)|\n", 67 | "|set slice\t|O(n+k)|\n", 68 | "|reverse\t|O(n)|\n", 69 | "|concatenate\t|O(k)|\n", 70 | "|sort\t|O(n log n)|\n", 71 | "|multiply\t|O(nk)|\n", 72 | "\n", 73 | "其中,需要注意的是concatenate还有pop和pop(i)的区别。\n", 74 | "\n", 75 | "[python 中list的实现](http://www.jianshu.com/p/J4U6rR)\n", 76 | "\n", 77 | "![](https://raw.github.com/acmerfight/insight_python/master/images/list_insert.png)\n", 78 | "\n", 79 | "### 三. Dictionary\n", 80 | "\n", 81 | "\n", 82 | "|operation\t|Big-O Efficiency|\n", 83 | "|:-----------:|:----------------:|\n", 84 | "|copy\t|O(n)|\n", 85 | "|get item\t|O(1)|\n", 86 | "|set item\t|O(1)|\n", 87 | "|delete item\t|O(1)|\n", 88 | "|contains (in)\t|O(1)|\n", 89 | "|iteration\t|O(n)|\n", 90 | "\n", 91 | "dict的in操作是O(1),list是O(n)\n", 92 | "\n", 93 | "[python的dict实现](http://www.jianshu.com/p/02af9673ab34)\n", 94 | "\n", 95 | "## 作业\n", 96 | "\n", 97 | "[作业链接](http://interactivepython.org/courselib/static/pythonds/AlgorithmAnalysis/ProgrammingExercises.html)" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "### q1\n", 105 | "\n", 106 | "是证明list的index操作是O(1)。暂且认为list的pop(0)是O(n)是已知条件。" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 1, 112 | "metadata": { 113 | "collapsed": true 114 | }, 115 | "outputs": [], 116 | "source": [ 117 | "from timeit import Timer\n", 118 | "# q1\n", 119 | "def test5():\n", 120 | " l = list(range(1000))\n", 121 | " for i in range(1000):\n", 122 | " l[i] = 1\n", 123 | "\n", 124 | "def test6():\n", 125 | " l = list(range(1000))\n", 126 | " for i in range(1000):\n", 127 | " l.pop(0)" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": 2, 133 | "metadata": { 134 | "collapsed": false 135 | }, 136 | "outputs": [ 137 | { 138 | "name": "stdout", 139 | "output_type": "stream", 140 | "text": [ 141 | "list index operation: 0.0006067330032237805 milliseconds\n", 142 | "list pop(0): 0.0025059110048459843 milliseconds\n" 143 | ] 144 | } 145 | ], 146 | "source": [ 147 | "t5 = Timer(\"test5()\", \"from __main__ import test5\")\n", 148 | "print(\"list index operation:\",t5.timeit(number=10), \"milliseconds\")\n", 149 | "t6 = Timer(\"test6()\", \"from __main__ import test6\")\n", 150 | "print(\"list pop(0):\",t6.timeit(number=10), \"milliseconds\")" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": {}, 156 | "source": [ 157 | "### q2\n", 158 | "\n", 159 | "是证明dict的get和set是O(1),先认为dict的遍历是O(n)" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": 4, 165 | "metadata": { 166 | "collapsed": true 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "def test7():\n", 171 | " d = dict(zip(list(range(1000)), list(range(1000,2000))))\n", 172 | " for i in range(1000):\n", 173 | " d[i] = 1\n", 174 | "\n", 175 | "def test8():\n", 176 | " d = dict(zip(list(range(1000)), list(range(1000,2000))))\n", 177 | " for i in range(1000):\n", 178 | " k = d[i]\n", 179 | " \n", 180 | "def test9():\n", 181 | " d = dict(zip(list(range(1000)), list(range(1000,2000))))\n", 182 | " for i in range(1000):\n", 183 | " for one in d:\n", 184 | " pass" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 5, 190 | "metadata": { 191 | "collapsed": false 192 | }, 193 | "outputs": [ 194 | { 195 | "name": "stdout", 196 | "output_type": "stream", 197 | "text": [ 198 | "dict set item: 0.13327282800310059 milliseconds\n", 199 | "dict get item: 0.1195711559994379 milliseconds\n", 200 | "dict iteration: 11.517200201000378 milliseconds\n" 201 | ] 202 | } 203 | ], 204 | "source": [ 205 | "t7 = Timer(\"test7()\", \"from __main__ import test7\")\n", 206 | "print(\"dict set item:\",t7.timeit(number=1000), \"milliseconds\")\n", 207 | "t8 = Timer(\"test8()\", \"from __main__ import test8\")\n", 208 | "print(\"dict get item:\",t8.timeit(number=1000), \"milliseconds\")\n", 209 | "t9 = Timer(\"test9()\", \"from __main__ import test9\")\n", 210 | "print(\"dict iteration:\",t9.timeit(number=1000), \"milliseconds\")" 211 | ] 212 | }, 213 | { 214 | "cell_type": "markdown", 215 | "metadata": {}, 216 | "source": [ 217 | "### q3\n", 218 | "\n", 219 | "对比list和dict的del操作" 220 | ] 221 | }, 222 | { 223 | "cell_type": "code", 224 | "execution_count": 6, 225 | "metadata": { 226 | "collapsed": true 227 | }, 228 | "outputs": [], 229 | "source": [ 230 | "def test10():\n", 231 | " l = list(range(1000))\n", 232 | " for i in range(1000):\n", 233 | " del l[0]\n", 234 | " \n", 235 | "def test11():\n", 236 | " d = dict(zip(list(range(1000)), list(range(1000,2000))))\n", 237 | " for i in range(1000):\n", 238 | " del d[i]" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": 8, 244 | "metadata": { 245 | "collapsed": false 246 | }, 247 | "outputs": [ 248 | { 249 | "name": "stdout", 250 | "output_type": "stream", 251 | "text": [ 252 | "list del item: 0.14648991199646844 milliseconds\n", 253 | "dict del item: 0.13190877300075954 milliseconds\n", 254 | "list del item: 0.14080390099843498 milliseconds\n", 255 | "dict del item: 0.13055396499839844 milliseconds\n" 256 | ] 257 | } 258 | ], 259 | "source": [ 260 | "t10 = Timer(\"test10()\", \"from __main__ import test10\")\n", 261 | "print(\"list del item:\",t10.timeit(number=1000), \"milliseconds\")\n", 262 | "t11 = Timer(\"test11()\", \"from __main__ import test11\")\n", 263 | "print(\"dict del item:\",t11.timeit(number=1000), \"milliseconds\")\n", 264 | "t10 = Timer(\"test10()\", \"from __main__ import test10\")\n", 265 | "print(\"list del item:\",t10.timeit(number=1000), \"milliseconds\")\n", 266 | "t11 = Timer(\"test11()\", \"from __main__ import test11\")\n", 267 | "print(\"dict del item:\",t11.timeit(number=1000), \"milliseconds\")" 268 | ] 269 | }, 270 | { 271 | "cell_type": "markdown", 272 | "metadata": {}, 273 | "source": [ 274 | "### q4\n", 275 | "\n", 276 | "给定一个list,返回第k小的数。先给出时间复杂度是O(nlogn)的简单版:" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": 9, 282 | "metadata": { 283 | "collapsed": true 284 | }, 285 | "outputs": [], 286 | "source": [ 287 | "def kth_smallest_1(nums, k):\n", 288 | " \"\"\"\n", 289 | " find kth smallest of a list, O(nlogn)\n", 290 | " :type nums: list\n", 291 | " :type k: int\n", 292 | " :rtype: num\n", 293 | " \"\"\"\n", 294 | " nums.sort()\n", 295 | " return nums[k-1]" 296 | ] 297 | }, 298 | { 299 | "cell_type": "code", 300 | "execution_count": 10, 301 | "metadata": { 302 | "collapsed": false 303 | }, 304 | "outputs": [ 305 | { 306 | "data": { 307 | "text/plain": [ 308 | "5" 309 | ] 310 | }, 311 | "execution_count": 10, 312 | "metadata": {}, 313 | "output_type": "execute_result" 314 | } 315 | ], 316 | "source": [ 317 | "kth_smallest_1([1, 5,9,10,2,3], 4)" 318 | ] 319 | }, 320 | { 321 | "cell_type": "markdown", 322 | "metadata": {}, 323 | "source": [ 324 | "### q5\n", 325 | "\n", 326 | "q4时间复杂度是O(n)的解法。利用快排的思路。平均时间复杂度是O(n),最坏情况时间复杂度是$O(n^2)$" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 11, 332 | "metadata": { 333 | "collapsed": true 334 | }, 335 | "outputs": [], 336 | "source": [ 337 | "def kth_smallest_2(nums, k):\n", 338 | " \"\"\"\n", 339 | " find kth smallest of a list, O(n)\n", 340 | " :type nums: list\n", 341 | " :type k: int\n", 342 | " :rtype: num\n", 343 | " \"\"\"\n", 344 | " def partition(nums, start, end):\n", 345 | " \"\"\"\n", 346 | " Pick last element as pivot\n", 347 | " Place all smaller elements before pivot\n", 348 | " Place all bigger elements after pivot\n", 349 | " \"\"\"\n", 350 | " pivot = nums[end]\n", 351 | " # currentSmaller 记录当前有几个数比pivot小\n", 352 | " currentSmaller = start\n", 353 | " for i in range(start, end):\n", 354 | " # If current element <= pivot, put it to right position\n", 355 | " if nums[i] <= pivot:\n", 356 | " nums[i], nums[currentSmaller] = nums[currentSmaller], nums[i]\n", 357 | " currentSmaller += 1\n", 358 | " # Put pivot to right position\n", 359 | " nums[end], nums[currentSmaller] = nums[currentSmaller], nums[end]\n", 360 | " return currentSmaller\n", 361 | "\n", 362 | " def quickSelect(nums, start, end, k):\n", 363 | " pos = partition(nums, start, end)\n", 364 | " # print(nums)\n", 365 | " if pos == k - 1:\n", 366 | " return nums[pos]\n", 367 | " if pos < k - 1:\n", 368 | " return quickSelect(nums, pos + 1, end, k)\n", 369 | " else:\n", 370 | " return quickSelect(nums, start, pos - 1, k)\n", 371 | " return quickSelect(nums, 0, len(nums)-1, k)" 372 | ] 373 | }, 374 | { 375 | "cell_type": "code", 376 | "execution_count": 12, 377 | "metadata": { 378 | "collapsed": false 379 | }, 380 | "outputs": [ 381 | { 382 | "data": { 383 | "text/plain": [ 384 | "5" 385 | ] 386 | }, 387 | "execution_count": 12, 388 | "metadata": {}, 389 | "output_type": "execute_result" 390 | } 391 | ], 392 | "source": [ 393 | "kth_smallest_2([1,5,9,10,2,3], 4)" 394 | ] 395 | }, 396 | { 397 | "cell_type": "code", 398 | "execution_count": 13, 399 | "metadata": { 400 | "collapsed": false 401 | }, 402 | "outputs": [ 403 | { 404 | "name": "stdout", 405 | "output_type": "stream", 406 | "text": [ 407 | "kth_smallest_1: 7.706003088969737e-06 milliseconds\n", 408 | "kth_smallest_2: 4.322700260672718e-05 milliseconds\n" 409 | ] 410 | } 411 | ], 412 | "source": [ 413 | "t12 = Timer(\"kth_smallest_1([1, 5,9,10,2,3], 4)\", \"from __main__ import kth_smallest_1\")\n", 414 | "print(\"kth_smallest_1:\",t12.timeit(number=10), \"milliseconds\")\n", 415 | "t13 = Timer(\"kth_smallest_2([1,5,9,10,2,3], 4)\", \"from __main__ import kth_smallest_2\")\n", 416 | "print(\"kth_smallest_2:\",t13.timeit(number=10), \"milliseconds\")" 417 | ] 418 | } 419 | ], 420 | "metadata": { 421 | "anaconda-cloud": {}, 422 | "kernelspec": { 423 | "display_name": "Python [conda env:py35]", 424 | "language": "python", 425 | "name": "conda-env-py35-py" 426 | }, 427 | "language_info": { 428 | "codemirror_mode": { 429 | "name": "ipython", 430 | "version": 3 431 | }, 432 | "file_extension": ".py", 433 | "mimetype": "text/x-python", 434 | "name": "python", 435 | "nbconvert_exporter": "python", 436 | "pygments_lexer": "ipython3", 437 | "version": "3.5.2" 438 | } 439 | }, 440 | "nbformat": 4, 441 | "nbformat_minor": 1 442 | } 443 | -------------------------------------------------------------------------------- /pythonds/trees/bst.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3.1 2 | # Bradley N. Miller, David L. Ranum 3 | # Introduction to Data Structures and Algorithms in Python 4 | # Copyright 2005, 2010 5 | # 6 | 7 | import unittest 8 | class BinarySearchTree: 9 | ''' 10 | Author: Brad Miller 11 | Date: 1/15/2005 12 | Description: Imlement a binary search tree with the following interface 13 | functions: 14 | __contains__(y) <==> y in x 15 | __getitem__(y) <==> x[y] 16 | __init__() 17 | __len__() <==> len(x) 18 | __setitem__(k,v) <==> x[k] = v 19 | clear() 20 | get(k) 21 | items() 22 | keys() 23 | values() 24 | put(k,v) 25 | in 26 | del <==> 27 | ''' 28 | 29 | def __init__(self): 30 | self.root = None 31 | self.size = 0 32 | 33 | def put(self,key,val): 34 | if self.root: 35 | self._put(key,val,self.root) 36 | else: 37 | self.root = TreeNode(key,val) 38 | self.size = self.size + 1 39 | 40 | def _put(self,key,val,currentNode): 41 | if key < currentNode.key: 42 | if currentNode.hasLeftChild(): 43 | self._put(key,val,currentNode.leftChild) 44 | else: 45 | currentNode.leftChild = TreeNode(key,val,parent=currentNode) 46 | else: 47 | if currentNode.hasRightChild(): 48 | self._put(key,val,currentNode.rightChild) 49 | else: 50 | currentNode.rightChild = TreeNode(key,val,parent=currentNode) 51 | 52 | def __setitem__(self,k,v): 53 | self.put(k,v) 54 | 55 | def get(self,key): 56 | if self.root: 57 | res = self._get(key,self.root) 58 | if res: 59 | return res.payload 60 | else: 61 | return None 62 | else: 63 | return None 64 | 65 | def _get(self,key,currentNode): 66 | if not currentNode: 67 | return None 68 | elif currentNode.key == key: 69 | return currentNode 70 | elif key < currentNode.key: 71 | return self._get(key,currentNode.leftChild) 72 | else: 73 | return self._get(key,currentNode.rightChild) 74 | 75 | 76 | def __getitem__(self,key): 77 | res = self.get(key) 78 | if res: 79 | return res 80 | else: 81 | raise KeyError('Error, key not in tree') 82 | 83 | 84 | def __contains__(self,key): 85 | if self._get(key,self.root): 86 | return True 87 | else: 88 | return False 89 | 90 | def length(self): 91 | return self.size 92 | 93 | def __len__(self): 94 | return self.size 95 | 96 | def __iter__(self): 97 | return self.root.__iter__() 98 | 99 | def delete(self,key): 100 | if self.size > 1: 101 | nodeToRemove = self._get(key,self.root) 102 | if nodeToRemove: 103 | self.remove(nodeToRemove) 104 | self.size = self.size-1 105 | else: 106 | raise KeyError('Error, key not in tree') 107 | elif self.size == 1 and self.root.key == key: 108 | self.root = None 109 | self.size = self.size - 1 110 | else: 111 | raise KeyError('Error, key not in tree') 112 | 113 | def __delitem__(self,key): 114 | self.delete(key) 115 | 116 | def remove(self,currentNode): 117 | if currentNode.isLeaf(): #leaf 118 | if currentNode == currentNode.parent.leftChild: 119 | currentNode.parent.leftChild = None 120 | else: 121 | currentNode.parent.rightChild = None 122 | elif currentNode.hasBothChildren(): #interior 123 | succ = currentNode.findSuccessor() 124 | succ.spliceOut() 125 | currentNode.key = succ.key 126 | currentNode.payload = succ.payload 127 | else: # this node has one child 128 | if currentNode.hasLeftChild(): 129 | if currentNode.isLeftChild(): 130 | currentNode.leftChild.parent = currentNode.parent 131 | currentNode.parent.leftChild = currentNode.leftChild 132 | elif currentNode.isRightChild(): 133 | currentNode.leftChild.parent = currentNode.parent 134 | currentNode.parent.rightChild = currentNode.leftChild 135 | else: 136 | currentNode.replaceNodeData(currentNode.leftChild.key, 137 | currentNode.leftChild.payload, 138 | currentNode.leftChild.leftChild, 139 | currentNode.leftChild.rightChild) 140 | else: 141 | if currentNode.isLeftChild(): 142 | currentNode.rightChild.parent = currentNode.parent 143 | currentNode.parent.leftChild = currentNode.rightChild 144 | elif currentNode.isRightChild(): 145 | currentNode.rightChild.parent = currentNode.parent 146 | currentNode.parent.rightChild = currentNode.rightChild 147 | else: 148 | currentNode.replaceNodeData(currentNode.rightChild.key, 149 | currentNode.rightChild.payload, 150 | currentNode.rightChild.leftChild, 151 | currentNode.rightChild.rightChild) 152 | 153 | def inorder(self): 154 | self._inorder(self.root) 155 | 156 | def _inorder(self,tree): 157 | if tree != None: 158 | self._inorder(tree.leftChild) 159 | print(tree.key) 160 | self._inorder(tree.rightChild) 161 | 162 | def postorder(self): 163 | self._postorder(self.root) 164 | 165 | def _postorder(self, tree): 166 | if tree: 167 | self._postorder(tree.rightChild) 168 | self._postorder(tree.leftChild) 169 | print(tree.key) 170 | 171 | def preorder(self): 172 | self._preorder(self,self.root) 173 | 174 | def _preorder(self,tree): 175 | if tree: 176 | print(tree.key) 177 | self._preorder(tree.leftChild) 178 | self._preorder(tree.rightChild) 179 | 180 | 181 | class TreeNode: 182 | def __init__(self,key,val,left=None,right=None,parent=None): 183 | self.key = key 184 | self.payload = val 185 | self.leftChild = left 186 | self.rightChild = right 187 | self.parent = parent 188 | self.balanceFactor = 0 189 | 190 | def hasLeftChild(self): 191 | return self.leftChild 192 | 193 | def hasRightChild(self): 194 | return self.rightChild 195 | 196 | def isLeftChild(self): 197 | return self.parent and self.parent.leftChild == self 198 | 199 | def isRightChild(self): 200 | return self.parent and self.parent.rightChild == self 201 | 202 | def isRoot(self): 203 | return not self.parent 204 | 205 | def isLeaf(self): 206 | return not (self.rightChild or self.leftChild) 207 | 208 | def hasAnyChildren(self): 209 | return self.rightChild or self.leftChild 210 | 211 | def hasBothChildren(self): 212 | return self.rightChild and self.leftChild 213 | 214 | def replaceNodeData(self,key,value,lc,rc): 215 | self.key = key 216 | self.payload = value 217 | self.leftChild = lc 218 | self.rightChild = rc 219 | if self.hasLeftChild(): 220 | self.leftChild.parent = self 221 | if self.hasRightChild(): 222 | self.rightChild.parent = self 223 | 224 | def findSuccessor(self): 225 | succ = None 226 | if self.hasRightChild(): 227 | succ = self.rightChild.findMin() 228 | else: 229 | if self.parent: 230 | if self.isLeftChild(): 231 | succ = self.parent 232 | else: 233 | self.parent.rightChild = None 234 | succ = self.parent.findSuccessor() 235 | self.parent.rightChild = self 236 | return succ 237 | 238 | 239 | def spliceOut(self): 240 | if self.isLeaf(): 241 | if self.isLeftChild(): 242 | self.parent.leftChild = None 243 | else: 244 | self.parent.rightChild = None 245 | elif self.hasAnyChildren(): 246 | if self.hasLeftChild(): 247 | if self.isLeftChild(): 248 | self.parent.leftChild = self.leftChild 249 | else: 250 | self.parent.rightChild = self.leftChild 251 | self.leftChild.parent = self.parent 252 | else: 253 | if self.isLeftChild(): 254 | self.parent.leftChild = self.rightChild 255 | else: 256 | self.parent.rightChild = self.rightChild 257 | self.rightChild.parent = self.parent 258 | 259 | def findMin(self): 260 | current = self 261 | while current.hasLeftChild(): 262 | current = current.leftChild 263 | return current 264 | 265 | def __iter__(self): 266 | """The standard inorder traversal of a binary tree.""" 267 | if self: 268 | if self.hasLeftChild(): 269 | for elem in self.leftChild: 270 | yield elem 271 | yield self.key 272 | if self.hasRightChild(): 273 | for elem in self.rightChild: 274 | yield elem 275 | 276 | 277 | class BinaryTreeTests(unittest.TestCase): 278 | def setUp(self): 279 | self.bst = BinarySearchTree() 280 | 281 | def testgetput(self): 282 | print('testgetput') 283 | self.bst.put(50,'a') 284 | self.bst.put(10,'b') 285 | self.bst.put(70,'c') 286 | self.bst.put(30,'d') 287 | self.bst.put(85,'d') 288 | self.bst.put(15,'e') 289 | self.bst.put(45,'f') 290 | print(self.bst.get(50)) 291 | assert self.bst.get(50) == 'a' 292 | assert self.bst.get(45) == 'f' 293 | assert self.bst.get(85) == 'd' 294 | assert self.bst.get(10) == 'b' 295 | assert self.bst.root.key == 50 296 | assert self.bst.root.leftChild.key == 10 297 | assert self.bst.root.rightChild.key == 70 298 | 299 | def testputoper(self): 300 | print('testputoper') 301 | self.bst[25] = 'g' 302 | assert self.bst[25] == 'g' 303 | 304 | def testFindSucc(self): 305 | print('testing findSucc') 306 | x = BinarySearchTree() 307 | x.put(10,'a') 308 | x.put(15,'b') 309 | x.put(6,'c') 310 | x.put(2,'d') 311 | x.put(8,'e') 312 | x.put(9,'f') 313 | assert x.root.leftChild.leftChild.findSuccessor().key == 6 314 | assert x.root.leftChild.rightChild.findSuccessor().key == 9 315 | assert x.root.leftChild.rightChild.rightChild.findSuccessor().key == 10 316 | 317 | def testSize(self): 318 | print('testing testSize') 319 | self.bst.put(50,'a') 320 | self.bst.put(10,'b') 321 | self.bst.put(70,'c') 322 | self.bst.put(30,'d') 323 | self.bst.put(85,'d') 324 | self.bst.put(15,'e') 325 | self.bst.put(45,'f') 326 | assert self.bst.length() == 7 327 | 328 | def testDelete(self): 329 | print('testing delete') 330 | self.bst.put(50,'a') 331 | self.bst.put(10,'b') 332 | self.bst.put(70,'c') 333 | self.bst.put(30,'d') 334 | self.bst.put(85,'d') 335 | self.bst.put(15,'e') 336 | self.bst.put(45,'f') 337 | self.bst.put(5,'g') 338 | print('initial inorder') 339 | self.bst.inorder() 340 | assert (10 in self.bst) == True 341 | self.bst.delete_key(10) 342 | print('delete 10 inorder') 343 | self.bst.inorder() 344 | assert (10 in self.bst) == False 345 | assert self.bst.root.leftChild.key == 15 346 | assert self.bst.root.leftChild.parent == self.bst.root 347 | assert self.bst.root.leftChild.rightChild.parent == self.bst.root.leftChild 348 | assert self.bst.get(30) == 'd' 349 | self.bst.delete_key(15) 350 | print('delete 15 inorder') 351 | self.bst.inorder() 352 | assert self.bst.root.leftChild.key == 30 353 | assert self.bst.root.leftChild.rightChild.key == 45 354 | assert self.bst.root.leftChild.rightChild.parent == self.bst.root.leftChild 355 | self.bst.delete_key(70) 356 | print('delete 70 inorder') 357 | self.bst.inorder() 358 | assert (85 in self.bst) == True 359 | assert self.bst.get(30) == 'd' 360 | print('root key = ', self.bst.root.key) 361 | print('left = ',self.bst.root.leftChild.key) 362 | print('left left = ',self.bst.root.leftChild.leftChild.key) 363 | print('left right = ',self.bst.root.leftChild.rightChild.key) 364 | print('right = ',self.bst.root.rightChild.key) 365 | self.bst.delete_key(50) 366 | assert self.bst.root.key == 85 367 | assert self.bst.root.leftChild.key == 30 368 | assert self.bst.root.rightChild == None 369 | assert self.bst.root.leftChild.leftChild.key == 5 370 | assert self.bst.root.leftChild.rightChild.key == 45 371 | assert self.bst.root.leftChild.leftChild.parent == self.bst.root.leftChild 372 | assert self.bst.root.leftChild.rightChild.parent == self.bst.root.leftChild 373 | print('new root key = ', self.bst.root.key) 374 | self.bst.inorder() 375 | self.bst.delete_key(45) 376 | assert self.bst.root.leftChild.key == 30 377 | self.bst.delete_key(85) 378 | assert self.bst.root.key == 30 379 | print('xxxx ',self.bst.root.leftChild.parent.key, self.bst.root.key) 380 | assert self.bst.root.leftChild.parent == self.bst.root 381 | self.bst.delete_key(30) 382 | assert self.bst.root.key == 5 383 | self.bst.inorder() 384 | print("final root = " + str(self.bst.root.key)) 385 | assert self.bst.root.key == 5 386 | self.bst.delete_key(5) 387 | assert self.bst.root == None 388 | 389 | def testDel2(self): 390 | self.bst.put(21,'a') 391 | self.bst.put(10,'b') 392 | self.bst.put(24,'c') 393 | self.bst.put(11,'d') 394 | self.bst.put(22,'d') 395 | self.bst.delete_key(10) 396 | assert self.bst.root.leftChild.key == 11 397 | assert self.bst.root.leftChild.parent == self.bst.root 398 | assert self.bst.root.rightChild.key == 24 399 | self.bst.delete_key(24) 400 | assert self.bst.root.rightChild.key == 22 401 | assert self.bst.root.rightChild.parent == self.bst.root 402 | self.bst.delete_key(22) 403 | self.bst.delete_key(21) 404 | print("del2 root = ",self.bst.root.key) 405 | assert self.bst.root.key == 11 406 | assert self.bst.root.leftChild == None 407 | assert self.bst.root.rightChild == None 408 | 409 | def testLarge(self): 410 | import random 411 | print('testing a large random tree') 412 | i = 0 413 | randList = [] 414 | while i < 10000: 415 | nrand = random.randrange(1,10000000) 416 | if nrand not in randList: 417 | randList.append(nrand) 418 | i += 1 419 | print(randList) 420 | for n in randList: 421 | self.bst.put(n,n) 422 | sortList = randList[:] 423 | sortList.sort() 424 | random.shuffle(randList) 425 | for n in randList: 426 | minNode = self.bst.root.findMin() 427 | if minNode: 428 | assert minNode.key == sortList[0] 429 | rootPos = sortList.index(self.bst.root.key) 430 | succ = self.bst.root.findSuccessor() 431 | if succ: 432 | assert succ.key == sortList[rootPos+1] 433 | else: 434 | assert self.bst.root.rightChild == None 435 | self.bst.delete_key(n) 436 | sortList.remove(n) 437 | 438 | assert self.bst.root == None 439 | 440 | def testIter(self): 441 | import random 442 | i = 0 443 | randList = [] 444 | while i < 100: 445 | nrand = random.randrange(1,10000) 446 | if nrand not in randList: 447 | randList.append(nrand) 448 | i += 1 449 | for n in randList: 450 | self.bst.put(n,n) 451 | sortList = randList[:] 452 | sortList.sort() 453 | 454 | i = 0 455 | for j in self.bst: 456 | assert j == sortList[i] 457 | i += 1 458 | # the following exercises all of the branches in deleting a node with one child 459 | def testCase1(self): 460 | self.bst.put(10,10) 461 | self.bst.put(7,7) 462 | self.bst.put(5,5) 463 | self.bst.put(1,1) 464 | self.bst.put(6,6) 465 | self.bst.delete_key(7) 466 | assert self.bst.root.leftChild.key == 5 467 | assert self.bst.root == self.bst.root.leftChild.parent 468 | assert self.bst.root.leftChild.leftChild.key == 1 469 | assert self.bst.root.leftChild.rightChild.key == 6 470 | 471 | def testCase2(self): 472 | self.bst = BinarySearchTree() 473 | self.bst.put(10,10) 474 | self.bst.put(15,15) 475 | self.bst.put(12,12) 476 | self.bst.put(11,11) 477 | self.bst.put(13,13) 478 | self.bst.delete_key(15) 479 | assert self.bst.root.rightChild.key == 12 480 | assert self.bst.root.rightChild.parent == self.bst.root 481 | assert self.bst.root.rightChild.leftChild.key == 11 482 | assert self.bst.root.rightChild.rightChild.key == 13 483 | 484 | def testCase3(self): 485 | self.bst = BinarySearchTree() 486 | self.bst.put(10,10) 487 | self.bst.put(6,6) 488 | self.bst.put(8,8) 489 | self.bst.put(7,7) 490 | self.bst.put(9,9) 491 | self.bst.delete_key(6) 492 | assert self.bst.root.leftChild.key == 8 493 | assert self.bst.root.leftChild.parent == self.bst.root 494 | assert self.bst.root.leftChild.leftChild.key == 7 495 | assert self.bst.root.leftChild.rightChild.key == 9 496 | 497 | def testCase4(self): 498 | self.bst = BinarySearchTree() 499 | self.bst.put(10,10) 500 | self.bst.put(15,15) 501 | self.bst.put(20,20) 502 | self.bst.put(17,17) 503 | self.bst.put(22,22) 504 | self.bst.delete_key(15) 505 | assert self.bst.root.rightChild.key == 20 506 | assert self.bst.root.rightChild.parent == self.bst.root 507 | assert self.bst.root.rightChild.rightChild.key == 22 508 | assert self.bst.root.rightChild.leftChild.key == 17 509 | 510 | def testCase5(self): 511 | self.bst.put(10,10) 512 | self.bst.put(20,20) 513 | self.bst.put(17,17) 514 | self.bst.put(22,22) 515 | self.bst.delete_key(10) 516 | assert self.bst.root.key == 20 517 | assert self.bst.root.leftChild.parent == self.bst.root 518 | assert self.bst.root.rightChild.parent == self.bst.root 519 | assert self.bst.root.leftChild.key == 17 520 | assert self.bst.root.rightChild.key == 22 521 | 522 | def testCase6(self): 523 | self.bst.put(10,10) 524 | self.bst.put(5,5) 525 | self.bst.put(1,1) 526 | self.bst.put(7,7) 527 | self.bst.delete_key(10) 528 | assert self.bst.root.key == 5 529 | assert self.bst.root.leftChild.parent == self.bst.root 530 | assert self.bst.root.rightChild.parent == self.bst.root 531 | assert self.bst.root.leftChild.key == 1 532 | assert self.bst.root.rightChild.key == 7 533 | 534 | def testBadDelete(self): 535 | self.bst.put(10,10) 536 | with self.assertRaises(KeyError): 537 | self.bst.delete_key(5) 538 | self.bst.delete_key(10) 539 | with self.assertRaises(KeyError): 540 | self.bst.delete_key(5) 541 | 542 | if __name__ == '__main__': 543 | import platform 544 | print(platform.python_version()) 545 | unittest.main() 546 | 547 | ### Local Variables: 548 | ### End: 549 | -------------------------------------------------------------------------------- /book_learning_5.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业答案(五. Sorting and Searching)\n", 8 | "\n", 9 | "```\n", 10 | "对应本书第五章。\n", 11 | "```" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## 目录\n", 19 | "\n", 20 | "* [5.Sorting and Searching](#5.Sorting-and-Searching)\n", 21 | " + [笔记](#笔记)\n", 22 | " + [二分查找](#二分查找)\n", 23 | " + [用Hash实现一个Map抽象数据类型](#用Hash实现一个Map抽象数据类型)\n", 24 | " + [冒泡排序](#冒泡排序)\n", 25 | " + [选择排序](#选择排序)\n", 26 | " + [插入排序](#插入排序)\n", 27 | " + [希尔排序](#希尔排序)\n", 28 | " + [合并排序](#合并排序)\n", 29 | " + [快速排序](#快速排序)\n", 30 | " + [作业](#作业)\n", 31 | " + [q1](#q1)\n", 32 | " + [q2](#q2)\n", 33 | " + [q4](#q4)\n", 34 | " + [q9](#q9)" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "# 5.Sorting and Searching\n", 42 | "\n", 43 | "[原目录](http://interactivepython.org/courselib/static/pythonds/SortSearch/toctree.html)\n", 44 | "\n", 45 | "## 笔记\n", 46 | "\n", 47 | "这章开始进入算法部分,讲解排序和查找。\n", 48 | "\n", 49 | "### 二分查找\n" 50 | ] 51 | }, 52 | { 53 | "cell_type": "code", 54 | "execution_count": 1, 55 | "metadata": { 56 | "collapsed": false 57 | }, 58 | "outputs": [ 59 | { 60 | "name": "stdout", 61 | "output_type": "stream", 62 | "text": [ 63 | "False\n", 64 | "True\n" 65 | ] 66 | } 67 | ], 68 | "source": [ 69 | "from __future__ import print_function\n", 70 | "\n", 71 | "def binarySearch(alist, item):\n", 72 | " first = 0\n", 73 | " last = len(alist)-1\n", 74 | " found = False\n", 75 | "\n", 76 | " while first<=last and not found:\n", 77 | " midpoint = (first + last)//2\n", 78 | " if alist[midpoint] == item:\n", 79 | " found = True\n", 80 | " else:\n", 81 | " if item < alist[midpoint]:\n", 82 | " last = midpoint-1\n", 83 | " else:\n", 84 | " first = midpoint+1\n", 85 | "\n", 86 | " return found\n", 87 | "\n", 88 | "testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,]\n", 89 | "print(binarySearch(testlist, 3))\n", 90 | "print(binarySearch(testlist, 13))" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "### 用Hash实现一个Map抽象数据类型\n", 98 | "\n", 99 | "Hash碰到collision的时候一般有两种解决方法:\n", 100 | "\n", 101 | "- 开放寻址(open addressing):寻找下一个空的slot;\n", 102 | "- 链接法(Chaining):允许一个hash值对应的slot中可以存放多个元素。\n", 103 | "\n", 104 | "![](https://github.com/applenob/algorithm_note/raw/master/res/map.png)\n", 105 | "\n", 106 | "使用Hash的Map,查找的理想的时间复杂度是$O(1)$。\n", 107 | "\n", 108 | "实际上,对于占用度是$λ$的HashTable,开放寻址的线性探测的比较次数是:$\\frac{1}{2}(1+\\frac{1}{1−λ})$;链接法的比较次数是:$\\frac{1}{2}(1+(\\frac{1}{1−λ})^2)$。" 109 | ] 110 | }, 111 | { 112 | "cell_type": "code", 113 | "execution_count": 5, 114 | "metadata": { 115 | "collapsed": true 116 | }, 117 | "outputs": [], 118 | "source": [ 119 | "class HashTable:\n", 120 | " \n", 121 | " def __init__(self):\n", 122 | " self.size = 11\n", 123 | " self.slots = [None] * self.size\n", 124 | " self.data = [None] * self.size\n", 125 | " \n", 126 | " def put(self,key,data):\n", 127 | " # 计算key的hash值\n", 128 | " hashvalue = self.hashfunction(key,len(self.slots))\n", 129 | "\n", 130 | " if self.slots[hashvalue] == None:\n", 131 | " self.slots[hashvalue] = key\n", 132 | " self.data[hashvalue] = data\n", 133 | " else:\n", 134 | " if self.slots[hashvalue] == key:\n", 135 | " self.data[hashvalue] = data #replace\n", 136 | " else:\n", 137 | " # 碰到collision, rehash\n", 138 | " nextslot = self.rehash(hashvalue,len(self.slots))\n", 139 | " while self.slots[nextslot] != None and self.slots[nextslot] != key:\n", 140 | " nextslot = self.rehash(nextslot,len(self.slots))\n", 141 | "\n", 142 | " if self.slots[nextslot] == None:\n", 143 | " self.slots[nextslot]=key\n", 144 | " self.data[nextslot]=data\n", 145 | " else:\n", 146 | " self.data[nextslot] = data #replace\n", 147 | "\n", 148 | " def hashfunction(self,key,size):\n", 149 | " # 根据key计算hash值\n", 150 | " return key%size\n", 151 | "\n", 152 | " def rehash(self,oldhash,size):\n", 153 | " return (oldhash+1)%size\n", 154 | "\n", 155 | " def get(self,key):\n", 156 | " startslot = self.hashfunction(key,len(self.slots))\n", 157 | "\n", 158 | " data = None\n", 159 | " stop = False\n", 160 | " found = False\n", 161 | " position = startslot\n", 162 | " while self.slots[position] != None and not found and not stop:\n", 163 | " if self.slots[position] == key:\n", 164 | " found = True\n", 165 | " data = self.data[position]\n", 166 | " else:\n", 167 | " position=self.rehash(position,len(self.slots))\n", 168 | " if position == startslot:\n", 169 | " stop = True\n", 170 | " return data\n", 171 | "\n", 172 | " def __getitem__(self,key):\n", 173 | " return self.get(key)\n", 174 | "\n", 175 | " def __setitem__(self,key,data):\n", 176 | " self.put(key,data)" 177 | ] 178 | }, 179 | { 180 | "cell_type": "code", 181 | "execution_count": 6, 182 | "metadata": { 183 | "collapsed": false 184 | }, 185 | "outputs": [ 186 | { 187 | "data": { 188 | "text/plain": [ 189 | "[77, 44, 55, 20, 26, 93, 17, None, None, 31, 54]" 190 | ] 191 | }, 192 | "execution_count": 6, 193 | "metadata": {}, 194 | "output_type": "execute_result" 195 | } 196 | ], 197 | "source": [ 198 | "H=HashTable()\n", 199 | "H[54]=\"cat\"\n", 200 | "H[26]=\"dog\"\n", 201 | "H[93]=\"lion\"\n", 202 | "H[17]=\"tiger\"\n", 203 | "H[77]=\"bird\"\n", 204 | "H[31]=\"cow\"\n", 205 | "H[44]=\"goat\"\n", 206 | "H[55]=\"pig\"\n", 207 | "H[20]=\"chicken\"\n", 208 | "H.slots" 209 | ] 210 | }, 211 | { 212 | "cell_type": "code", 213 | "execution_count": 7, 214 | "metadata": { 215 | "collapsed": false 216 | }, 217 | "outputs": [ 218 | { 219 | "data": { 220 | "text/plain": [ 221 | "['bird',\n", 222 | " 'goat',\n", 223 | " 'pig',\n", 224 | " 'chicken',\n", 225 | " 'dog',\n", 226 | " 'lion',\n", 227 | " 'tiger',\n", 228 | " None,\n", 229 | " None,\n", 230 | " 'cow',\n", 231 | " 'cat']" 232 | ] 233 | }, 234 | "execution_count": 7, 235 | "metadata": {}, 236 | "output_type": "execute_result" 237 | } 238 | ], 239 | "source": [ 240 | "H.data" 241 | ] 242 | }, 243 | { 244 | "cell_type": "markdown", 245 | "metadata": {}, 246 | "source": [ 247 | "### 冒泡排序\n", 248 | "\n", 249 | "默认从小到大排序。 两两比较,逆序则两两互换。\n", 250 | "\n", 251 | "时间复杂度:$O(n^2)$" 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "execution_count": 8, 257 | "metadata": { 258 | "collapsed": false 259 | }, 260 | "outputs": [ 261 | { 262 | "name": "stdout", 263 | "output_type": "stream", 264 | "text": [ 265 | "[17, 20, 26, 31, 44, 54, 55, 77, 93]\n" 266 | ] 267 | } 268 | ], 269 | "source": [ 270 | "def bubbleSort(alist):\n", 271 | " for passnum in range(len(alist)-1,0,-1):\n", 272 | " for i in range(passnum):\n", 273 | " # 逆序\n", 274 | " if alist[i]>alist[i+1]:\n", 275 | " # 互换\n", 276 | " temp = alist[i]\n", 277 | " alist[i] = alist[i+1]\n", 278 | " alist[i+1] = temp\n", 279 | "\n", 280 | "alist = [54,26,93,17,77,31,44,55,20]\n", 281 | "bubbleSort(alist)\n", 282 | "print(alist)" 283 | ] 284 | }, 285 | { 286 | "cell_type": "markdown", 287 | "metadata": {}, 288 | "source": [ 289 | "### 选择排序\n", 290 | "\n", 291 | "每次找到第k大的数,移动到倒数第k位。\n", 292 | "\n", 293 | "时间复杂度:$O(n^2)$" 294 | ] 295 | }, 296 | { 297 | "cell_type": "code", 298 | "execution_count": 9, 299 | "metadata": { 300 | "collapsed": false 301 | }, 302 | "outputs": [ 303 | { 304 | "name": "stdout", 305 | "output_type": "stream", 306 | "text": [ 307 | "[17, 20, 26, 31, 44, 54, 55, 77, 93]\n" 308 | ] 309 | } 310 | ], 311 | "source": [ 312 | "def selectionSort(alist):\n", 313 | " for fillslot in range(len(alist)-1,0,-1):\n", 314 | " positionOfMax=0\n", 315 | " for location in range(1,fillslot+1):\n", 316 | " if alist[location]>alist[positionOfMax]:\n", 317 | " positionOfMax = location\n", 318 | "\n", 319 | " temp = alist[fillslot]\n", 320 | " alist[fillslot] = alist[positionOfMax]\n", 321 | " alist[positionOfMax] = temp\n", 322 | "\n", 323 | "alist = [54,26,93,17,77,31,44,55,20]\n", 324 | "selectionSort(alist)\n", 325 | "print(alist)" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": {}, 331 | "source": [ 332 | "### 插入排序\n", 333 | "\n", 334 | "递归思想,前面的子序列先排完序,再**插入**下一个元素排序。\n", 335 | "\n", 336 | "时间复杂度:$O(n^2)$" 337 | ] 338 | }, 339 | { 340 | "cell_type": "code", 341 | "execution_count": 11, 342 | "metadata": { 343 | "collapsed": false 344 | }, 345 | "outputs": [ 346 | { 347 | "name": "stdout", 348 | "output_type": "stream", 349 | "text": [ 350 | "[17, 20, 26, 31, 44, 54, 55, 77, 93]\n" 351 | ] 352 | } 353 | ], 354 | "source": [ 355 | "def insertionSort(alist):\n", 356 | " for index in range(1,len(alist)):\n", 357 | "\n", 358 | " currentvalue = alist[index]\n", 359 | " position = index\n", 360 | "\n", 361 | " while position>0 and alist[position-1]>currentvalue:\n", 362 | " # 大于插入元素的元素后移\n", 363 | " alist[position]=alist[position-1]\n", 364 | " # 插入元素前移\n", 365 | " position = position-1\n", 366 | "\n", 367 | " alist[position]=currentvalue\n", 368 | "\n", 369 | "alist = [54,26,93,17,77,31,44,55,20]\n", 370 | "insertionSort(alist)\n", 371 | "print(alist)" 372 | ] 373 | }, 374 | { 375 | "cell_type": "markdown", 376 | "metadata": {}, 377 | "source": [ 378 | "### 希尔排序\n", 379 | "\n", 380 | "使用由大到小的gap,将将list分割成几个sublist,对每个sublist做插入排序。\n", 381 | "\n", 382 | "![](https://github.com/applenob/algorithm_note/raw/master/res/Shell-Sort.png)\n", 383 | "\n", 384 | "时间复杂度:介于$O(n)$和$O(n^2)$之间。" 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": 12, 390 | "metadata": { 391 | "collapsed": false 392 | }, 393 | "outputs": [ 394 | { 395 | "name": "stdout", 396 | "output_type": "stream", 397 | "text": [ 398 | "After increments of size 4 The list is [20, 26, 44, 17, 54, 31, 93, 55, 77]\n", 399 | "After increments of size 2 The list is [20, 17, 44, 26, 54, 31, 77, 55, 93]\n", 400 | "After increments of size 1 The list is [17, 20, 26, 31, 44, 54, 55, 77, 93]\n", 401 | "[17, 20, 26, 31, 44, 54, 55, 77, 93]\n" 402 | ] 403 | } 404 | ], 405 | "source": [ 406 | "def shellSort(alist):\n", 407 | " # gap为len的一半, gap等于sublistcount\n", 408 | " sublistcount = len(alist)//2\n", 409 | " while sublistcount > 0:\n", 410 | "\n", 411 | " for startposition in range(sublistcount):\n", 412 | " gapInsertionSort(alist,startposition,sublistcount)\n", 413 | "\n", 414 | " print(\"After increments of size\",sublistcount,\n", 415 | " \"The list is\",alist)\n", 416 | "\n", 417 | " # gap再减半\n", 418 | " sublistcount = sublistcount // 2\n", 419 | "\n", 420 | "def gapInsertionSort(alist,start,gap):\n", 421 | " # 对sublist进行插入排序\n", 422 | " for i in range(start+gap,len(alist),gap):\n", 423 | "\n", 424 | " currentvalue = alist[i]\n", 425 | " position = i\n", 426 | "\n", 427 | " while position>=gap and alist[position-gap]>currentvalue:\n", 428 | " alist[position]=alist[position-gap]\n", 429 | " position = position-gap\n", 430 | "\n", 431 | " alist[position]=currentvalue\n", 432 | " \n", 433 | "alist = [54,26,93,17,77,31,44,55,20]\n", 434 | "shellSort(alist)\n", 435 | "print(alist)\n" 436 | ] 437 | }, 438 | { 439 | "cell_type": "markdown", 440 | "metadata": { 441 | "collapsed": true 442 | }, 443 | "source": [ 444 | "### 合并排序\n", 445 | "\n", 446 | "递归地将一个list拆分成两个sublist,然后merge。\n", 447 | "\n", 448 | "时间复杂度是:$O(nlogn)$。" 449 | ] 450 | }, 451 | { 452 | "cell_type": "code", 453 | "execution_count": 3, 454 | "metadata": { 455 | "collapsed": false 456 | }, 457 | "outputs": [ 458 | { 459 | "name": "stdout", 460 | "output_type": "stream", 461 | "text": [ 462 | "Splitting [54, 26, 93, 17, 77, 31, 44, 55, 20]\n", 463 | "Splitting [54, 26, 93, 17]\n", 464 | "Splitting [54, 26]\n", 465 | "Splitting [54]\n", 466 | "Merging [54]\n", 467 | "Splitting [26]\n", 468 | "Merging [26]\n", 469 | "Merging [26, 54]\n", 470 | "Splitting [93, 17]\n", 471 | "Splitting [93]\n", 472 | "Merging [93]\n", 473 | "Splitting [17]\n", 474 | "Merging [17]\n", 475 | "Merging [17, 93]\n", 476 | "Merging [17, 26, 54, 93]\n", 477 | "Splitting [77, 31, 44, 55, 20]\n", 478 | "Splitting [77, 31]\n", 479 | "Splitting [77]\n", 480 | "Merging [77]\n", 481 | "Splitting [31]\n", 482 | "Merging [31]\n", 483 | "Merging [31, 77]\n", 484 | "Splitting [44, 55, 20]\n", 485 | "Splitting [44]\n", 486 | "Merging [44]\n", 487 | "Splitting [55, 20]\n", 488 | "Splitting [55]\n", 489 | "Merging [55]\n", 490 | "Splitting [20]\n", 491 | "Merging [20]\n", 492 | "Merging [20, 55]\n", 493 | "Merging [20, 44, 55]\n", 494 | "Merging [20, 31, 44, 55, 77]\n", 495 | "Merging [17, 20, 26, 31, 44, 54, 55, 77, 93]\n", 496 | "[17, 20, 26, 31, 44, 54, 55, 77, 93]\n" 497 | ] 498 | } 499 | ], 500 | "source": [ 501 | "def mergeSort(alist):\n", 502 | " print(\"Splitting \",alist)\n", 503 | " if len(alist)>1:\n", 504 | " # 拆分成两个子序列\n", 505 | " mid = len(alist)//2\n", 506 | " # 注意:使用切片的方式空间复杂度较高\n", 507 | " lefthalf = alist[:mid]\n", 508 | " righthalf = alist[mid:]\n", 509 | "\n", 510 | " mergeSort(lefthalf)\n", 511 | " mergeSort(righthalf)\n", 512 | "\n", 513 | " # 执行merge\n", 514 | " i=0\n", 515 | " j=0\n", 516 | " k=0\n", 517 | " while i < len(lefthalf) and j < len(righthalf):\n", 518 | " if lefthalf[i] < righthalf[j]:\n", 519 | " alist[k]=lefthalf[i]\n", 520 | " i=i+1\n", 521 | " else:\n", 522 | " alist[k]=righthalf[j]\n", 523 | " j=j+1\n", 524 | " k=k+1\n", 525 | "\n", 526 | " while i < len(lefthalf):\n", 527 | " alist[k]=lefthalf[i]\n", 528 | " i=i+1\n", 529 | " k=k+1\n", 530 | "\n", 531 | " while j < len(righthalf):\n", 532 | " alist[k]=righthalf[j]\n", 533 | " j=j+1\n", 534 | " k=k+1\n", 535 | " print(\"Merging \",alist)\n", 536 | "\n", 537 | "alist = [54,26,93,17,77,31,44,55,20]\n", 538 | "mergeSort(alist)\n", 539 | "print(alist)" 540 | ] 541 | }, 542 | { 543 | "cell_type": "markdown", 544 | "metadata": {}, 545 | "source": [ 546 | "### 快速排序\n", 547 | "\n", 548 | "递归地执行:小的元素移到pivot左边,大的元素移到pivot的右边。\n", 549 | "\n", 550 | "类比军训时,教官会找出一个同学说,以这位同学为基准,矮的站右边,高的站右边。\n", 551 | "\n", 552 | "一般在实现的时候,上面提到的这步使用**双指针**的技巧实现。\n", 553 | "\n", 554 | "时间复杂度是:$O(nlogn)$。" 555 | ] 556 | }, 557 | { 558 | "cell_type": "code", 559 | "execution_count": null, 560 | "metadata": { 561 | "collapsed": true 562 | }, 563 | "outputs": [], 564 | "source": [ 565 | "def quickSort(alist):\n", 566 | " quickSortHelper(alist,0,len(alist)-1)\n", 567 | "\n", 568 | "def quickSortHelper(alist,first,last):\n", 569 | " if first= pivotvalue and rightmark >= leftmark:\n", 593 | " rightmark = rightmark -1\n", 594 | "\n", 595 | " if rightmark < leftmark:\n", 596 | " done = True\n", 597 | " else:\n", 598 | " # 空间复杂度是1\n", 599 | " temp = alist[leftmark]\n", 600 | " alist[leftmark] = alist[rightmark]\n", 601 | " alist[rightmark] = temp\n", 602 | "\n", 603 | " temp = alist[first]\n", 604 | " alist[first] = alist[rightmark]\n", 605 | " alist[rightmark] = temp\n", 606 | " return rightmark\n", 607 | "\n", 608 | "alist = [54,26,93,17,77,31,44,55,20]\n", 609 | "quickSort(alist)\n", 610 | "print(alist)" 611 | ] 612 | }, 613 | { 614 | "cell_type": "markdown", 615 | "metadata": {}, 616 | "source": [ 617 | "## 作业\n", 618 | "\n", 619 | "[作业原地址](http://interactivepython.org/courselib/static/pythonds/SortSearch/DiscussionQuestions.html)\n", 620 | "\n", 621 | "### q1\n", 622 | "\n", 623 | "计算装填因子$λ$分别是:0.1, 0.25, 0.5, 0.75, 0.9, 0.99时需要的比较次数。\n", 624 | "\n", 625 | "计算公式:$\\frac{1}{2}(1+\\frac{1}{1−λ})$。\n", 626 | "\n", 627 | "直接封装一个函数:" 628 | ] 629 | }, 630 | { 631 | "cell_type": "code", 632 | "execution_count": 15, 633 | "metadata": { 634 | "collapsed": false 635 | }, 636 | "outputs": [ 637 | { 638 | "name": "stdout", 639 | "output_type": "stream", 640 | "text": [ 641 | "0.1 : 1.05555555556\n", 642 | "0.25 : 1.16666666667\n", 643 | "0.5 : 1.5\n", 644 | "0.75 : 2.5\n", 645 | "0.9 : 5.5\n", 646 | "0.99 : 50.5\n" 647 | ] 648 | } 649 | ], 650 | "source": [ 651 | "def hash_compare(load_fraction):\n", 652 | " lam = (1 + 1./(1-load_fraction)) / 2\n", 653 | " return lam\n", 654 | "\n", 655 | "for i in [0.1, 0.25, 0.5, 0.75, 0.9, 0.99]:\n", 656 | " print(i, \" : \", hash_compare(i))" 657 | ] 658 | }, 659 | { 660 | "cell_type": "markdown", 661 | "metadata": {}, 662 | "source": [ 663 | "### q2\n", 664 | "\n", 665 | "设计计算字符串的hash值的hash函数。\n", 666 | "\n", 667 | "使用positional weight:\n", 668 | "\n", 669 | "![](http://interactivepython.org/courselib/static/pythonds/_images/stringhash2.png)\n" 670 | ] 671 | }, 672 | { 673 | "cell_type": "code", 674 | "execution_count": 21, 675 | "metadata": { 676 | "collapsed": false 677 | }, 678 | "outputs": [ 679 | { 680 | "name": "stdout", 681 | "output_type": "stream", 682 | "text": [ 683 | "641\n", 684 | "3\n", 685 | "1687\n", 686 | "4\n" 687 | ] 688 | }, 689 | { 690 | "data": { 691 | "text/plain": [ 692 | "4" 693 | ] 694 | }, 695 | "execution_count": 21, 696 | "metadata": {}, 697 | "output_type": "execute_result" 698 | } 699 | ], 700 | "source": [ 701 | "def hash_function_4str(key_str, size):\n", 702 | " hash_value = 0\n", 703 | " for i, c in enumerate(key_str):\n", 704 | " hash_value += (i+1)* ord(c)\n", 705 | " print(hash_value)\n", 706 | " hash_value %= size\n", 707 | " print(hash_value)\n", 708 | " return hash_value\n", 709 | " \n", 710 | " \n", 711 | "hash_function_4str(\"cat\", 11)\n", 712 | "hash_function_4str(\"happy\", 11)" 713 | ] 714 | }, 715 | { 716 | "cell_type": "markdown", 717 | "metadata": {}, 718 | "source": [ 719 | "### q4\n", 720 | "\n", 721 | "调研string的hash函数:\n", 722 | "\n", 723 | "参考[这篇博客](https://www.byvoid.com/zhs/blog/string-hash-compare)" 724 | ] 725 | }, 726 | { 727 | "cell_type": "markdown", 728 | "metadata": {}, 729 | "source": [ 730 | "### q9\n", 731 | "\n", 732 | "修改快排中pivot的位置(原算法的pivot是第一个元素,题目推荐尝试中间元素),对比性能。" 733 | ] 734 | }, 735 | { 736 | "cell_type": "code", 737 | "execution_count": 19, 738 | "metadata": { 739 | "collapsed": false 740 | }, 741 | "outputs": [ 742 | { 743 | "name": "stdout", 744 | "output_type": "stream", 745 | "text": [ 746 | "pivotvalue: 77\n", 747 | "pivotvalue: 17\n", 748 | "pivotvalue: 55\n", 749 | "pivotvalue: 54\n", 750 | "pivotvalue: 31\n", 751 | "pivotvalue: 20\n", 752 | "pivotvalue: 26\n", 753 | "[17, 20, 26, 31, 44, 54, 55, 77, 93]\n" 754 | ] 755 | } 756 | ], 757 | "source": [ 758 | "def quickSort(alist):\n", 759 | " quickSortHelper(alist,0,len(alist)-1)\n", 760 | "\n", 761 | "def quickSortHelper(alist,first,last):\n", 762 | " if first= pivotvalue and rightmark >= leftmark and rightmark > first:\n", 787 | " rightmark = rightmark -1\n", 788 | "\n", 789 | " if rightmark < leftmark or rightmark == first:\n", 790 | " done = True\n", 791 | " else:\n", 792 | " # 空间复杂度是1\n", 793 | " temp = alist[leftmark]\n", 794 | " alist[leftmark] = alist[rightmark]\n", 795 | " alist[rightmark] = temp\n", 796 | " temp = alist[pivotindex]\n", 797 | " alist[pivotindex] = alist[rightmark]\n", 798 | " alist[rightmark] = temp\n", 799 | " return leftmark\n", 800 | "\n", 801 | "alist = [54,26,93,17,77,31,44,55,20]\n", 802 | "quickSort(alist)\n", 803 | "# partition(alist,0, 8)\n", 804 | "print(alist)" 805 | ] 806 | }, 807 | { 808 | "cell_type": "markdown", 809 | "metadata": {}, 810 | "source": [ 811 | "pivot设置在中间位置可以让已经排好序的list不用再做交换操作。" 812 | ] 813 | } 814 | ], 815 | "metadata": { 816 | "anaconda-cloud": {}, 817 | "kernelspec": { 818 | "display_name": "Python [default]", 819 | "language": "python", 820 | "name": "python2" 821 | }, 822 | "language_info": { 823 | "codemirror_mode": { 824 | "name": "ipython", 825 | "version": 2 826 | }, 827 | "file_extension": ".py", 828 | "mimetype": "text/x-python", 829 | "name": "python", 830 | "nbconvert_exporter": "python", 831 | "pygments_lexer": "ipython2", 832 | "version": "2.7.12" 833 | } 834 | }, 835 | "nbformat": 4, 836 | "nbformat_minor": 1 837 | } 838 | -------------------------------------------------------------------------------- /book_learning_1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# 《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业答案(一. intro)\n", 10 | "\n", 11 | "```\n", 12 | "对应本书第一章。\n", 13 | "```\n", 14 | "\n", 15 | "## 目录\n", 16 | "\n", 17 | "* [0.简介](#0.简介)\n", 18 | "* [1.intro](#1.intro)\n", 19 | " + [笔记](#笔记)\n", 20 | " + [作业](#作业)\n", 21 | " + [q1-to-q9 Fraction类](#q1-to-q9)\n", 22 | " + [q10 实现NAND, NOR, 和 XOR](#q10)\n", 23 | " + [q11 实现half-adder](#q11)\n", 24 | " + [q12 实现full adder](#q12)\n", 25 | " \n", 26 | "# 0.简介\n", 27 | "\n", 28 | "![](https://raw.githubusercontent.com/applenob/algorithm_note/master/res/cover.jpg)\n", 29 | "\n", 30 | "这本书的[豆瓣评分](https://book.douban.com/subject/3098386/)高达9.3,python作为接近算法伪码的一种脚本语言,其实用它写算法是极好的,可以将注意力集中在算法本身。\n", 31 | "\n", 32 | "但是由于python性能的问题,用python写算法不算太主流,因此市面上介绍算法的书也多以使用c/c++或者Java居多。\n", 33 | "\n", 34 | "这本书几乎是用python介绍算法豆瓣评分最高的一本书了,网上可以下到pdf,但最好的阅读方式是直接使用这本书的[网站](http://interactivepython.org/courselib/static/pythonds/index.html)。这本书貌似也是拿ipython notebook写的,还可以直接在网站上运行示例程序。self check的部分还有作者视频讲解,这才是编程类书籍的未来嘛!\n", 35 | "\n", 36 | "这里我记录每章的学习笔记,同时记录每章课后作业的个人解决代码,统一使用python3。\n" 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "# 1.intro\n", 44 | "\n", 45 | "## 笔记 \n", 46 | "\n", 47 | "这章主要给python做了个简短介绍,魔术方法部分以前没有仔细学习过,看看感觉挺好用,可以让自定义的方法看起来像内建的方法。\n", 48 | "\n", 49 | "[python 官方文档关于operator的表格](https://docs.python.org/2/library/operator.html)\n", 50 | "\n", 51 | "![](https://raw.githubusercontent.com/applenob/algorithm_note/master/res/table1.png)\n", 52 | "![](https://raw.githubusercontent.com/applenob/algorithm_note/master/res/table2.png)\n", 53 | "\n", 54 | "两个不错的学习链接:\n", 55 | "\n", 56 | "- [PYTHON-进阶-魔术方法小结(方法运算符重载)](http://wklken.me/posts/2012/10/29/python-base-magic.html)\n", 57 | "\n", 58 | "- [Python 魔术方法指南](http://pycoders-weekly-chinese.readthedocs.io/en/latest/issue6/a-guide-to-pythons-magic-methods.html)\n", 59 | "\n", 60 | "## 作业\n", 61 | "\n", 62 | "[作业链接](http://interactivepython.org/courselib/static/pythonds/Introduction/ProgrammingExercises.html)\n", 63 | "\n", 64 | "### q1 to q9\n", 65 | "\n", 66 | "完善处理分式的Fraction类。" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 1, 72 | "metadata": { 73 | "collapsed": true 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "def gcd(m,n):\n", 78 | " \"\"\"求两个数最大公因数\"\"\"\n", 79 | " while m%n != 0:\n", 80 | " oldm = m\n", 81 | " oldn = n\n", 82 | "\n", 83 | " m = oldn\n", 84 | " n = oldm%oldn\n", 85 | " return n\n", 86 | "\n", 87 | "class Fraction:\n", 88 | " \"\"\"处理分数的类\"\"\"\n", 89 | " def __init__(self,top,bottom):\n", 90 | " # q5 检查分子分母是否都是整数\n", 91 | " if not isinstance(top, int) or not isinstance(bottom, int):\n", 92 | " raise Exception(\"top or bottom of Fraction is not int type!\")\n", 93 | " # q2 初始化时直接约分\n", 94 | " common = gcd(top,bottom)\n", 95 | " self.num = top//common\n", 96 | " self.den = bottom//common\n", 97 | " \n", 98 | "\n", 99 | " def __str__(self):\n", 100 | " return str(self.num)+\"/\"+str(self.den)\n", 101 | "\n", 102 | " def show(self):\n", 103 | " print(self.num,\"/\",self.den)\n", 104 | "\n", 105 | " def __add__(self,other):\n", 106 | " # q2\n", 107 | " newnum = self.num*other.den + \\\n", 108 | " self.den*other.num\n", 109 | " newden = self.den * other.den\n", 110 | " return Fraction(newnum,newden)\n", 111 | "\n", 112 | " def __eq__(self, other):\n", 113 | " firstnum = self.num * other.den\n", 114 | " secondnum = other.num * self.den\n", 115 | "\n", 116 | " return firstnum == secondnum\n", 117 | " \n", 118 | " # q1\n", 119 | " def getNum(self):\n", 120 | " return self.num\n", 121 | " \n", 122 | " def getDen(self):\n", 123 | " return self.den\n", 124 | " \n", 125 | " # q3\n", 126 | " def __sub__(self, other):\n", 127 | " newnum = self.num*other.den - \\\n", 128 | " other.num*self.den\n", 129 | " newden = self.den * other.den\n", 130 | " return Fraction(newnum, newden)\n", 131 | " \n", 132 | " def __mul__(self, other):\n", 133 | " return Fraction(self.num*other.num, self.den*other.den)\n", 134 | " \n", 135 | " def __truediv__(self, other):\n", 136 | " return Fraction(self.num*other.den, self.den*other.num)\n", 137 | " \n", 138 | " # q4\n", 139 | " def __gt__(self, other): \n", 140 | " if self.num*other.den > self.den*other.num:\n", 141 | " return True\n", 142 | " else:\n", 143 | " return False\n", 144 | " \n", 145 | " def __ge__(self, other): \n", 146 | " if self.num*other.den >= self.den*other.num:\n", 147 | " return True\n", 148 | " else:\n", 149 | " return False\n", 150 | " \n", 151 | " def __lt__(self, other): \n", 152 | " if self.num*other.den < self.den*other.num:\n", 153 | " return True\n", 154 | " else:\n", 155 | " return False\n", 156 | " \n", 157 | " def __le__(self, other): \n", 158 | " if self.num*other.den < self.den*other.num:\n", 159 | " return True\n", 160 | " else:\n", 161 | " return False\n", 162 | " \n", 163 | " def __ne__(self, other): \n", 164 | " if self.num*other.den != self.den*other.num:\n", 165 | " return True\n", 166 | " else:\n", 167 | " return False\n", 168 | " \n", 169 | " # q7\n", 170 | " def __radd__(self,other_int):\n", 171 | " \"\"\"\n", 172 | " Python will first try (4).__add__(myobj), \n", 173 | " and if that returns NotImplemented Python will check if \n", 174 | " the right-hand operand implements __radd__, and if it does, \n", 175 | " it will call myobj.__radd__(4) rather than raising a TypeError.\n", 176 | " \"\"\"\n", 177 | " newnum = self.num + \\\n", 178 | " self.den*other_int\n", 179 | " return Fraction(newnum,self.den)\n", 180 | " \n", 181 | " # q8\n", 182 | " def __iadd__(self, other):\n", 183 | " \"\"\"a = iadd(a, b) is equivalent to a += b.\"\"\"\n", 184 | " newnum = self.num*other.den + \\\n", 185 | " self.den*other.num\n", 186 | " newden = self.den * other.den\n", 187 | " return Fraction(newnum, newden)\n", 188 | " \n", 189 | " # q9\n", 190 | " def __repr__(self):\n", 191 | " \"\"\"\n", 192 | " In short, the goal of __repr__ is to be unambiguous and __str__ is to be readable.\n", 193 | " 也就是说,__repr__是用于输出更多的关于对象的信息的,而__str__是为了print好看的。\n", 194 | " \"\"\"\n", 195 | " return \"num:{}, den:{}\".format(self.num, self.den)" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": 2, 201 | "metadata": { 202 | "collapsed": false 203 | }, 204 | "outputs": [ 205 | { 206 | "name": "stdout", 207 | "output_type": "stream", 208 | "text": [ 209 | "7/6\n", 210 | "False\n" 211 | ] 212 | } 213 | ], 214 | "source": [ 215 | "x = Fraction(1,2)\n", 216 | "y = Fraction(2,3)\n", 217 | "print(x+y)\n", 218 | "print(x == y)" 219 | ] 220 | }, 221 | { 222 | "cell_type": "code", 223 | "execution_count": 3, 224 | "metadata": { 225 | "collapsed": false 226 | }, 227 | "outputs": [ 228 | { 229 | "name": "stdout", 230 | "output_type": "stream", 231 | "text": [ 232 | "1\n", 233 | "2\n" 234 | ] 235 | } 236 | ], 237 | "source": [ 238 | "# q1\n", 239 | "print(x.getNum())\n", 240 | "print(x.getDen())" 241 | ] 242 | }, 243 | { 244 | "cell_type": "code", 245 | "execution_count": 4, 246 | "metadata": { 247 | "collapsed": false 248 | }, 249 | "outputs": [ 250 | { 251 | "name": "stdout", 252 | "output_type": "stream", 253 | "text": [ 254 | "2/1\n" 255 | ] 256 | } 257 | ], 258 | "source": [ 259 | "# q2\n", 260 | "z = Fraction(10, 5)\n", 261 | "print(z)" 262 | ] 263 | }, 264 | { 265 | "cell_type": "code", 266 | "execution_count": 5, 267 | "metadata": { 268 | "collapsed": false 269 | }, 270 | "outputs": [ 271 | { 272 | "name": "stdout", 273 | "output_type": "stream", 274 | "text": [ 275 | "3/4\n" 276 | ] 277 | } 278 | ], 279 | "source": [ 280 | "# q3\n", 281 | "print(x/y)" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 6, 287 | "metadata": { 288 | "collapsed": false 289 | }, 290 | "outputs": [ 291 | { 292 | "name": "stdout", 293 | "output_type": "stream", 294 | "text": [ 295 | "False\n", 296 | "False\n", 297 | "True\n", 298 | "True\n", 299 | "True\n" 300 | ] 301 | } 302 | ], 303 | "source": [ 304 | "# q4\n", 305 | "print(x>y)\n", 306 | "print(x>=y)\n", 307 | "print(x\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;31m# q5\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mFraction\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1.1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 327 | "\u001b[0;32m\u001b[0m in \u001b[0;36m__init__\u001b[0;34m(self, top, bottom)\u001b[0m\n\u001b[1;32m 14\u001b[0m \u001b[0;31m# q5 检查分子分母是否都是整数\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 15\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtop\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0misinstance\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbottom\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mint\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 16\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"top or bottom of Fraction is not int type!\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 17\u001b[0m \u001b[0;31m# q2 初始化时直接约分\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 18\u001b[0m \u001b[0mcommon\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgcd\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mtop\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0mbottom\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", 328 | "\u001b[0;31mException\u001b[0m: top or bottom of Fraction is not int type!" 329 | ] 330 | } 331 | ], 332 | "source": [ 333 | "# q5\n", 334 | "Fraction(1.1, 2)" 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": 8, 340 | "metadata": { 341 | "collapsed": false 342 | }, 343 | "outputs": [ 344 | { 345 | "name": "stdout", 346 | "output_type": "stream", 347 | "text": [ 348 | "-1/2\n", 349 | "-1\n", 350 | "2\n", 351 | "False\n" 352 | ] 353 | } 354 | ], 355 | "source": [ 356 | "# q6\n", 357 | "w=Fraction(1, -2)\n", 358 | "print(w)\n", 359 | "print(w.getNum())\n", 360 | "print(w.getDen())\n", 361 | "print(w>x)" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": 9, 367 | "metadata": { 368 | "collapsed": false 369 | }, 370 | "outputs": [ 371 | { 372 | "name": "stdout", 373 | "output_type": "stream", 374 | "text": [ 375 | "3/2\n" 376 | ] 377 | } 378 | ], 379 | "source": [ 380 | "# q7\n", 381 | "print(1+x)" 382 | ] 383 | }, 384 | { 385 | "cell_type": "code", 386 | "execution_count": 10, 387 | "metadata": { 388 | "collapsed": false 389 | }, 390 | "outputs": [ 391 | { 392 | "name": "stdout", 393 | "output_type": "stream", 394 | "text": [ 395 | "7/6\n" 396 | ] 397 | } 398 | ], 399 | "source": [ 400 | "# q8\n", 401 | "x += y\n", 402 | "print(x)" 403 | ] 404 | }, 405 | { 406 | "cell_type": "code", 407 | "execution_count": 11, 408 | "metadata": { 409 | "collapsed": false 410 | }, 411 | "outputs": [ 412 | { 413 | "name": "stdout", 414 | "output_type": "stream", 415 | "text": [ 416 | "num:7, den:6\n" 417 | ] 418 | } 419 | ], 420 | "source": [ 421 | "# q9\n", 422 | "\n", 423 | "print(repr(x))" 424 | ] 425 | }, 426 | { 427 | "cell_type": "markdown", 428 | "metadata": {}, 429 | "source": [ 430 | "数字电路部分:" 431 | ] 432 | }, 433 | { 434 | "cell_type": "code", 435 | "execution_count": 12, 436 | "metadata": { 437 | "collapsed": false 438 | }, 439 | "outputs": [ 440 | { 441 | "name": "stdout", 442 | "output_type": "stream", 443 | "text": [ 444 | "Enter Pin A input for gate G1-->1\n", 445 | "Enter Pin B input for gate G1-->1\n", 446 | "Enter Pin A input for gate G2-->1\n", 447 | "Enter Pin B input for gate G2-->1\n", 448 | "0\n", 449 | "Enter Pin A input for gate G1-->1\n", 450 | "Enter Pin B input for gate G1-->1\n", 451 | "Enter Pin A input for gate G2-->1\n", 452 | "Enter Pin B input for gate G2-->1\n", 453 | "1\n" 454 | ] 455 | } 456 | ], 457 | "source": [ 458 | "# 作者实现的部分\n", 459 | " \n", 460 | "class LogicGate:\n", 461 | " \"\"\"逻辑门总类\"\"\"\n", 462 | " def __init__(self,n):\n", 463 | " self.name = n\n", 464 | " self.output = None\n", 465 | "\n", 466 | " def getName(self):\n", 467 | " return self.name\n", 468 | "\n", 469 | " def getOutput(self):\n", 470 | " self.output = self.performGateLogic()\n", 471 | " return self.output\n", 472 | "\n", 473 | "\n", 474 | "class BinaryGate(LogicGate):\n", 475 | " \"\"\"两个输入引脚的门\"\"\"\n", 476 | " def __init__(self,n):\n", 477 | " LogicGate.__init__(self,n)\n", 478 | "\n", 479 | " self.pinA = None\n", 480 | " self.pinB = None\n", 481 | "\n", 482 | " def getPinA(self):\n", 483 | " if self.pinA == None:\n", 484 | " return int(input(\"Enter Pin A input for gate \"+self.getName()+\"-->\"))\n", 485 | " else:\n", 486 | " return self.pinA.getFrom().getOutput()\n", 487 | "\n", 488 | " def getPinB(self):\n", 489 | " if self.pinB == None:\n", 490 | " return int(input(\"Enter Pin B input for gate \"+self.getName()+\"-->\"))\n", 491 | " else:\n", 492 | " return self.pinB.getFrom().getOutput()\n", 493 | " \n", 494 | "\n", 495 | " def setNextPin(self,source):\n", 496 | " \"\"\"有其他逻辑门连接该门的输入\"\"\"\n", 497 | " if self.pinA == None:\n", 498 | " self.pinA = source\n", 499 | " else:\n", 500 | " if self.pinB == None:\n", 501 | " self.pinB = source\n", 502 | " else:\n", 503 | " print(\"Cannot Connect: NO EMPTY PINS on this gate\")\n", 504 | "\n", 505 | "\n", 506 | "class AndGate(BinaryGate):\n", 507 | " \"\"\"与门\"\"\"\n", 508 | " def __init__(self,n):\n", 509 | " BinaryGate.__init__(self,n)\n", 510 | "\n", 511 | " def performGateLogic(self):\n", 512 | "\n", 513 | " a = self.getPinA()\n", 514 | " b = self.getPinB()\n", 515 | " if a==1 and b==1:\n", 516 | " return 1\n", 517 | " else:\n", 518 | " return 0\n", 519 | "\n", 520 | "class OrGate(BinaryGate):\n", 521 | " \"\"\"或门\"\"\"\n", 522 | " def __init__(self,n):\n", 523 | " BinaryGate.__init__(self,n)\n", 524 | "\n", 525 | " def performGateLogic(self):\n", 526 | "\n", 527 | " a = self.getPinA()\n", 528 | " b = self.getPinB()\n", 529 | " if a ==1 or b==1:\n", 530 | " return 1\n", 531 | " else:\n", 532 | " return 0\n", 533 | "\n", 534 | "class UnaryGate(LogicGate):\n", 535 | " \"\"\"单个输入引脚的门\"\"\"\n", 536 | " def __init__(self,n):\n", 537 | " LogicGate.__init__(self,n)\n", 538 | "\n", 539 | " self.pin = None\n", 540 | "\n", 541 | " def getPin(self):\n", 542 | " if self.pin == None:\n", 543 | " return int(input(\"Enter Pin input for gate \"+self.getName()+\"-->\"))\n", 544 | " else:\n", 545 | " return self.pin.getFrom().getOutput()\n", 546 | "\n", 547 | " def setNextPin(self,source):\n", 548 | " \"\"\"有其他逻辑门连接该门的输入\"\"\"\n", 549 | " if self.pin == None:\n", 550 | " self.pin = source\n", 551 | " else:\n", 552 | " print(\"Cannot Connect: NO EMPTY PINS on this gate\")\n", 553 | "\n", 554 | "\n", 555 | "class NotGate(UnaryGate):\n", 556 | " \"\"\"非门\"\"\"\n", 557 | " def __init__(self,n):\n", 558 | " UnaryGate.__init__(self,n)\n", 559 | "\n", 560 | " def performGateLogic(self):\n", 561 | " if self.getPin():\n", 562 | " return 0\n", 563 | " else:\n", 564 | " return 1\n", 565 | "\n", 566 | "\n", 567 | "class Connector:\n", 568 | " \"\"\"引线\"\"\"\n", 569 | " def __init__(self, fgate, tgate):\n", 570 | " self.fromgate = fgate\n", 571 | " self.togate = tgate\n", 572 | "\n", 573 | " tgate.setNextPin(self)\n", 574 | "\n", 575 | " def getFrom(self):\n", 576 | " return self.fromgate\n", 577 | "\n", 578 | " def getTo(self):\n", 579 | " return self.togate\n", 580 | "\n", 581 | "\n", 582 | "def main():\n", 583 | " g1 = AndGate(\"G1\")\n", 584 | " g2 = AndGate(\"G2\")\n", 585 | " g3 = OrGate(\"G3\")\n", 586 | " g4 = NotGate(\"G4\")\n", 587 | " c1 = Connector(g1,g3)\n", 588 | " c2 = Connector(g2,g3)\n", 589 | " c3 = Connector(g3,g4)\n", 590 | " print(g4.getOutput())\n", 591 | " print(g4.getPin())\n", 592 | "\n", 593 | "main()" 594 | ] 595 | }, 596 | { 597 | "cell_type": "markdown", 598 | "metadata": {}, 599 | "source": [ 600 | "### q10\n", 601 | "\n", 602 | "实现NAND, NOR, 和 XOR" 603 | ] 604 | }, 605 | { 606 | "cell_type": "code", 607 | "execution_count": 13, 608 | "metadata": { 609 | "collapsed": true 610 | }, 611 | "outputs": [], 612 | "source": [ 613 | "# q10 \n", 614 | "\n", 615 | "class NANDGate(BinaryGate):\n", 616 | " \"\"\"与非门\"\"\"\n", 617 | " def __init__(self,n):\n", 618 | " BinaryGate.__init__(self,n)\n", 619 | "\n", 620 | " def performGateLogic(self):\n", 621 | "\n", 622 | " a = self.getPinA()\n", 623 | " b = self.getPinB()\n", 624 | " if a ==1 and b==1:\n", 625 | " return 0\n", 626 | " else:\n", 627 | " return 1\n", 628 | " \n", 629 | "class NORGate(BinaryGate):\n", 630 | " \"\"\"或非门\"\"\"\n", 631 | " def __init__(self,n):\n", 632 | " BinaryGate.__init__(self,n)\n", 633 | "\n", 634 | " def performGateLogic(self):\n", 635 | "\n", 636 | " a = self.getPinA()\n", 637 | " b = self.getPinB()\n", 638 | " if a ==0 and b==0:\n", 639 | " return 1\n", 640 | " else:\n", 641 | " return 0\n", 642 | " \n", 643 | "class XORGate(BinaryGate):\n", 644 | " \"\"\"异或门\"\"\"\n", 645 | " def __init__(self,n):\n", 646 | " BinaryGate.__init__(self,n)\n", 647 | "\n", 648 | " def performGateLogic(self):\n", 649 | "\n", 650 | " a = self.getPinA()\n", 651 | " b = self.getPinB()\n", 652 | " if a == b:\n", 653 | " return 0\n", 654 | " else:\n", 655 | " return 1" 656 | ] 657 | }, 658 | { 659 | "cell_type": "markdown", 660 | "metadata": {}, 661 | "source": [ 662 | "### q11 \n", 663 | "\n", 664 | "实现half-adder\n", 665 | "\n", 666 | "half adder的定义可参考[wikipedia](https://en.wikipedia.org/wiki/Adder_(electronics)\n", 667 | "\n", 668 | "![](https://raw.githubusercontent.com/applenob/algorithm_note/master/res/Halfadder.gif)" 669 | ] 670 | }, 671 | { 672 | "cell_type": "code", 673 | "execution_count": 14, 674 | "metadata": { 675 | "collapsed": false 676 | }, 677 | "outputs": [ 678 | { 679 | "name": "stdout", 680 | "output_type": "stream", 681 | "text": [ 682 | "Enter Pin input for gate I1-->1\n", 683 | "Enter Pin input for gate I2-->0\n", 684 | "1\n", 685 | "Enter Pin input for gate I1-->1\n", 686 | "Enter Pin input for gate I2-->0\n", 687 | "0\n" 688 | ] 689 | } 690 | ], 691 | "source": [ 692 | "class InGate(UnaryGate):\n", 693 | " \"\"\"输入门\"\"\"\n", 694 | " def __init__(self,n):\n", 695 | " UnaryGate.__init__(self,n)\n", 696 | "\n", 697 | " def performGateLogic(self):\n", 698 | " if self.getPin():\n", 699 | " return 1\n", 700 | " else:\n", 701 | " return 0\n", 702 | "\n", 703 | "class HalfAdder(BinaryGate):\n", 704 | " \n", 705 | " def __init__(self, n):\n", 706 | " BinaryGate.__init__(self, n)\n", 707 | " self.i1 = InGate(\"I1\")\n", 708 | " self.i2 = InGate(\"I2\")\n", 709 | " self.x1 = XORGate(\"X1\")\n", 710 | " self.a1 = AndGate(\"A2\")\n", 711 | " c1 = Connector(self.i1, self.x1)\n", 712 | " c2 = Connector(self.i2, self.x1)\n", 713 | " c3 = Connector(self.i1, self.a1)\n", 714 | " c4 = Connector(self.i2, self.a1)\n", 715 | " \n", 716 | " def setNextPin(self,source):\n", 717 | " if self.i1.pin == None:\n", 718 | " self.i1.pin = source\n", 719 | " elif self.i2.pin == None:\n", 720 | " self.i2.pin = source\n", 721 | " else:\n", 722 | " print(\"Cannot Connect: NO EMPTY PINS on this gate\")\n", 723 | "\n", 724 | " def getSum(self):\n", 725 | " return self.x1.getOutput()\n", 726 | " \n", 727 | " def getCarry(self):\n", 728 | " return self.a1.getOutput()\n", 729 | " \n", 730 | "half_adder = HalfAdder(\"Half_Adder\")\n", 731 | "print(half_adder.getSum())\n", 732 | "print(half_adder.getCarry())" 733 | ] 734 | }, 735 | { 736 | "cell_type": "markdown", 737 | "metadata": {}, 738 | "source": [ 739 | "上面的实现还是有些缺点,就是因为输出有两个,所以相同的输入要输两次。\n", 740 | "\n", 741 | "### q12\n", 742 | "\n", 743 | "实现full adder\n", 744 | "\n", 745 | " $$ S=A\\oplus B\\oplus C_{in}$$\n", 746 | " $$ {\\displaystyle C_{\\text{out}}=(A\\cdot B)+(C_{\\text{in}}\\cdot (A\\oplus B))} $$\n", 747 | " \n", 748 | " ![](https://raw.githubusercontent.com/applenob/algorithm_note/master/res/Fulladder.gif)\n", 749 | " " 750 | ] 751 | }, 752 | { 753 | "cell_type": "code", 754 | "execution_count": 15, 755 | "metadata": { 756 | "collapsed": false 757 | }, 758 | "outputs": [ 759 | { 760 | "name": "stdout", 761 | "output_type": "stream", 762 | "text": [ 763 | "Enter Pin input for gate I1-->1\n", 764 | "Enter Pin input for gate I2-->0\n", 765 | "Enter Pin input for gate I3-->0\n", 766 | "1\n", 767 | "Enter Pin input for gate I1-->1\n", 768 | "Enter Pin input for gate I2-->0\n", 769 | "Enter Pin input for gate I3-->0\n", 770 | "Enter Pin input for gate I1-->1\n", 771 | "Enter Pin input for gate I2-->0\n", 772 | "0\n" 773 | ] 774 | } 775 | ], 776 | "source": [ 777 | "# q12\n", 778 | "\n", 779 | "class FullAdder(LogicGate):\n", 780 | "\n", 781 | " def __init__(self, n):\n", 782 | " LogicGate.__init__(self, n)\n", 783 | " self.i1 = InGate(\"I1\")\n", 784 | " self.i2 = InGate(\"I2\")\n", 785 | " self.i3 = InGate(\"I3\")\n", 786 | " self.x1 = XORGate(\"X1\")\n", 787 | " self.x2 = XORGate(\"X2\")\n", 788 | " self.a1 = AndGate(\"A1\")\n", 789 | " self.a2 = AndGate(\"A2\")\n", 790 | " self.o1 = XORGate(\"O1\")\n", 791 | " c1 = Connector(self.i1, self.x1)\n", 792 | " c2 = Connector(self.i2, self.x1)\n", 793 | " c3 = Connector(self.x1, self.x2)\n", 794 | " c4 = Connector(self.i3, self.x2)\n", 795 | " c5 = Connector(self.x1, self.a1)\n", 796 | " c6 = Connector(self.i3, self.a1)\n", 797 | " c7 = Connector(self.i1, self.a2)\n", 798 | " c8 = Connector(self.i2, self.a2)\n", 799 | " c9 = Connector(self.a1, self.o1)\n", 800 | " c10 = Connector(self.a2, self.o1)\n", 801 | " \n", 802 | " def setNextPin(self,source):\n", 803 | " if self.i1.pin == None:\n", 804 | " self.i1.pin = source\n", 805 | " elif self.i2.pin == None:\n", 806 | " self.i2.pin = source\n", 807 | " elif self.i3.pin == None:\n", 808 | " self.i3.pin = source\n", 809 | " else:\n", 810 | " print(\"Cannot Connect: NO EMPTY PINS on this gate\")\n", 811 | "\n", 812 | " def getSum(self):\n", 813 | " return self.x2.getOutput()\n", 814 | " \n", 815 | " def getCarry(self):\n", 816 | " return self.o1.getOutput()\n", 817 | " \n", 818 | "full_adder = FullAdder(\"Full_Adder\")\n", 819 | "print(full_adder.getSum())\n", 820 | "print(full_adder.getCarry())" 821 | ] 822 | } 823 | ], 824 | "metadata": { 825 | "anaconda-cloud": {}, 826 | "kernelspec": { 827 | "display_name": "Python [conda env:py35]", 828 | "language": "python", 829 | "name": "conda-env-py35-py" 830 | }, 831 | "language_info": { 832 | "codemirror_mode": { 833 | "name": "ipython", 834 | "version": 3 835 | }, 836 | "file_extension": ".py", 837 | "mimetype": "text/x-python", 838 | "name": "python", 839 | "nbconvert_exporter": "python", 840 | "pygments_lexer": "ipython3", 841 | "version": "3.5.2" 842 | } 843 | }, 844 | "nbformat": 4, 845 | "nbformat_minor": 0 846 | } 847 | -------------------------------------------------------------------------------- /book_learning_6.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# 《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业答案(六. Trees and Tree Algorithms)\n", 8 | "\n", 9 | "```\n", 10 | "对应本书第六章。\n", 11 | "```" 12 | ] 13 | }, 14 | { 15 | "cell_type": "markdown", 16 | "metadata": {}, 17 | "source": [ 18 | "## 目录\n", 19 | "\n", 20 | "* [6.Trees and Tree Algorithms](#6.Trees-and-Tree-Algorithms)\n", 21 | " + [笔记](#笔记)\n", 22 | " + [用结点和引用实现的二叉树](#用结点和引用实现的二叉树)\n", 23 | " + [解析树](#解析树)\n", 24 | " + [树的遍历](#树的遍历)\n", 25 | " + [二叉堆](#二叉堆)\n", 26 | " + [二叉查找树](#二叉查找树)\n", 27 | " + [平衡二叉查找树](#平衡二叉查找树)\n", 28 | " + [讨论作业](#讨论作业)" 29 | ] 30 | }, 31 | { 32 | "cell_type": "markdown", 33 | "metadata": {}, 34 | "source": [ 35 | "# 6.Trees and Tree Algorithms\n", 36 | "\n", 37 | "[原目录](http://interactivepython.org/courselib/static/pythonds/Trees/toctree.html)\n", 38 | "\n", 39 | "## 笔记\n", 40 | "\n", 41 | "作者介绍了两个计算机中的树结构:文件系统(File System)和HTML页面。\n", 42 | "\n", 43 | "### 用结点和引用实现的二叉树\n" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 1, 49 | "metadata": { 50 | "collapsed": true 51 | }, 52 | "outputs": [], 53 | "source": [ 54 | "from __future__ import print_function" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 2, 60 | "metadata": { 61 | "collapsed": false 62 | }, 63 | "outputs": [], 64 | "source": [ 65 | "class BinaryTree:\n", 66 | " def __init__(self,rootObj):\n", 67 | " self.key = rootObj\n", 68 | " self.leftChild = None\n", 69 | " self.rightChild = None\n", 70 | "\n", 71 | " def insertLeft(self,newNode):\n", 72 | " if self.leftChild == None:\n", 73 | " self.leftChild = BinaryTree(newNode)\n", 74 | " else:\n", 75 | " t = BinaryTree(newNode)\n", 76 | " t.leftChild = self.leftChild\n", 77 | " self.leftChild = t\n", 78 | "\n", 79 | " def insertRight(self,newNode):\n", 80 | " if self.rightChild == None:\n", 81 | " self.rightChild = BinaryTree(newNode)\n", 82 | " else:\n", 83 | " t = BinaryTree(newNode)\n", 84 | " t.rightChild = self.rightChild\n", 85 | " self.rightChild = t\n", 86 | "\n", 87 | " def getRightChild(self):\n", 88 | " return self.rightChild\n", 89 | "\n", 90 | " def getLeftChild(self):\n", 91 | " return self.leftChild\n", 92 | "\n", 93 | " def setRootVal(self,obj):\n", 94 | " self.key = obj\n", 95 | "\n", 96 | " def getRootVal(self):\n", 97 | " return self.key" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 3, 103 | "metadata": { 104 | "collapsed": false 105 | }, 106 | "outputs": [ 107 | { 108 | "name": "stdout", 109 | "output_type": "stream", 110 | "text": [ 111 | "a\n", 112 | "None\n", 113 | "<__main__.BinaryTree instance at 0x7f55e658a320>\n", 114 | "b\n", 115 | "<__main__.BinaryTree instance at 0x7f55e33bedd0>\n", 116 | "c\n", 117 | "hello\n" 118 | ] 119 | } 120 | ], 121 | "source": [ 122 | "r = BinaryTree('a')\n", 123 | "print(r.getRootVal())\n", 124 | "print(r.getLeftChild())\n", 125 | "r.insertLeft('b')\n", 126 | "print(r.getLeftChild())\n", 127 | "print(r.getLeftChild().getRootVal())\n", 128 | "r.insertRight('c')\n", 129 | "print(r.getRightChild())\n", 130 | "print(r.getRightChild().getRootVal())\n", 131 | "r.getRightChild().setRootVal('hello')\n", 132 | "print(r.getRightChild().getRootVal())" 133 | ] 134 | }, 135 | { 136 | "cell_type": "markdown", 137 | "metadata": {}, 138 | "source": [ 139 | "### 解析树\n", 140 | "\n", 141 | "Parse Tree的构建规则(这里是数学表达式的解析树):\n", 142 | "\n", 143 | "- 如果当前token是`'('`,增加一个结点作为左子结点,下降到左子结点。\n", 144 | "- 如果当前token属于`['+','-','/','*']`,将当前结点的值设为这个操作符,增加一个结点作为右子结点,下降到右子结点。\n", 145 | "- 如果当前token是一个数字,则将当前结点的值设为这个数字,回到父结点。\n", 146 | "- 如果当前token是`')'`,回到父结点。\n", 147 | "\n", 148 | "构建的解析树如下:\n", 149 | "\n", 150 | "![](http://interactivepython.org/courselib/static/pythonds/_images/buildExp8.png)\n", 151 | "\n", 152 | "构建解析树的代码:" 153 | ] 154 | }, 155 | { 156 | "cell_type": "code", 157 | "execution_count": 6, 158 | "metadata": { 159 | "collapsed": false 160 | }, 161 | "outputs": [ 162 | { 163 | "name": "stdout", 164 | "output_type": "stream", 165 | "text": [ 166 | "3\n", 167 | "4\n", 168 | "5\n", 169 | "*\n", 170 | "+\n" 171 | ] 172 | } 173 | ], 174 | "source": [ 175 | "from pythonds.basic.stack import Stack\n", 176 | "from pythonds.trees.binaryTree import BinaryTree\n", 177 | "\n", 178 | "def buildParseTree(fpexp):\n", 179 | " fplist = fpexp.split()\n", 180 | " pStack = Stack()\n", 181 | " eTree = BinaryTree('')\n", 182 | " pStack.push(eTree)\n", 183 | " currentTree = eTree\n", 184 | " for i in fplist:\n", 185 | " # 如果当前token是`'('`,增加一个结点作为左子结点,下降到左子结点。\n", 186 | " if i == '(':\n", 187 | " currentTree.insertLeft('')\n", 188 | " pStack.push(currentTree)\n", 189 | " currentTree = currentTree.getLeftChild()\n", 190 | " # 如果当前token是一个数字,则将当前结点的值设为这个数字,回到父结点。\n", 191 | " elif i not in ['+', '-', '*', '/', ')']:\n", 192 | " currentTree.setRootVal(int(i))\n", 193 | " parent = pStack.pop()\n", 194 | " currentTree = parent\n", 195 | " # 如果当前token属于`['+','-','/','*']`,将当前结点的值设为这个操作符,增加一个结点作为右子结点,下降到右子结点。\n", 196 | " elif i in ['+', '-', '*', '/']:\n", 197 | " currentTree.setRootVal(i)\n", 198 | " currentTree.insertRight('')\n", 199 | " pStack.push(currentTree)\n", 200 | " currentTree = currentTree.getRightChild()\n", 201 | " # 如果当前token是`')'`,回到父结点。\n", 202 | " elif i == ')':\n", 203 | " currentTree = pStack.pop()\n", 204 | " else:\n", 205 | " raise ValueError\n", 206 | " return eTree\n", 207 | "\n", 208 | "pt = buildParseTree(\"( 3 + ( 4 * 5 ) )\")\n", 209 | "pt.postorder() #defined and explained in the next section" 210 | ] 211 | }, 212 | { 213 | "cell_type": "markdown", 214 | "metadata": {}, 215 | "source": [ 216 | "计算表达式的值:" 217 | ] 218 | }, 219 | { 220 | "cell_type": "code", 221 | "execution_count": 7, 222 | "metadata": { 223 | "collapsed": false 224 | }, 225 | "outputs": [ 226 | { 227 | "data": { 228 | "text/plain": [ 229 | "23" 230 | ] 231 | }, 232 | "execution_count": 7, 233 | "metadata": {}, 234 | "output_type": "execute_result" 235 | } 236 | ], 237 | "source": [ 238 | "import operator\n", 239 | "def evaluate(parseTree):\n", 240 | " opers = {'+':operator.add, '-':operator.sub, '*':operator.mul, '/':operator.truediv}\n", 241 | "\n", 242 | " leftC = parseTree.getLeftChild()\n", 243 | " rightC = parseTree.getRightChild()\n", 244 | "\n", 245 | " if leftC and rightC:\n", 246 | " fn = opers[parseTree.getRootVal()]\n", 247 | " return fn(evaluate(leftC),evaluate(rightC))\n", 248 | " else:\n", 249 | " return parseTree.getRootVal()\n", 250 | "evaluate(pt)" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "### 树的遍历\n", 258 | "\n", 259 | "- 1.前序遍历(preorder),即根左右。\n", 260 | "```python\n", 261 | "def preorder(tree):\n", 262 | " if tree:\n", 263 | " print(tree.getRootVal())\n", 264 | " preorder(tree.getLeftChild())\n", 265 | " preorder(tree.getRightChild())\n", 266 | "```\n", 267 | "\n", 268 | "- 2.中序遍历(inorder),即左根右。\n", 269 | "```python\n", 270 | "def inorder(tree):\n", 271 | " if tree != None:\n", 272 | " inorder(tree.getLeftChild())\n", 273 | " print(tree.getRootVal())\n", 274 | " inorder(tree.getRightChild())\n", 275 | "```\n", 276 | "\n", 277 | "- 3.后序遍历(postorder),即左右根。\n", 278 | "```python\n", 279 | "def postorder(tree):\n", 280 | " if tree != None:\n", 281 | " postorder(tree.getLeftChild())\n", 282 | " postorder(tree.getRightChild())\n", 283 | " print(tree.getRootVal())\n", 284 | "```\n", 285 | "\n", 286 | "- 4.层次遍历,即:根/子子/孙孙孙孙\n", 287 | "\n", 288 | "原书没有提及层次遍历,这里做一点补充。" 289 | ] 290 | }, 291 | { 292 | "cell_type": "code", 293 | "execution_count": 9, 294 | "metadata": { 295 | "collapsed": true 296 | }, 297 | "outputs": [], 298 | "source": [ 299 | "def level_order(tree):\n", 300 | " # 维护一个队列\n", 301 | " trav_queue = []\n", 302 | " trav_queue.insert(0, tree)\n", 303 | " while len(trav_queue) != 0:\n", 304 | " cur_node = trav_queue.pop()\n", 305 | " if cur_node.getLeftChild():\n", 306 | " trav_queue.insert(0, cur_node.getLeftChild())\n", 307 | " if cur_node.getRightChild():\n", 308 | " trav_queue.insert(0, cur_node.getRightChild())\n", 309 | " print(cur_node.getRootVal())" 310 | ] 311 | }, 312 | { 313 | "cell_type": "code", 314 | "execution_count": 8, 315 | "metadata": { 316 | "collapsed": false 317 | }, 318 | "outputs": [ 319 | { 320 | "name": "stdout", 321 | "output_type": "stream", 322 | "text": [ 323 | "a\n", 324 | "b\n", 325 | "e\n", 326 | "d\n", 327 | "c\n" 328 | ] 329 | } 330 | ], 331 | "source": [ 332 | "# 对比前序遍历和层次遍历的区别\n", 333 | "def preorder(tree):\n", 334 | " if tree:\n", 335 | " print(tree.getRootVal())\n", 336 | " preorder(tree.getLeftChild())\n", 337 | " preorder(tree.getRightChild())\n", 338 | "r = BinaryTree('a')\n", 339 | "r.insertLeft('b')\n", 340 | "r.insertRight('c')\n", 341 | "r.getLeftChild().insertLeft('d')\n", 342 | "r.getLeftChild().insertLeft('e')\n", 343 | "preorder(r)" 344 | ] 345 | }, 346 | { 347 | "cell_type": "code", 348 | "execution_count": 10, 349 | "metadata": { 350 | "collapsed": false 351 | }, 352 | "outputs": [ 353 | { 354 | "name": "stdout", 355 | "output_type": "stream", 356 | "text": [ 357 | "a\n", 358 | "b\n", 359 | "c\n", 360 | "e\n", 361 | "d\n" 362 | ] 363 | } 364 | ], 365 | "source": [ 366 | "level_order(r)" 367 | ] 368 | }, 369 | { 370 | "cell_type": "markdown", 371 | "metadata": { 372 | "collapsed": true 373 | }, 374 | "source": [ 375 | "### 二叉堆\n", 376 | "\n", 377 | "首先引入`priority queue`的概念,你可以把它想象成操作系统中优先级队列,它的特征是:**每一次出队的是优先级最高的元素。**\n", 378 | "\n", 379 | "如果你使用list实现它,每一次出队前需要重新排序,那么时间复杂度可能是$O(nlogn)$。或者你可以说,只需要第一次排个序就可以,后面每入队一个元素的时候,都加到合适的位置里去,这样插入一个元素的时间复杂度是$O(n)$。\n", 380 | "\n", 381 | "计算机科学家觉得还是不够,于是发明了`二叉堆`,使得`优先级队列`的时间复杂度是$O(logn)$。\n", 382 | "\n", 383 | "二叉堆从外观上来看是一棵完全二叉树,但它有一个性质,即**父结点比子结点小(小顶堆)**。\n", 384 | "\n", 385 | "接下来看看如何实现一个二叉堆:\n", 386 | "\n", 387 | "需要实现的方法:\n", 388 | "\n", 389 | "![](https://github.com/applenob/algorithm_note/raw/master/res/min_heap.png)\n", 390 | "\n", 391 | "实现的时候,首先牢记二叉堆的两个属性:\n", 392 | "\n", 393 | "1.**The Structure Property**,即二叉堆在结构上是一棵完全二叉树。原因是完全二叉树可以直接存在list中。第0个元素置空,第$i$个元素的左子结点和右子结点分别是$i×2$和$i×2+1$,第i个元素的父结点是$i/2$。\n", 394 | "\n", 395 | "2.**Heap Order Property**,即父结点比子结点小。\n", 396 | "\n", 397 | "主要关注两个过程,注意过程中要维护上面的两个属性:\n", 398 | "\n", 399 | "1.插入元素时的**percolate up**,即结点比父结点大的,和父结点互换。\n", 400 | "\n", 401 | "2.删除元素时的**percolate down**,即删除顶结点以后,先将**最后一个结点**补入第一位,然后再不断地将这个结点跟其最小的子结点比,大于这个子结点则互换。\n", 402 | "\n", 403 | "这里如果忘了,最好回去看[原文](http://interactivepython.org/courselib/static/pythonds/Trees/BinaryHeapImplementation.html),有很生动的图,一看就懂。" 404 | ] 405 | }, 406 | { 407 | "cell_type": "code", 408 | "execution_count": null, 409 | "metadata": { 410 | "collapsed": true 411 | }, 412 | "outputs": [], 413 | "source": [ 414 | "class BinHeap:\n", 415 | " def __init__(self):\n", 416 | " self.heapList = [0]\n", 417 | " self.currentSize = 0\n", 418 | " \n", 419 | " def percUp(self,i):\n", 420 | " while i // 2 > 0:\n", 421 | " if self.heapList[i] < self.heapList[i // 2]:\n", 422 | " tmp = self.heapList[i // 2]\n", 423 | " self.heapList[i // 2] = self.heapList[i]\n", 424 | " self.heapList[i] = tmp\n", 425 | " i = i // 2\n", 426 | " \n", 427 | " def insert(self,k):\n", 428 | " self.heapList.append(k)\n", 429 | " self.currentSize = self.currentSize + 1\n", 430 | " self.percUp(self.currentSize)\n", 431 | " \n", 432 | " def percDown(self,i):\n", 433 | " while (i * 2) <= self.currentSize:\n", 434 | " mc = self.minChild(i)\n", 435 | " if self.heapList[i] > self.heapList[mc]:\n", 436 | " tmp = self.heapList[i]\n", 437 | " self.heapList[i] = self.heapList[mc]\n", 438 | " self.heapList[mc] = tmp\n", 439 | " i = mc\n", 440 | "\n", 441 | " def minChild(self,i):\n", 442 | " if i * 2 + 1 > self.currentSize:\n", 443 | " return i * 2\n", 444 | " else:\n", 445 | " if self.heapList[i*2] < self.heapList[i*2+1]:\n", 446 | " return i * 2\n", 447 | " else:\n", 448 | " return i * 2 + 1\n", 449 | " \n", 450 | " def delMin(self):\n", 451 | " retval = self.heapList[1]\n", 452 | " self.heapList[1] = self.heapList[self.currentSize]\n", 453 | " self.currentSize = self.currentSize - 1\n", 454 | " self.heapList.pop()\n", 455 | " self.percDown(1)\n", 456 | " return retval\n", 457 | " \n", 458 | " def buildHeap(self,alist):\n", 459 | " i = len(alist) // 2\n", 460 | " self.currentSize = len(alist)\n", 461 | " self.heapList = [0] + alist[:]\n", 462 | " while (i > 0):\n", 463 | " self.percDown(i)\n", 464 | " i = i - 1" 465 | ] 466 | }, 467 | { 468 | "cell_type": "markdown", 469 | "metadata": { 470 | "collapsed": true 471 | }, 472 | "source": [ 473 | "### 二叉查找树\n", 474 | "\n", 475 | "`二叉查找树(bst)`即这样的二叉树:\n", 476 | "\n", 477 | "所有的父结点的值都比左子结点的大,但比右子结点的小。\n", 478 | "\n", 479 | "值得**注意**的是**bst的删除操作**,分三种情况:\n", 480 | "\n", 481 | "1.该目标结点没有子节点;\n", 482 | "\n", 483 | "![](http://interactivepython.org/courselib/static/pythonds/_images/bstdel1.png)\n", 484 | "\n", 485 | "这种情况直接删除目标结点即可。\n", 486 | "\n", 487 | "2.该目标结点只有一个子节点;\n", 488 | "\n", 489 | "![](http://interactivepython.org/courselib/static/pythonds/_images/bstdel2.png)\n", 490 | "\n", 491 | "这种情况需要先将目标结点删除,再把目标结点的子结点连到目标结点的父结点上\n", 492 | "\n", 493 | "3.该目标结点有两个子节点。\n", 494 | "\n", 495 | "![](http://interactivepython.org/courselib/static/pythonds/_images/bstdel3.png)\n", 496 | "\n", 497 | "这种情况最复杂,删除目标结点后,需要寻找successor,来替代当前结点的位置。\n", 498 | "\n", 499 | "successor必须满足比目标结点所有左子树要大,比所有右子树要小,又分三种情况:\n", 500 | "\n", 501 | "1.目标结点有右子结点,则successor是`右子树的最小结点`(这个结点是比目标结点大的最小结点)。上面图示就是这种情况。\n", 502 | "\n", 503 | "2.目标结点没有右子结点,且是父结点的左子结点,这说明父结点小于目标结点的所有左子树,则successor直接选用目标结点的父结点。\n", 504 | "\n", 505 | "3.目标结点没有右子结点,且是父结点的右子结点,这说明父结点大于目标结点的所有左子树,则递归地再去寻找父结点的successor。" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": 2, 511 | "metadata": { 512 | "collapsed": false 513 | }, 514 | "outputs": [ 515 | { 516 | "name": "stdout", 517 | "output_type": "stream", 518 | "text": [ 519 | "yellow\n", 520 | "at\n" 521 | ] 522 | } 523 | ], 524 | "source": [ 525 | "class TreeNode:\n", 526 | " \"\"\"helper类\"\"\"\n", 527 | " def __init__(self,key,val,left=None,right=None,parent=None):\n", 528 | " self.key = key\n", 529 | " self.payload = val\n", 530 | " self.leftChild = left\n", 531 | " self.rightChild = right\n", 532 | " self.parent = parent\n", 533 | "\n", 534 | " def hasLeftChild(self):\n", 535 | " return self.leftChild\n", 536 | "\n", 537 | " def hasRightChild(self):\n", 538 | " return self.rightChild\n", 539 | "\n", 540 | " def isLeftChild(self):\n", 541 | " return self.parent and self.parent.leftChild == self\n", 542 | "\n", 543 | " def isRightChild(self):\n", 544 | " return self.parent and self.parent.rightChild == self\n", 545 | "\n", 546 | " def isRoot(self):\n", 547 | " return not self.parent\n", 548 | "\n", 549 | " def isLeaf(self):\n", 550 | " return not (self.rightChild or self.leftChild)\n", 551 | "\n", 552 | " def hasAnyChildren(self):\n", 553 | " return self.rightChild or self.leftChild\n", 554 | "\n", 555 | " def hasBothChildren(self):\n", 556 | " return self.rightChild and self.leftChild\n", 557 | "\n", 558 | " def replaceNodeData(self,key,value,lc,rc):\n", 559 | " self.key = key\n", 560 | " self.payload = value\n", 561 | " self.leftChild = lc\n", 562 | " self.rightChild = rc\n", 563 | " if self.hasLeftChild():\n", 564 | " self.leftChild.parent = self\n", 565 | " if self.hasRightChild():\n", 566 | " self.rightChild.parent = self\n", 567 | "\n", 568 | "\n", 569 | "class BinarySearchTree:\n", 570 | "\n", 571 | " def __init__(self):\n", 572 | " self.root = None\n", 573 | " self.size = 0\n", 574 | "\n", 575 | " def length(self):\n", 576 | " return self.size\n", 577 | "\n", 578 | " def __len__(self):\n", 579 | " return self.size\n", 580 | "\n", 581 | " def put(self, key, val):\n", 582 | " if self.root:\n", 583 | " self._put(key, val, self.root)\n", 584 | " else:\n", 585 | " self.root = TreeNode(key,val)\n", 586 | " self.size = self.size + 1\n", 587 | "\n", 588 | " def _put(self, key, val, currentNode):\n", 589 | " # 小于当前结点的值放入左子树,否则放入右子树\n", 590 | " if key < currentNode.key:\n", 591 | " if currentNode.hasLeftChild():\n", 592 | " self._put(key, val, currentNode.leftChild)\n", 593 | " else:\n", 594 | " currentNode.leftChild = TreeNode(key,val,parent=currentNode)\n", 595 | " else:\n", 596 | " if currentNode.hasRightChild():\n", 597 | " self._put(key,val,currentNode.rightChild)\n", 598 | " else:\n", 599 | " currentNode.rightChild = TreeNode(key,val,parent=currentNode)\n", 600 | "\n", 601 | " def __setitem__(self,k,v):\n", 602 | " self.put(k,v)\n", 603 | "\n", 604 | " def get(self,key):\n", 605 | " if self.root:\n", 606 | " res = self._get(key,self.root)\n", 607 | " if res:\n", 608 | " return res.payload\n", 609 | " else:\n", 610 | " return None\n", 611 | " else:\n", 612 | " return None\n", 613 | "\n", 614 | " def _get(self,key,currentNode):\n", 615 | " if not currentNode:\n", 616 | " return None\n", 617 | " elif currentNode.key == key:\n", 618 | " return currentNode\n", 619 | " elif key < currentNode.key:\n", 620 | " return self._get(key,currentNode.leftChild)\n", 621 | " else:\n", 622 | " return self._get(key,currentNode.rightChild)\n", 623 | "\n", 624 | " def __getitem__(self, key):\n", 625 | " return self.get(key)\n", 626 | "\n", 627 | " def __contains__(self, key):\n", 628 | " if self._get(key, self.root):\n", 629 | " return True\n", 630 | " else:\n", 631 | " return False\n", 632 | "\n", 633 | " def delete(self, key):\n", 634 | " if self.size > 1:\n", 635 | " nodeToRemove = self._get(key, self.root)\n", 636 | " if nodeToRemove:\n", 637 | " self.remove(nodeToRemove)\n", 638 | " self.size = self.size-1\n", 639 | " else:\n", 640 | " raise KeyError('Error, key not in tree')\n", 641 | " elif self.size == 1 and self.root.key == key:\n", 642 | " self.root = None\n", 643 | " self.size = self.size - 1\n", 644 | " else:\n", 645 | " raise KeyError('Error, key not in tree')\n", 646 | "\n", 647 | " def __delitem__(self, key):\n", 648 | " self.delete(key)\n", 649 | "\n", 650 | " def spliceOut(self):\n", 651 | " if self.isLeaf():\n", 652 | " if self.isLeftChild():\n", 653 | " self.parent.leftChild = None\n", 654 | " else:\n", 655 | " self.parent.rightChild = None\n", 656 | " elif self.hasAnyChildren():\n", 657 | " if self.hasLeftChild():\n", 658 | " if self.isLeftChild():\n", 659 | " self.parent.leftChild = self.leftChild\n", 660 | " else:\n", 661 | " self.parent.rightChild = self.leftChild\n", 662 | " self.leftChild.parent = self.parent\n", 663 | " else:\n", 664 | " if self.isLeftChild():\n", 665 | " self.parent.leftChild = self.rightChild\n", 666 | " else:\n", 667 | " self.parent.rightChild = self.rightChild\n", 668 | " self.rightChild.parent = self.parent\n", 669 | "\n", 670 | " def findSuccessor(self):\n", 671 | " \"\"\"\n", 672 | " 1.结点有右子结点,则successor是右子树的最小结点。\n", 673 | " 2.结点没有右子结点,且是父结点的左子结点,则successor是父结点。\n", 674 | " 3.结点没有右子结点,且是父结点的右子结点,则successor是父结点的successor。\n", 675 | " \"\"\"\n", 676 | " succ = None\n", 677 | " if self.hasRightChild():\n", 678 | " succ = self.rightChild.findMin()\n", 679 | " else:\n", 680 | " if self.parent:\n", 681 | " if self.isLeftChild():\n", 682 | " succ = self.parent\n", 683 | " else:\n", 684 | " self.parent.rightChild = None\n", 685 | " succ = self.parent.findSuccessor()\n", 686 | " self.parent.rightChild = self\n", 687 | " return succ\n", 688 | "\n", 689 | " def findMin(self):\n", 690 | " current = self\n", 691 | " while current.hasLeftChild():\n", 692 | " current = current.leftChild\n", 693 | " return current\n", 694 | "\n", 695 | " def remove(self,currentNode):\n", 696 | " \"\"\"删除结点\"\"\"\n", 697 | " # 该结点没有子结点,即叶子节点,直接删去即可\n", 698 | " if currentNode.isLeaf(): #leaf \n", 699 | " if currentNode == currentNode.parent.leftChild:\n", 700 | " currentNode.parent.leftChild = None\n", 701 | " else:\n", 702 | " currentNode.parent.rightChild = None\n", 703 | " \n", 704 | " # 该结点有两个子结点,最复杂\n", 705 | " elif currentNode.hasBothChildren(): #interior\n", 706 | " succ = currentNode.findSuccessor()\n", 707 | " succ.spliceOut()\n", 708 | " currentNode.key = succ.key\n", 709 | " currentNode.payload = succ.payload\n", 710 | "\n", 711 | " else: # 该结点只有一个子结点\n", 712 | " if currentNode.hasLeftChild():\n", 713 | " if currentNode.isLeftChild():\n", 714 | " currentNode.leftChild.parent = currentNode.parent\n", 715 | " currentNode.parent.leftChild = currentNode.leftChild\n", 716 | " elif currentNode.isRightChild():\n", 717 | " currentNode.leftChild.parent = currentNode.parent\n", 718 | " currentNode.parent.rightChild = currentNode.leftChild\n", 719 | " else:\n", 720 | " # 该结点是root\n", 721 | " currentNode.replaceNodeData(currentNode.leftChild.key,\n", 722 | " currentNode.leftChild.payload,\n", 723 | " currentNode.leftChild.leftChild,\n", 724 | " currentNode.leftChild.rightChild)\n", 725 | " else:\n", 726 | " if currentNode.isLeftChild():\n", 727 | " currentNode.rightChild.parent = currentNode.parent\n", 728 | " currentNode.parent.leftChild = currentNode.rightChild\n", 729 | " elif currentNode.isRightChild():\n", 730 | " currentNode.rightChild.parent = currentNode.parent\n", 731 | " currentNode.parent.rightChild = currentNode.rightChild\n", 732 | " else:\n", 733 | " currentNode.replaceNodeData(currentNode.rightChild.key,\n", 734 | " currentNode.rightChild.payload,\n", 735 | " currentNode.rightChild.leftChild,\n", 736 | " currentNode.rightChild.rightChild)\n", 737 | "\n", 738 | "\n", 739 | "mytree = BinarySearchTree()\n", 740 | "mytree[3]=\"red\"\n", 741 | "mytree[4]=\"blue\"\n", 742 | "mytree[6]=\"yellow\"\n", 743 | "mytree[2]=\"at\"\n", 744 | "\n", 745 | "print(mytree[6])\n", 746 | "print(mytree[2])" 747 | ] 748 | }, 749 | { 750 | "cell_type": "markdown", 751 | "metadata": { 752 | "collapsed": true 753 | }, 754 | "source": [ 755 | "### 平衡二叉查找树\n", 756 | "\n", 757 | "当一般的二叉查找树出现倾斜的情况(skewed tree):\n", 758 | "\n", 759 | "![](http://interactivepython.org/courselib/static/pythonds/_images/skewedTree.png)\n", 760 | "\n", 761 | "时间复杂度右重新变成了$O(n)$,于是引入**平衡二叉查找树(Balanced Binary Search Trees, AVL tree)**,解决倾斜问题(AVL的命名源自其作者的名字)。\n", 762 | "\n", 763 | "**平衡因子(balance factor)**:$balanceFactor=height(leftSubTree)−height(rightSubTree)$,即左子树的高度-右子树的高度。\n", 764 | "\n", 765 | "如果一棵二叉查找树可以被称为**“平衡”**,则其所有子树的平衡因子只能是:-1,0,1三者之一。\n", 766 | "\n", 767 | "先来看看AVL的性能:\n", 768 | "\n", 769 | "来找到**最左倾**的AVL的结点个数和高度:\n", 770 | "\n", 771 | "![](http://interactivepython.org/courselib/static/pythonds/_images/worstAVL.png)\n", 772 | "\n", 773 | "很容易可以发现一个规律,即$N_h=1+N_{h−1}+N_{h−2}$。其中,$h$是树的高度,$N_h$是高度$h$的情况下,最左倾的AVL的结点个数。这个形态和斐波那契数列是一致的。斐波那契数列有一个性质就是,对于足够大的数,后一个比前一个的比值接近于**“黄金分割”**:$Φ=\\frac{1+\\sqrt 5}{2}$。\n", 774 | "\n", 775 | "可以将递推公式转换成:$h=1.44logN_h$,也就是说,AVL的查找操作的复杂度,依然是:$O(logN)$。\n", 776 | "\n", 777 | "现在考虑如何维护平衡,对插入元素的父结点递归地更新平衡因子,直到:1.已经更新到root;2.当前父结点的平衡因子是0。\n", 778 | "\n", 779 | "```python\n", 780 | "def updateBalance(self,node):\n", 781 | " \"\"\"更新平衡因子\"\"\"\n", 782 | " if node.balanceFactor > 1 or node.balanceFactor < -1:\n", 783 | " self.rebalance(node)\n", 784 | " return\n", 785 | " if node.parent != None:\n", 786 | " if node.isLeftChild():\n", 787 | " node.parent.balanceFactor += 1\n", 788 | " elif node.isRightChild():\n", 789 | " node.parent.balanceFactor -= 1\n", 790 | "\n", 791 | " if node.parent.balanceFactor != 0:\n", 792 | " self.updateBalance(node.parent)\n", 793 | "```\n", 794 | "\n", 795 | "获得新的平衡因子以后,需要对不平衡的结点做**旋转(操作)**。\n", 796 | "\n", 797 | "左旋:\n", 798 | "\n", 799 | "![](http://interactivepython.org/courselib/static/pythonds/_images/simpleunbalanced.png)\n", 800 | "\n", 801 | "```python\n", 802 | "def rotateLeft(self,rotRoot):\n", 803 | " # 当前根结点的右子结点变成新的根节点\n", 804 | " newRoot = rotRoot.rightChild\n", 805 | " # 如果新根有左子树,移到当前根结点的右子树上\n", 806 | " rotRoot.rightChild = newRoot.leftChild\n", 807 | " if newRoot.leftChild != None:\n", 808 | " newRoot.leftChild.parent = rotRoot\n", 809 | " # 当前根的父结点传给新根\n", 810 | " newRoot.parent = rotRoot.parent\n", 811 | " if rotRoot.isRoot():\n", 812 | " self.root = newRoot\n", 813 | " else:\n", 814 | " if rotRoot.isLeftChild():\n", 815 | " rotRoot.parent.leftChild = newRoot\n", 816 | " else:\n", 817 | " rotRoot.parent.rightChild = newRoot\n", 818 | " # 当前根变成新根的左子树\n", 819 | " newRoot.leftChild = rotRoot\n", 820 | " rotRoot.parent = newRoot\n", 821 | " # 更新平衡参数\n", 822 | " rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)\n", 823 | " newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)\n", 824 | "```\n", 825 | "\n", 826 | "最后两行的更新平衡参数需要补充解释:\n", 827 | "\n", 828 | "![](http://interactivepython.org/courselib/static/pythonds/_images/bfderive.png)\n", 829 | "\n", 830 | "$newBal(B)=h_A−h_C$,\n", 831 | "\n", 832 | "$oldBal(B)=h_A−h_D$,\n", 833 | "\n", 834 | "对于旋转前:$h_D=1+max(h_C,h_E)$,\n", 835 | "\n", 836 | "而经过旋转后$h_C$和$h_E$是不变的。\n", 837 | "\n", 838 | "于是有:$oldBal(B)=h_A−(1+max(h_C,h_E))$\n", 839 | "\n", 840 | "再将两条表达式相减,有:\n", 841 | "\n", 842 | "$$\n", 843 | "newBal(B) - oldBal(B) = h_A - h_C - (h_A - (1 + max(h_C,h_E))) \\\\\n", 844 | "newBal(B) - oldBal(B) = h_A - h_C - h_A + (1 + max(h_C,h_E)) \\\\\n", 845 | "newBal(B) - oldBal(B) = h_A - h_A + 1 + max(h_C,h_E) - h_C \\\\\n", 846 | "newBal(B) - oldBal(B) = 1 + max(h_C,h_E) - h_C \\\\\n", 847 | "newBal(B) = oldBal(B) + 1 + max(h_C - h_C ,h_E - h_C) \\\\\n", 848 | "newBal(B) = oldBal(B) + 1 + max(0 , -oldBal(D)) \\\\\n", 849 | "newBal(B) = oldBal(B) + 1 - min(0 , oldBal(D)) \\\\\n", 850 | "$$\n", 851 | "即\n", 852 | "```python\n", 853 | "rotRoot.balanceFactor = rotRoot.balanceFactor + 1 - min(newRoot.balanceFactor, 0)\n", 854 | "```\n", 855 | "类似的方法可以推出:\n", 856 | "```python\n", 857 | "newRoot.balanceFactor = newRoot.balanceFactor + 1 + max(rotRoot.balanceFactor, 0)\n", 858 | "```\n", 859 | "还有一个问题,如果是这样的右倾:\n", 860 | "\n", 861 | "![](http://interactivepython.org/courselib/static/pythonds/_images/hardunbalanced.png)\n", 862 | "\n", 863 | "直接进行左旋,结果是:\n", 864 | "\n", 865 | "![](http://interactivepython.org/courselib/static/pythonds/_images/badrotate.png)\n", 866 | "\n", 867 | "结果变成了左倾。\n", 868 | "\n", 869 | "因此在做旋转之前,比如左旋,先检查左子树是否左倾,如果有要先右旋左子树:\n", 870 | "\n", 871 | "![](http://interactivepython.org/courselib/static/pythonds/_images/rotatelr.png)\n", 872 | "\n", 873 | "```python\n", 874 | "def rebalance(self,node):\n", 875 | " if node.balanceFactor < 0:\n", 876 | " if node.rightChild.balanceFactor > 0:\n", 877 | " self.rotateRight(node.rightChild)\n", 878 | " self.rotateLeft(node)\n", 879 | " else:\n", 880 | " self.rotateLeft(node)\n", 881 | " elif node.balanceFactor > 0:\n", 882 | " if node.leftChild.balanceFactor < 0:\n", 883 | " self.rotateLeft(node.leftChild)\n", 884 | " self.rotateRight(node)\n", 885 | " else:\n", 886 | " self.rotateRight(node)\n", 887 | "```" 888 | ] 889 | }, 890 | { 891 | "cell_type": "markdown", 892 | "metadata": { 893 | "collapsed": true 894 | }, 895 | "source": [ 896 | "## 讨论作业" 897 | ] 898 | } 899 | ], 900 | "metadata": { 901 | "anaconda-cloud": {}, 902 | "kernelspec": { 903 | "display_name": "Python [default]", 904 | "language": "python", 905 | "name": "python2" 906 | }, 907 | "language_info": { 908 | "codemirror_mode": { 909 | "name": "ipython", 910 | "version": 2 911 | }, 912 | "file_extension": ".py", 913 | "mimetype": "text/x-python", 914 | "name": "python", 915 | "nbconvert_exporter": "python", 916 | "pygments_lexer": "ipython2", 917 | "version": "2.7.12" 918 | } 919 | }, 920 | "nbformat": 4, 921 | "nbformat_minor": 1 922 | } 923 | -------------------------------------------------------------------------------- /book_learning_4.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "# 《Problem Solving with Algorithms and Data Structures using Python》 的学习笔记和课后作业答案(四. Recursion)\n", 10 | "\n", 11 | "```\n", 12 | "对应本书第四章。\n", 13 | "```" 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "## 目录\n", 21 | "\n", 22 | "* [4.Recursion](#4.Recursion)\n", 23 | " + [笔记](#笔记)\n", 24 | " + [1.分形](#1.分形)\n", 25 | " + [2.汉诺塔](#2.汉诺塔)\n", 26 | " + [3.迷宫问题](#3.迷宫问题)\n", 27 | " + [对比动态规划](#对比动态规划)\n", 28 | " + [作业](#作业)\n", 29 | " + [q1](#q1)\n", 30 | " + [q2](#q2)\n", 31 | " + [q3](#q3)\n", 32 | " + [q4](#q4)\n", 33 | " + [q5](#q5)\n", 34 | " + [q6](#q6)\n", 35 | " + [q7](#q7)\n", 36 | " + [q8](#q8)\n", 37 | " + [q9](#q9)\n", 38 | " + [q10](#q10)\n", 39 | " + [q11](#q11)\n", 40 | " + [q12](#q12)\n", 41 | " + [q13](#q13)\n", 42 | " + [q14](#q14)\n", 43 | " + [q15](#q15)" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "# 4.Recursion\n", 51 | "\n", 52 | "[原目录](http://interactivepython.org/courselib/static/pythonds/Recursion/toctree.html#)\n", 53 | "\n", 54 | "## 笔记\n", 55 | "\n", 56 | "第四章主讲递归,用了很多有意思的例子(可视化的),所以这章的作业会比较有料。\n", 57 | "\n", 58 | "**使用递归的三条件**:\n", 59 | "\n", 60 | "1. 问题必须有**基础情况**;\n", 61 | "2. 问题必须可以被分解,直至基础情况;\n", 62 | "3. 递归算法必须自己调用自己。\n", 63 | "\n", 64 | "**例子**:\n", 65 | "\n", 66 | "下面的例子都用到了python的画图模块turtle,但貌似只是个toy,用于教学的,这里有一篇[入门教程](http://www.jb51.net/article/52659.htm),随便看看。\n" 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "execution_count": 1, 72 | "metadata": { 73 | "collapsed": false 74 | }, 75 | "outputs": [], 76 | "source": [ 77 | "from __future__ import print_function\n", 78 | "import turtle\n", 79 | "\n", 80 | "# 初始化乌龟对象和屏幕对象\n", 81 | "t = turtle.Turtle()\n", 82 | "myWin = turtle.Screen()" 83 | ] 84 | }, 85 | { 86 | "cell_type": "markdown", 87 | "metadata": {}, 88 | "source": [ 89 | "![](https://github.com/applenob/algorithm_note/raw/master/res/turtle_init.png)" 90 | ] 91 | }, 92 | { 93 | "cell_type": "code", 94 | "execution_count": 2, 95 | "metadata": { 96 | "collapsed": true 97 | }, 98 | "outputs": [], 99 | "source": [ 100 | "t.left(90) # 方向默认是朝右,向左九十度使其向正上方" 101 | ] 102 | }, 103 | { 104 | "cell_type": "markdown", 105 | "metadata": {}, 106 | "source": [ 107 | "![](https://github.com/applenob/algorithm_note/raw/master/res/left_90.png)" 108 | ] 109 | }, 110 | { 111 | "cell_type": "markdown", 112 | "metadata": {}, 113 | "source": [ 114 | "- t.up()和t.down()是指画笔的起落,落下则行动中会留下轨迹,起来则行动不留痕迹;\n", 115 | "- t.right(degree)和t.left(degree)调整乌龟的方向;\n", 116 | "- t.forward(distance)和t.backward(distance)使乌龟前进后退;\n", 117 | "- t.goto(x,y)使乌龟移动到指定点;\n", 118 | "- t.color(color_str)设置乌龟颜色;\n", 119 | "- t.fillcolor(color)设置填充颜色,t.begin_fill()在画需要填充颜色的形状前调用,t.end_fill()在画需要填充颜色的形状后调用;\n", 120 | "- t.setheading()设置乌龟方向:0 - east,90 - north,180 - west,270 - south;\n", 121 | "- t.pensize()调整画笔尺寸。" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "### 1.分形\n", 129 | "\n", 130 | "分形(fractal),就是满足如下条件的图形,无论对图像如何进行放大,它的局部图像和总体图像总是一致的。[wikipedia的解释](https://en.wikipedia.org/wiki/Fractal)。大自然中的分形有雪花/海岸线等等。\n", 131 | "\n", 132 | "你可以想象如果你是上帝,你在创造海岸线的时候,首先在空中画了一副轮廓,当你想进一步描绘海岸线的细节时,你下降了一些高度,然后按照你刚才的思路,接着画了一模一样的轮廓,如此循环。\n", 133 | "\n", 134 | "上面这个过程,正好符合递归的思路。\n", 135 | "\n", 136 | "下面的代码,画一棵分形的树:" 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": 2, 142 | "metadata": { 143 | "collapsed": false 144 | }, 145 | "outputs": [], 146 | "source": [ 147 | "def tree(branchLen,t):\n", 148 | " if branchLen > 5:\n", 149 | " t.forward(branchLen)\n", 150 | " t.right(20)\n", 151 | " tree(branchLen-15,t)\n", 152 | " t.left(40)\n", 153 | " tree(branchLen-15,t)\n", 154 | " t.right(20)\n", 155 | " t.backward(branchLen)\n", 156 | "\n", 157 | " \n", 158 | "def draw_tree():\n", 159 | " #t = turtle.Turtle()\n", 160 | " #myWin = turtle.Screen()\n", 161 | " t.left(90)\n", 162 | " t.up()\n", 163 | " t.backward(100)\n", 164 | " t.down()\n", 165 | " t.color(\"green\")\n", 166 | " tree(75,t)\n", 167 | " \n", 168 | "draw_tree()" 169 | ] 170 | }, 171 | { 172 | "cell_type": "markdown", 173 | "metadata": {}, 174 | "source": [ 175 | "![]()" 176 | ] 177 | }, 178 | { 179 | "cell_type": "markdown", 180 | "metadata": {}, 181 | "source": [ 182 | "![](https://github.com/applenob/algorithm_note/raw/master/res/basic_tree.png)\n", 183 | "\n", 184 | "接着画一个分形的三角:" 185 | ] 186 | }, 187 | { 188 | "cell_type": "code", 189 | "execution_count": 4, 190 | "metadata": { 191 | "collapsed": false 192 | }, 193 | "outputs": [], 194 | "source": [ 195 | "def drawTriangle(points,color,myTurtle):\n", 196 | " myTurtle.fillcolor(color)\n", 197 | " myTurtle.up()\n", 198 | " myTurtle.goto(points[0][0],points[0][1])\n", 199 | " myTurtle.down()\n", 200 | " myTurtle.begin_fill()\n", 201 | " myTurtle.goto(points[1][0],points[1][1])\n", 202 | " myTurtle.goto(points[2][0],points[2][1])\n", 203 | " myTurtle.goto(points[0][0],points[0][1])\n", 204 | " myTurtle.end_fill()\n", 205 | "\n", 206 | "def getMid(p1,p2):\n", 207 | " return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]) / 2)\n", 208 | "\n", 209 | "def sierpinski(points,degree,myTurtle):\n", 210 | " colormap = ['blue','red','green','white','yellow',\n", 211 | " 'violet','orange']\n", 212 | " drawTriangle(points,colormap[degree],myTurtle)\n", 213 | " if degree > 0:\n", 214 | " sierpinski([points[0],\n", 215 | " getMid(points[0], points[1]),\n", 216 | " getMid(points[0], points[2])],\n", 217 | " degree-1, myTurtle)\n", 218 | " sierpinski([points[1],\n", 219 | " getMid(points[0], points[1]),\n", 220 | " getMid(points[1], points[2])],\n", 221 | " degree-1, myTurtle)\n", 222 | " sierpinski([points[2],\n", 223 | " getMid(points[2], points[1]),\n", 224 | " getMid(points[0], points[2])],\n", 225 | " degree-1, myTurtle)\n", 226 | "\n", 227 | "def main():\n", 228 | " # myTurtle = turtle.Turtle()\n", 229 | " myTurtle = t\n", 230 | " # myWin = turtle.Screen()\n", 231 | " myPoints = [[-100,-50],[0,100],[100,-50]]\n", 232 | " sierpinski(myPoints,3,myTurtle)\n", 233 | "\n", 234 | "myWin.clearscreen()\n", 235 | "main()" 236 | ] 237 | }, 238 | { 239 | "cell_type": "markdown", 240 | "metadata": {}, 241 | "source": [ 242 | "![](https://github.com/applenob/algorithm_note/raw/master/res/triangle.png)" 243 | ] 244 | }, 245 | { 246 | "cell_type": "markdown", 247 | "metadata": {}, 248 | "source": [ 249 | "### 2.汉诺塔\n", 250 | "\n", 251 | "汉诺塔规则:\n", 252 | "\n", 253 | "有三根柱子和一些大小不同的盘子。初始状态是所有盘子从大到小一直往上堆积在起始柱上。\n", 254 | "\n", 255 | "每次可以移动一个盘子,到其他两根柱子上,必须保证大的盘子在下面,小的盘子在上面。\n", 256 | "\n", 257 | "最终的目标是把所有盘子从大到小往上堆积在目标柱上。\n", 258 | "\n", 259 | "![](https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=3171323217,1599602218&fm=23&gp=0.jpg)\n", 260 | "\n", 261 | "**分析**:\n", 262 | "\n", 263 | "考虑问题的分解:\n", 264 | "\n", 265 | "如果有n片盘子,那么可以分解成先将n-1片移到中间柱上,再把最大的一片从起始柱移到目标柱上,再把n-1片移到目标柱上。\n", 266 | "\n", 267 | "移动n片盘子和移动n-1片盘子是完全相同的问题,所以可以使用自己调用自己。\n", 268 | "\n", 269 | "那么基础情况呢?如果只是移动一片盘子,那就直接从起始柱移到目标柱上,就解决了。\n", 270 | "\n", 271 | "根据上面的分析,使用递归的三个条件都满足了。" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 4, 277 | "metadata": { 278 | "collapsed": false 279 | }, 280 | "outputs": [ 281 | { 282 | "name": "stdout", 283 | "output_type": "stream", 284 | "text": [ 285 | "('moving disk from', 'A', 'to', 'B')\n", 286 | "('moving disk from', 'A', 'to', 'C')\n", 287 | "('moving disk from', 'B', 'to', 'C')\n", 288 | "('moving disk from', 'A', 'to', 'B')\n", 289 | "('moving disk from', 'C', 'to', 'A')\n", 290 | "('moving disk from', 'C', 'to', 'B')\n", 291 | "('moving disk from', 'A', 'to', 'B')\n" 292 | ] 293 | } 294 | ], 295 | "source": [ 296 | "def moveTower(height,fromPole, toPole, withPole):\n", 297 | " if height >= 1:\n", 298 | " moveTower(height-1,fromPole,withPole,toPole)\n", 299 | " moveDisk(fromPole,toPole)\n", 300 | " moveTower(height-1,withPole,toPole,fromPole)\n", 301 | "\n", 302 | "def moveDisk(fp,tp):\n", 303 | " print(\"moving disk from\",fp,\"to\",tp)\n", 304 | "\n", 305 | "moveTower(3,\"A\",\"B\",\"C\")" 306 | ] 307 | }, 308 | { 309 | "cell_type": "markdown", 310 | "metadata": {}, 311 | "source": [ 312 | "### 3.迷宫问题\n", 313 | "\n", 314 | "想必这个问题就不用介绍了,就是给定起点和迷宫信息,要找到一条出去的路径。\n", 315 | "\n", 316 | "**分析**:\n", 317 | "\n", 318 | "如何找到一条可以出去的路呢?\n", 319 | "\n", 320 | "不考虑出去的路的长短的话,我们可以先考虑当前点可能的操作,无非是上下左右移动(能不能移动另说)。\n", 321 | "\n", 322 | "那么我们可以把问题等价于在当前点的所有可能操作+假设这样做了一步之后的整体搜索。\n", 323 | "\n", 324 | "其实说白了就是深度优先搜索。\n", 325 | "\n", 326 | "作者给出了一个turtle实现的案例(都可以做一个游戏了,老外写书真心认真~)" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 5, 332 | "metadata": { 333 | "collapsed": false 334 | }, 335 | "outputs": [ 336 | { 337 | "data": { 338 | "text/plain": [ 339 | "True" 340 | ] 341 | }, 342 | "execution_count": 5, 343 | "metadata": {}, 344 | "output_type": "execute_result" 345 | } 346 | ], 347 | "source": [ 348 | "PART_OF_PATH = 'O'\n", 349 | "TRIED = '.'\n", 350 | "OBSTACLE = '+'\n", 351 | "DEAD_END = '-'\n", 352 | "\n", 353 | "class Maze:\n", 354 | " def __init__(self,mazeFileName):\n", 355 | " rowsInMaze = 0\n", 356 | " columnsInMaze = 0\n", 357 | " self.mazelist = []\n", 358 | " # 从文件中读取迷宫信息:包括每个坐标的点是墙还是路;起始坐标;迷宫的宽/高\n", 359 | " mazeFile = open(mazeFileName,'r')\n", 360 | " rowsInMaze = 0\n", 361 | " for line in mazeFile:\n", 362 | " rowList = []\n", 363 | " col = 0\n", 364 | " for ch in line[:-1]:\n", 365 | " rowList.append(ch)\n", 366 | " if ch == 'S':\n", 367 | " self.startRow = rowsInMaze\n", 368 | " self.startCol = col\n", 369 | " col = col + 1\n", 370 | " rowsInMaze = rowsInMaze + 1\n", 371 | " self.mazelist.append(rowList)\n", 372 | " columnsInMaze = len(rowList)\n", 373 | "\n", 374 | " self.rowsInMaze = rowsInMaze\n", 375 | " self.columnsInMaze = columnsInMaze\n", 376 | " # x和y的起点默认在屏幕中心,所以要移到\n", 377 | " self.xTranslate = -columnsInMaze/2\n", 378 | " self.yTranslate = rowsInMaze/2\n", 379 | " # self.t = turtle.Turtle()\n", 380 | " self.t = t\n", 381 | " self.t.shape('turtle')\n", 382 | " # self.wn = turtle.Screen()\n", 383 | " self.wn = myWin\n", 384 | " # 设置窗口的左下角坐标和右上角坐标\n", 385 | " self.wn.setworldcoordinates(-(columnsInMaze-1)/2-.5,-(rowsInMaze-1)/2-.5,\n", 386 | " (columnsInMaze-1)/2+.5,(rowsInMaze-1)/2+.5)\n", 387 | "\n", 388 | " def drawMaze(self):\n", 389 | " \"\"\"画迷宫\"\"\"\n", 390 | " self.t.speed(10)\n", 391 | " self.wn.tracer(0)\n", 392 | " for y in range(self.rowsInMaze):\n", 393 | " for x in range(self.columnsInMaze):\n", 394 | " if self.mazelist[y][x] == OBSTACLE:\n", 395 | " self.drawCenteredBox(x+self.xTranslate,-y+self.yTranslate,'orange')\n", 396 | " self.t.color('black')\n", 397 | " self.t.fillcolor('blue')\n", 398 | " self.wn.update()\n", 399 | " self.wn.tracer(1)\n", 400 | "\n", 401 | " def drawCenteredBox(self,x,y,color):\n", 402 | " self.t.up()\n", 403 | " self.t.goto(x-.5,y-.5)\n", 404 | " self.t.color(color)\n", 405 | " self.t.fillcolor(color)\n", 406 | " self.t.setheading(90)\n", 407 | " self.t.down()\n", 408 | " self.t.begin_fill()\n", 409 | " for i in range(4):\n", 410 | " self.t.forward(1)\n", 411 | " self.t.right(90)\n", 412 | " self.t.end_fill()\n", 413 | "\n", 414 | " def moveTurtle(self,x,y):\n", 415 | " self.t.up()\n", 416 | " self.t.setheading(self.t.towards(x+self.xTranslate,-y+self.yTranslate))\n", 417 | " self.t.goto(x+self.xTranslate,-y+self.yTranslate)\n", 418 | "\n", 419 | " def dropBreadcrumb(self,color):\n", 420 | " self.t.dot(10,color)\n", 421 | "\n", 422 | " def updatePosition(self,row,col,val=None):\n", 423 | " if val:\n", 424 | " self.mazelist[row][col] = val\n", 425 | " self.moveTurtle(col,row)\n", 426 | "\n", 427 | " if val == PART_OF_PATH:\n", 428 | " color = 'green'\n", 429 | " elif val == OBSTACLE:\n", 430 | " color = 'red'\n", 431 | " elif val == TRIED:\n", 432 | " color = 'black'\n", 433 | " elif val == DEAD_END:\n", 434 | " color = 'red'\n", 435 | " else:\n", 436 | " color = None\n", 437 | "\n", 438 | " if color:\n", 439 | " self.dropBreadcrumb(color)\n", 440 | "\n", 441 | " def isExit(self,row,col):\n", 442 | " return (row == 0 or\n", 443 | " row == self.rowsInMaze-1 or\n", 444 | " col == 0 or\n", 445 | " col == self.columnsInMaze-1 )\n", 446 | "\n", 447 | " def __getitem__(self,idx):\n", 448 | " return self.mazelist[idx]\n", 449 | "\n", 450 | "\n", 451 | "def searchFrom(maze, startRow, startColumn):\n", 452 | " # try each of four directions from this point until we find a way out.\n", 453 | " # base Case return values:\n", 454 | " # 1. We have run into an obstacle, return false\n", 455 | " maze.updatePosition(startRow, startColumn)\n", 456 | " if maze[startRow][startColumn] == OBSTACLE :\n", 457 | " return False\n", 458 | " # 2. We have found a square that has already been explored\n", 459 | " if maze[startRow][startColumn] == TRIED or maze[startRow][startColumn] == DEAD_END:\n", 460 | " return False\n", 461 | " # 3. We have found an outside edge not occupied by an obstacle\n", 462 | " if maze.isExit(startRow,startColumn):\n", 463 | " maze.updatePosition(startRow, startColumn, PART_OF_PATH)\n", 464 | " return True\n", 465 | " maze.updatePosition(startRow, startColumn, TRIED)\n", 466 | " # Otherwise, use logical short circuiting to try each direction\n", 467 | " # in turn (if needed)\n", 468 | " found = searchFrom(maze, startRow-1, startColumn) or \\\n", 469 | " searchFrom(maze, startRow+1, startColumn) or \\\n", 470 | " searchFrom(maze, startRow, startColumn-1) or \\\n", 471 | " searchFrom(maze, startRow, startColumn+1)\n", 472 | " if found:\n", 473 | " maze.updatePosition(startRow, startColumn, PART_OF_PATH)\n", 474 | " else:\n", 475 | " maze.updatePosition(startRow, startColumn, DEAD_END)\n", 476 | " return found\n", 477 | "\n", 478 | "\n", 479 | "myWin.clearscreen()\n", 480 | "myMaze = Maze('maze2.txt')\n", 481 | "myMaze.drawMaze()\n", 482 | "myMaze.updatePosition(myMaze.startRow,myMaze.startCol)\n", 483 | "\n", 484 | "searchFrom(myMaze, myMaze.startRow, myMaze.startCol)" 485 | ] 486 | }, 487 | { 488 | "cell_type": "markdown", 489 | "metadata": {}, 490 | "source": [ 491 | "![](https://github.com/applenob/algorithm_note/raw/master/res/maze.png)" 492 | ] 493 | }, 494 | { 495 | "cell_type": "markdown", 496 | "metadata": {}, 497 | "source": [ 498 | "### 对比动态规划\n", 499 | "\n", 500 | "**动态规划(Dynamic Programming)**。\n", 501 | "\n", 502 | "递归虽然好用,写起来简单,代码易读,但是依然有很多问题不适合用递归。\n", 503 | "\n", 504 | "比如说动态规划,拿最简单的**硬币找钱**问题举例子。\n", 505 | "\n", 506 | "比如有三种硬币1,2,5元的,现在有一定数额的总数,请问怎么找前可以使得硬币数最少?\n", 507 | "\n", 508 | "类似的还有**背包问题**:\n", 509 | "\n", 510 | "比如背包可以装的物品有1,2,5公斤的物品,带来的效益分别是1,3,10元,如何安排物品,使得背包的总效益最大?\n", 511 | "\n", 512 | "如果采用递归去解决这些问题,会产生时间复杂度非常大的问题,为什么呢?比如你要求的是规模是10的问题,你把它缩小成规模是5和5的问题,这两个子问题虽然是相同的问题,但由于递归不会记录子问题的答案,所以会计算两次。\n", 513 | "\n", 514 | "那么我们把每次递归计算的不同规模的问题的答案记录下来呢?\n", 515 | "\n", 516 | "当然可以,这会节省很多时间,但在很多情况下,这不是最高明的算法。\n", 517 | "\n", 518 | "如果采用动态规划,**直接从最小规模的问题开始出发,记录所有规模下的问题答案,直到指定的规模,获得结果。**\n", 519 | "\n", 520 | "当然,需要具体问题具体分析。\n", 521 | "\n", 522 | "## 作业\n", 523 | "\n", 524 | "[作业原地址](http://interactivepython.org/courselib/static/pythonds/Recursion/pythondsProgrammingExercises.html)\n", 525 | "\n", 526 | "### q1\n", 527 | "\n", 528 | "用递归的方法写一个阶乘函数:\n" 529 | ] 530 | }, 531 | { 532 | "cell_type": "code", 533 | "execution_count": 6, 534 | "metadata": { 535 | "collapsed": false 536 | }, 537 | "outputs": [ 538 | { 539 | "data": { 540 | "text/plain": [ 541 | "24" 542 | ] 543 | }, 544 | "execution_count": 6, 545 | "metadata": {}, 546 | "output_type": "execute_result" 547 | } 548 | ], 549 | "source": [ 550 | "def factorial(n):\n", 551 | " if n <= 0:\n", 552 | " return 0\n", 553 | " elif n == 1:\n", 554 | " return 1\n", 555 | " else:\n", 556 | " return n*factorial(n-1)\n", 557 | " \n", 558 | "factorial(4)" 559 | ] 560 | }, 561 | { 562 | "cell_type": "markdown", 563 | "metadata": {}, 564 | "source": [ 565 | "### q2\n", 566 | "\n", 567 | "用递归的方法逆转一个list:\n" 568 | ] 569 | }, 570 | { 571 | "cell_type": "code", 572 | "execution_count": 10, 573 | "metadata": { 574 | "collapsed": false 575 | }, 576 | "outputs": [ 577 | { 578 | "name": "stdout", 579 | "output_type": "stream", 580 | "text": [ 581 | "[6, 5, 4, 3, 2, 1]\n" 582 | ] 583 | } 584 | ], 585 | "source": [ 586 | "def reverse(l):\n", 587 | " if len(l) == 1:\n", 588 | " return l\n", 589 | " elif len(l) == 2:\n", 590 | " l[0], l[1] = l[1], l[0]\n", 591 | " return l\n", 592 | " else:\n", 593 | " l[0], l[-1] = l[-1], l[0]\n", 594 | " l[1:-1] = reverse(l[1:-1])\n", 595 | " return l\n", 596 | " \n", 597 | "l = [1,2,3,4,5,6]\n", 598 | "print reverse(l)" 599 | ] 600 | }, 601 | { 602 | "cell_type": "markdown", 603 | "metadata": {}, 604 | "source": [ 605 | "### q3\n", 606 | "\n", 607 | "修改作者的分形树:" 608 | ] 609 | }, 610 | { 611 | "cell_type": "code", 612 | "execution_count": 3, 613 | "metadata": { 614 | "collapsed": false 615 | }, 616 | "outputs": [], 617 | "source": [ 618 | "import random\n", 619 | "\n", 620 | "def myTree(branchLen,t):\n", 621 | " # print branchLen\n", 622 | " if branchLen > 5:\n", 623 | " step = random.randrange(15, 29)\n", 624 | " angle = random.randrange(15, 45)\n", 625 | " t.pensize(max(4, branchLen/step*5))\n", 626 | " if branchLen < 30:\n", 627 | " t.color(\"green\")\n", 628 | " t.forward(branchLen)\n", 629 | " t.right(angle)\n", 630 | " myTree(branchLen-step, t)\n", 631 | " t.left(angle*2)\n", 632 | " myTree(branchLen-step, t)\n", 633 | " t.right(angle)\n", 634 | " t.backward(branchLen)\n", 635 | " t.color(\"brown\")\n", 636 | " \n", 637 | "def my_draw_tree():\n", 638 | " t.left(90)\n", 639 | " t.up()\n", 640 | " t.backward(100)\n", 641 | " t.down()\n", 642 | " t.color(\"brown\")\n", 643 | " myTree(100,t)\n", 644 | " \n", 645 | "myWin.clearscreen() \n", 646 | "my_draw_tree()" 647 | ] 648 | }, 649 | { 650 | "cell_type": "markdown", 651 | "metadata": {}, 652 | "source": [ 653 | "![]()" 654 | ] 655 | }, 656 | { 657 | "cell_type": "markdown", 658 | "metadata": {}, 659 | "source": [ 660 | "![](https://github.com/applenob/algorithm_note/raw/master/res/my_tree.png)\n", 661 | "\n", 662 | "### q4\n", 663 | "\n", 664 | "画一座分形的山:" 665 | ] 666 | }, 667 | { 668 | "cell_type": "code", 669 | "execution_count": 4, 670 | "metadata": { 671 | "collapsed": false 672 | }, 673 | "outputs": [], 674 | "source": [ 675 | "def drawTriangle(points,color,myTurtle):\n", 676 | " myTurtle.fillcolor(color)\n", 677 | " myTurtle.up()\n", 678 | " myTurtle.goto(points[0][0],points[0][1])\n", 679 | " myTurtle.down()\n", 680 | " myTurtle.begin_fill()\n", 681 | " myTurtle.goto(points[1][0],points[1][1])\n", 682 | " myTurtle.goto(points[2][0],points[2][1])\n", 683 | " myTurtle.goto(points[0][0],points[0][1])\n", 684 | " myTurtle.end_fill()\n", 685 | " \n", 686 | "def drawPlatform(points,color,myTurtle):\n", 687 | " \"\"\"画一个梯形的平台,其实也可以直接写一个drawPolygon的方法\"\"\"\n", 688 | " myTurtle.fillcolor(color)\n", 689 | " myTurtle.up()\n", 690 | " myTurtle.goto(points[0][0],points[0][1])\n", 691 | " myTurtle.down()\n", 692 | " myTurtle.begin_fill()\n", 693 | " myTurtle.goto(points[1][0],points[1][1])\n", 694 | " myTurtle.goto(points[2][0],points[2][1])\n", 695 | " myTurtle.goto(points[3][0],points[3][1])\n", 696 | " myTurtle.goto(points[0][0],points[0][1])\n", 697 | " myTurtle.end_fill()\n", 698 | "\n", 699 | "def getMid(p1,p2):\n", 700 | " return ( (p1[0]+p2[0]) / 2, (p1[1] + p2[1]) / 2)\n", 701 | "\n", 702 | "def mountain(width,level,color,myTurtle):\n", 703 | " if width > 9:\n", 704 | " drawTriangle([(-width/2.,level),(-width/3.,level+width/3.),(-width/6.,level)],\n", 705 | " color,myTurtle)\n", 706 | " drawTriangle([(width/6.,level),(width/3.,level+width/3.),(width/2.,level)],\n", 707 | " color,myTurtle)\n", 708 | " drawPlatform([(-width/3.,level),(-width/6.,level+width/3.),\n", 709 | " (width/6.,level+width/3.),(width/3.,level)],\n", 710 | " color,myTurtle)\n", 711 | " mountain(width/3.,level+width/3.,color,myTurtle)\n", 712 | " else:\n", 713 | " drawTriangle([(-width/2.,level),(0.,level+width),(width/2.,level)],\n", 714 | " color,myTurtle)\n", 715 | "\n", 716 | "def main():\n", 717 | " myTurtle = t\n", 718 | " t.color(\"white\")\n", 719 | " mountain(900,-450,\"blue\",myTurtle)\n", 720 | "\n", 721 | "myWin.clearscreen()\n", 722 | "main()" 723 | ] 724 | }, 725 | { 726 | "cell_type": "markdown", 727 | "metadata": {}, 728 | "source": [ 729 | "![](https://github.com/applenob/algorithm_note/raw/master/res/mountain.png)\n", 730 | "\n", 731 | "画风有点像葫芦娃。" 732 | ] 733 | }, 734 | { 735 | "cell_type": "markdown", 736 | "metadata": {}, 737 | "source": [ 738 | "### q5\n", 739 | "\n", 740 | "用递归的方法解决斐波那契数列:" 741 | ] 742 | }, 743 | { 744 | "cell_type": "code", 745 | "execution_count": 15, 746 | "metadata": { 747 | "collapsed": false 748 | }, 749 | "outputs": [ 750 | { 751 | "name": "stdout", 752 | "output_type": "stream", 753 | "text": [ 754 | "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]\n" 755 | ] 756 | } 757 | ], 758 | "source": [ 759 | "def genFibonacci(n):\n", 760 | " \"\"\"递归方法\"\"\"\n", 761 | " if n<=2:\n", 762 | " return 1\n", 763 | " else :\n", 764 | " return genFibonacci(n-1)+genFibonacci(n-2)\n", 765 | " \n", 766 | "genFibonacci(7)\n", 767 | "l = []\n", 768 | "for i in range(1, 20):\n", 769 | " l.append(genFibonacci(i))\n", 770 | "print l" 771 | ] 772 | }, 773 | { 774 | "cell_type": "code", 775 | "execution_count": 16, 776 | "metadata": { 777 | "collapsed": false 778 | }, 779 | "outputs": [ 780 | { 781 | "name": "stdout", 782 | "output_type": "stream", 783 | "text": [ 784 | "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181]\n" 785 | ] 786 | } 787 | ], 788 | "source": [ 789 | "def genFibonacciSequence(n):\n", 790 | " \"\"\"循环方法\"\"\"\n", 791 | " if n<=2:\n", 792 | " return [1]*n\n", 793 | " else:\n", 794 | " l = [1, 1]\n", 795 | " for i in range(2, n):\n", 796 | " l.append(l[i-1]+l[i-2])\n", 797 | " return l\n", 798 | " \n", 799 | "print genFibonacciSequence(19)" 800 | ] 801 | }, 802 | { 803 | "cell_type": "markdown", 804 | "metadata": {}, 805 | "source": [ 806 | "分析:\n", 807 | "\n", 808 | "如果只是生成一个指定序号的斐波那契数,那么两种方法的时间复杂度是一样的。\n", 809 | "\n", 810 | "但是如果生成的是一个序列的话,还是循环方法时间复杂度小,是$O(n)$;递归方法的时间复杂度是$O(n^2)$。\n", 811 | "\n", 812 | "关键还是在于递归方法没有记录计算过的指定序号的斐波那契数的数值。\n" 813 | ] 814 | }, 815 | { 816 | "cell_type": "markdown", 817 | "metadata": {}, 818 | "source": [ 819 | "### q6\n", 820 | "\n", 821 | "改进汉诺塔的算法,使用3个栈记录三个柱子的实时情况:" 822 | ] 823 | }, 824 | { 825 | "cell_type": "code", 826 | "execution_count": 19, 827 | "metadata": { 828 | "collapsed": false 829 | }, 830 | "outputs": [ 831 | { 832 | "name": "stdout", 833 | "output_type": "stream", 834 | "text": [ 835 | "moving disk from A to B\n", 836 | "Tower A: [3, 2]\n", 837 | "Tower B: [1]\n", 838 | "Tower C: []\n", 839 | "moving disk from A to C\n", 840 | "Tower A: [3]\n", 841 | "Tower B: [1]\n", 842 | "Tower C: [2]\n", 843 | "moving disk from B to C\n", 844 | "Tower A: [3]\n", 845 | "Tower B: []\n", 846 | "Tower C: [2, 1]\n", 847 | "moving disk from A to B\n", 848 | "Tower A: []\n", 849 | "Tower B: [3]\n", 850 | "Tower C: [2, 1]\n", 851 | "moving disk from C to A\n", 852 | "Tower A: [1]\n", 853 | "Tower B: [3]\n", 854 | "Tower C: [2]\n", 855 | "moving disk from C to B\n", 856 | "Tower A: [1]\n", 857 | "Tower B: [3, 2]\n", 858 | "Tower C: []\n", 859 | "moving disk from A to B\n", 860 | "Tower A: []\n", 861 | "Tower B: [3, 2, 1]\n", 862 | "Tower C: []\n" 863 | ] 864 | } 865 | ], 866 | "source": [ 867 | "class Tower:\n", 868 | " def __init__(self, name):\n", 869 | " self.name = name\n", 870 | " self.stack = []\n", 871 | " \n", 872 | "def get_tower_by_name(name, ts):\n", 873 | " for t in ts:\n", 874 | " if t.name == name:\n", 875 | " return t\n", 876 | "\n", 877 | "def moveTower(height,fromPole, toPole, withPole):\n", 878 | " \"\"\"宏观上的移动\"\"\"\n", 879 | " if height >= 1:\n", 880 | " moveTower(height-1, fromPole, withPole, toPole)\n", 881 | " moveDisk(fromPole, toPole, withPole)\n", 882 | " moveTower(height-1, withPole, toPole, fromPole)\n", 883 | "\n", 884 | "def moveDisk(fromPole, toPole, withPole):\n", 885 | " \"\"\"具体移动一个盘子\"\"\"\n", 886 | " print(\"moving disk from\", fromPole.name, \"to\", toPole.name)\n", 887 | " toPole.stack.append(fromPole.stack.pop())\n", 888 | " print(\"Tower A: \", get_tower_by_name(\"A\", [fromPole, toPole, withPole]).stack)\n", 889 | " print(\"Tower B: \", get_tower_by_name(\"B\", [fromPole, toPole, withPole]).stack)\n", 890 | " print(\"Tower C: \", get_tower_by_name(\"C\", [fromPole, toPole, withPole]).stack)\n", 891 | "\n", 892 | "def main_hanoi(height):\n", 893 | " t_from = Tower(\"A\")\n", 894 | " t_to = Tower(\"B\")\n", 895 | " t_with = Tower(\"C\")\n", 896 | " t_from.stack = range(height, 0, -1)\n", 897 | " moveTower(height, t_from, t_to, t_with)\n", 898 | " \n", 899 | "main_hanoi(3)" 900 | ] 901 | }, 902 | { 903 | "cell_type": "markdown", 904 | "metadata": { 905 | "collapsed": false 906 | }, 907 | "source": [ 908 | "### q7\n", 909 | "\n", 910 | "使用turtle模块,画出Hilbert curve。\n", 911 | "\n", 912 | "Hilbert curve的介绍:[wikipedia](https://en.wikipedia.org/wiki/Hilbert_curve)\n", 913 | "\n", 914 | "![](https://upload.wikimedia.org/wikipedia/commons/4/46/Hilbert_curve.gif)\n", 915 | "\n", 916 | "思路:\n", 917 | "\n", 918 | "参考[这里](http://www.soc.napier.ac.uk/~andrew/hilbert.html),将问题分解成画“三笔凸包”和连接线。\n", 919 | "\n", 920 | "![](http://www.soc.napier.ac.uk/~andrew/hilbert1.gif)\n", 921 | "\n", 922 | "![](http://www.soc.napier.ac.uk/~andrew/hilbert2.gif)" 923 | ] 924 | }, 925 | { 926 | "cell_type": "code", 927 | "execution_count": 9, 928 | "metadata": { 929 | "collapsed": false 930 | }, 931 | "outputs": [], 932 | "source": [ 933 | "def draw_hilbert(x0, y0, xis, yis, xjs, yjs, n):\n", 934 | " \"\"\"\n", 935 | " x0 和 y0是左下角的坐标;\n", 936 | " xis 和 yis是右下角坐标(凸包的终点方向);\n", 937 | " xjs 和 yjs是左上角坐标(下一笔方向);\n", 938 | " 上述坐标皆是相对于缺口朝下的方向。\n", 939 | " \"\"\"\n", 940 | " if n <= 0:\n", 941 | " t.goto(x0+(xis+yis)/2, y0+(xjs+yjs)/2);\n", 942 | " else:\n", 943 | " draw_hilbert(x0, y0, yis/2, xis/2, yjs/2, xjs/2, n-1) # 画左下角分区 \n", 944 | " draw_hilbert(x0+xis/2, y0+xjs/2 ,xis/2, yis/2, xjs/2, yjs/2, n-1) # 画左上角分区 \n", 945 | " draw_hilbert(x0+xis/2+yis/2, y0+xjs/2+yjs/2, xis/2, yis/2, xjs/2, yjs/2,n-1) # 画右上角分区 \n", 946 | " draw_hilbert(x0+xis/2+yis, y0+xjs/2+yjs, -yis/2, -xis/2, -yjs/2, -xjs/2, n-1) # 画右下角分区 \n", 947 | "\n", 948 | "myWin.clearscreen()\n", 949 | "t.up()\n", 950 | "t.goto(0, 0)\n", 951 | "t.down()\n", 952 | "draw_hilbert(0., 0., 300., 0., 0., 300., 1)" 953 | ] 954 | }, 955 | { 956 | "cell_type": "markdown", 957 | "metadata": {}, 958 | "source": [ 959 | "### q8\n", 960 | "\n", 961 | "使用turtle模块,画出Koch snowflake。\n", 962 | "\n", 963 | "介绍参考[wiki-pedia](https://en.wikipedia.org/wiki/Koch_snowflake)。\n", 964 | "\n", 965 | "![](https://upload.wikimedia.org/wikipedia/commons/f/fd/Von_Koch_curve.gif)\n", 966 | "\n", 967 | "思路:\n", 968 | "\n", 969 | "1. 首先将任务分成三个相同的三部分,即三角形的三条边,不管每条边如何复杂化,都是相同的操作。\n", 970 | "\n", 971 | "2. 对于每条边,宏观上的操作如下,(在一根直线上长出一个角):\n", 972 | "\n", 973 | "![](http://python-with-science.readthedocs.io/en/latest/_images/koch_order_1.png)\n" 974 | ] 975 | }, 976 | { 977 | "cell_type": "code", 978 | "execution_count": 28, 979 | "metadata": { 980 | "collapsed": false 981 | }, 982 | "outputs": [], 983 | "source": [ 984 | "def koch(length, order):\n", 985 | " if order > 0:\n", 986 | " for turn in [0, 60, -120, 60]:\n", 987 | " t.left(turn)\n", 988 | " koch(length/3., order-1) \n", 989 | " else:\n", 990 | " t.forward(length)\n", 991 | "\n", 992 | "\n", 993 | "def draw_koch(length, order):\n", 994 | " for i in range(3):\n", 995 | " koch(length, order)\n", 996 | " t.right(120)\n", 997 | " \n", 998 | "myWin.clearscreen()\n", 999 | "t.pensize(2)\n", 1000 | "t.speed(10)\n", 1001 | "t.goto(0, 0)\n", 1002 | "t.down()\n", 1003 | "draw_koch(100., 3)" 1004 | ] 1005 | }, 1006 | { 1007 | "cell_type": "code", 1008 | "execution_count": null, 1009 | "metadata": { 1010 | "collapsed": true 1011 | }, 1012 | "outputs": [], 1013 | "source": [] 1014 | } 1015 | ], 1016 | "metadata": { 1017 | "anaconda-cloud": {}, 1018 | "kernelspec": { 1019 | "display_name": "Python [default]", 1020 | "language": "python", 1021 | "name": "python2" 1022 | }, 1023 | "language_info": { 1024 | "codemirror_mode": { 1025 | "name": "ipython", 1026 | "version": 2 1027 | }, 1028 | "file_extension": ".py", 1029 | "mimetype": "text/x-python", 1030 | "name": "python", 1031 | "nbconvert_exporter": "python", 1032 | "pygments_lexer": "ipython2", 1033 | "version": "2.7.12" 1034 | } 1035 | }, 1036 | "nbformat": 4, 1037 | "nbformat_minor": 0 1038 | } 1039 | --------------------------------------------------------------------------------