python-数据结构与算法
引入
计算三层for循环时间
1 1 import time 2 2 3 3 start_time = time.time() 4 4 5 5 # 三层循环 6 6 for a in range(0, 1001): 7 7 for b in range(0, 1001): 8 8 for c in range(0, 1001): 9 9 if a**2 + b**2 == c**2 and a+b+c == 1000: 10 10 print("a, b, c: %d, %d, %d" % (a, b, c)) 11 11 12 12 end_time = time.time() 13 13 print("elapsed: %f" % (end_time - start_time)) 14 14 print("complete!") 15 15 16 16 运行结果: 17 17 18 18 a, b, c: 0, 500, 500 19 19 a, b, c: 200, 375, 425 20 20 a, b, c: 375, 200, 425 21 21 a, b, c: 500, 0, 500 22 22 elapsed: 214.583347 23 23 complete! 24 25 24 注意运行的时间:214.583347秒
算法的概念
算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务。一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供以后再调用。 算法是独立存在的一种解决问题的方法和思想。 对于算法而言,实现的语言并不重要,重要的是思想。 算法可以有不同的语言描述实现版本(如C描述、C++描述、Python描述等),我们现在是在用Python语言进行描述实现。
算法的五大特性
输入: 算法具有0个或多个输入 输出: 算法至少有1个或多个输出 有穷性: 算法在有限的步骤之后会自动结束而不会无限循环,并且每一个步骤可以在可接受的时间内完成 确定性:算法中的每一步都有确定的含义,不会出现二义性 可行性:算法的每一步都是可行的,也就是说每一步都能够执行有限的次数完成
计算三层for循环时间(加入算法)
1 1 import time 2 2 3 3 start_time = time.time() 4 4 5 5 # 注意两层循环 6 6 for a in range(0, 1001): 7 7 for b in range(0, 1001-a): 8 8 c = 1000 - a - b 9 9 if a**2 + b**2 == c**2: 10 10 print("a, b, c: %d, %d, %d" % (a, b, c)) 11 11 12 12 end_time = time.time() 13 13 print("elapsed: %f" % (end_time - start_time)) 14 14 print("complete!") 15 15 16 16 运行结果: 17 17 18 18 a, b, c: 0, 500, 500 19 19 a, b, c: 200, 375, 425 20 20 a, b, c: 375, 200, 425 21 21 a, b, c: 500, 0, 500 22 22 elapsed: 0.182897 23 23 complete! 24 24 25 25 注意运行的时间:0.182897秒
对于同一问题,我们给出了两种解决算法,在两种算法的实现中,我们对程序执行的时间进行了测算,发现两段程序执行的时间相差悬殊(214.583347秒相比于0.182897秒),由此我们可以得出结论:实现算法程序的执行时间可以反应出算法的效率,即算法的优劣。
链表
单向链表
单链表的操作
is_empty() 链表是否为空 length() 链表长度 travel() 遍历整个链表 add(item) 链表头部添加元素 append(item) 链表尾部添加元素 insert(pos, item) 指定位置添加元素 remove(item) 删除节点 search(item) 查找节点是否存在
单链表的实现
1 1 class SingleLinkList(object): 2 2 """单链表""" 3 3 def __init__(self): 4 4 self.__head = None 5 5 6 6 def is_empty(self): 7 7 """判断链表是否为空""" 8 8 return self.__head == None 9 9 10 10 def length(self): 11 11 """链表长度""" 12 12 # cur初始时指向头节点 13 13 cur = self.__head 14 14 count = 0 15 15 # 尾节点指向None,当未到达尾部时 16 16 while cur != None: 17 17 count += 1 18 18 # 将cur后移一个节点 19 19 cur = cur.next 20 20 return count 21 21 22 22 def travel(self): 23 23 """遍历链表""" 24 24 cur = self.__head 25 25 while cur != None: 26 26 print cur.item, 27 27 cur = cur.next 28 28 print ""
头部添加元素
1 def add(self, item): 2 """头部添加元素""" 3 # 先创建一个保存item值的节点 4 node = SingleNode(item) 5 # 将新节点的链接域next指向头节点,即_head指向的位置 6 node.next = self.__head 7 # 将链表的头_head指向新节点 8 self.__head = node
尾部添加元素
1 1 def append(self, item): 2 2 """尾部添加元素""" 3 3 node = SingleNode(item) 4 4 # 先判断链表是否为空,若是空链表,则将_head指向新节点 5 5 if self.is_empty(): 6 6 self.__head = node 7 7 # 若不为空,则找到尾部,将尾节点的next指向新节点 8 8 else: 9 9 cur = self.__head 10 10 while cur.next != None: 11 11 cur = cur.next 12 12 cur.next = node
指定位置添加元素
1 1 def insert(self, pos, item): 2 2 """指定位置添加元素""" 3 3 # 若指定位置pos为第一个元素之前,则执行头部插入 4 4 if pos <= 0: 5 5 self.add(item) 6 6 # 若指定位置超过链表尾部,则执行尾部插入 7 7 elif pos > (self.length()-1): 8 8 self.append(item) 9 9 # 找到指定位置 10 10 else: 11 11 node = SingleNode(item) 12 12 count = 0 13 13 # pre用来指向指定位置pos的前一个位置pos-1,初始从头节点开始移动到指定位置 14 14 pre = self.__head 15 15 while count < (pos-1): 16 16 count += 1 17 17 pre = pre.next 18 18 # 先将新节点node的next指向插入位置的节点 19 19 node.next = pre.next 20 20 # 将插入位置的前一个节点的next指向新节点 21 21 pre.next = node
删除节点
1 1 def remove(self,item): 2 2 """删除节点""" 3 3 cur = self.__head 4 4 pre = None 5 5 while cur != None: 6 6 # 找到了指定元素 7 7 if cur.item == item: 8 8 # 如果第一个就是删除的节点 9 9 if not pre: 10 10 # 将头指针指向头节点的后一个节点 11 11 self.__head = cur.next 12 12 else: 13 13 # 将删除位置前一个节点的next指向删除位置的后一个节点 14 14 pre.next = cur.next 15 15 break 16 16 else: 17 17 # 继续按链表后移节点 18 18 pre = cur 19 19 cur = cur.next
查找节点是否存在
1 1 def search(self,item): 2 2 """链表查找节点是否存在,并返回True或者False""" 3 3 cur = self.__head 4 4 while cur != None: 5 5 if cur.item == item: 6 6 return True 7 7 cur = cur.next 8 8 return False
测试
1 1 if __name__ == "__main__": 2 2 ll = SingleLinkList() 3 3 ll.add(1) 4 4 ll.add(2) 5 5 ll.append(3) 6 6 ll.insert(2, 4) 7 7 print "length:",ll.length() 8 8 ll.travel() 9 9 print ll.search(3) 10 10 print ll.search(5) 11 11 ll.remove(1) 12 12 print "length:",ll.length() 13 13 ll.travel()
双向链表
操作
is_empty() 链表是否为空 length() 链表长度 travel() 遍历链表 add(item) 链表头部添加 append(item) 链表尾部添加 insert(pos, item) 指定位置添加 remove(item) 删除节点 search(item) 查找节点是否存在
实现
1 class Node(object): 2 """双向链表节点""" 3 def __init__(self, item): 4 self.item = item 5 self.next = None 6 self.prev = None 7 8 9 class DLinkList(object): 10 """双向链表""" 11 def __init__(self): 12 self.__head = None 13 14 def is_empty(self): 15 """判断链表是否为空""" 16 return self.__head == None 17 18 def length(self): 19 """返回链表的长度""" 20 cur = self.__head 21 count = 0 22 while cur != None: 23 count += 1 24 cur = cur.next 25 return count 26 27 def travel(self): 28 """遍历链表""" 29 cur = self.__head 30 while cur != None: 31 print cur.item, 32 cur = cur.next 33 print "" 34 35 def add(self, item): 36 """头部插入元素""" 37 node = Node(item) 38 if self.is_empty(): 39 # 如果是空链表,将_head指向node 40 self.__head = node 41 else: 42 # 将node的next指向_head的头节点 43 node.next = self.__head 44 # 将_head的头节点的prev指向node 45 self.__head.prev = node 46 # 将_head 指向node 47 self.__head = node 48 49 def append(self, item): 50 """尾部插入元素""" 51 node = Node(item) 52 if self.is_empty(): 53 # 如果是空链表,将_head指向node 54 self.__head = node 55 else: 56 # 移动到链表尾部 57 cur = self.__head 58 while cur.next != None: 59 cur = cur.next 60 # 将尾节点cur的next指向node 61 cur.next = node 62 # 将node的prev指向cur 63 node.prev = cur 64 65 66 67 def search(self, item): 68 """查找元素是否存在""" 69 cur = self.__head 70 while cur != None: 71 if cur.item == item: 72 return True 73 cur = cur.next 74 return False
指定位置插入节点
1 def insert(self, pos, item): 2 """在指定位置添加节点""" 3 if pos <= 0: 4 self.add(item) 5 elif pos > (self.length()-1): 6 self.append(item) 7 else: 8 node = Node(item) 9 cur = self.__head 10 count = 0 11 # 移动到指定位置的前一个位置 12 while count < (pos-1): 13 count += 1 14 cur = cur.next 15 # 将node的prev指向cur 16 node.prev = cur 17 # 将node的next指向cur的下一个节点 18 node.next = cur.next 19 # 将cur的下一个节点的prev指向node 20 cur.next.prev = node 21 # 将cur的next指向node 22 cur.next = node
删除元素
1 def remove(self, item): 2 """删除元素""" 3 cur = self.__head 4 while cur != None: 5 # 找到了要删除的元素 6 if cur.item == item: 7 # 先判断此结点是否是头节点 8 # 头节点 9 if cur == self.__head: 10 self.__head = cur.next 11 # 如果存在下一个结点,则设置下一个结点 12 if cur.next: 13 # 判断链表是否只有一个结点 14 cur.next.prev = None 15 else: 16 cur.prev.next = cur.next 17 # 如果存在下一个结点,则设置下一个结点 18 if cur.next: 19 cur.next.prev = cur.prev 20 break 21 else: 22 cur = cur.next
测试
1 if __name__ == "__main__": 2 ll = DLinkList() 3 ll.add(1) 4 ll.add(2) 5 ll.append(3) 6 ll.insert(2, 4) 7 ll.insert(4, 5) 8 ll.insert(0, 6) 9 print "length:",ll.length() 10 ll.travel() 11 print ll.search(3) 12 print ll.search(4) 13 ll.remove(1) 14 print "length:",ll.length() 15 ll.travel()
单向循环链表
操作
is_empty() 判断链表是否为空 length() 返回链表的长度 travel() 遍历 add(item) 在头部添加一个节点 append(item) 在尾部添加一个节点 insert(pos, item) 在指定位置pos添加节点 remove(item) 删除一个节点 search(item) 查找节点是否存在
实现
1 class Node(object): 2 """节点""" 3 def __init__(self, item): 4 self.item = item 5 self.next = None 6 7 8 class SinCycLinkedlist(object): 9 """单向循环链表""" 10 def __init__(self): 11 self.__head = None 12 13 def is_empty(self): 14 """判断链表是否为空""" 15 return self.__head == None 16 17 def length(self): 18 """返回链表的长度""" 19 # 如果链表为空,返回长度0 20 if self.is_empty(): 21 return 0 22 count = 1 23 cur = self.__head 24 while cur.next != self.__head: 25 count += 1 26 cur = cur.next 27 return count 28 29 def travel(self): 30 """遍历链表""" 31 if self.is_empty(): 32 return 33 cur = self.__head 34 print cur.item, 35 while cur.next != self.__head: 36 cur = cur.next 37 print cur.item, 38 print "" 39 40 41 def add(self, item): 42 """头部添加节点""" 43 node = Node(item) 44 if self.is_empty(): 45 self.__head = node 46 node.next = self.__head 47 else: 48 #添加的节点指向_head 49 node.next = self.__head 50 # 移到链表尾部,将尾部节点的next指向node 51 cur = self.__head 52 while cur.next != self.__head: 53 cur = cur.next 54 cur.next = node 55 #_head指向添加node的 56 self.__head = node 57 58 def append(self, item): 59 """尾部添加节点""" 60 node = Node(item) 61 if self.is_empty(): 62 self.__head = node 63 node.next = self.__head 64 else: 65 # 移到链表尾部 66 cur = self.__head 67 while cur.next != self.__head: 68 cur = cur.next 69 # 将尾节点指向node 70 cur.next = node 71 # 将node指向头节点_head 72 node.next = self.__head 73 74 def insert(self, pos, item): 75 """在指定位置添加节点""" 76 if pos <= 0: 77 self.add(item) 78 elif pos > (self.length()-1): 79 self.append(item) 80 else: 81 node = Node(item) 82 cur = self.__head 83 count = 0 84 # 移动到指定位置的前一个位置 85 while count < (pos-1): 86 count += 1 87 cur = cur.next 88 node.next = cur.next 89 cur.next = node 90 91 def remove(self, item): 92 """删除一个节点""" 93 # 若链表为空,则直接返回 94 if self.is_empty(): 95 return 96 # 将cur指向头节点 97 cur = self.__head 98 pre = None 99 while cur.next != self.__head: 100 if cur.item == item: 101 # 先判断此结点是否是头节点 102 if cur == self.__head: 103 # 头节点的情况 104 # 找尾节点 105 rear = self.__head 106 while rear.next != self.__head: 107 rear = rear.next 108 self.__head = cur.next 109 rear.next = self.__head 110 else: 111 # 中间节点 112 pre.next = cur.next 113 return 114 else: 115 pre = cur 116 cur = cur.next 117 # 退出循环,cur指向尾节点 118 if cur.item == item: 119 if cur == self.__head: 120 # 链表只有一个节点 121 self.__head = None 122 else: 123 # pre.next = cur.next 124 pre.next = self.__head 125 126 def search(self, item): 127 """查找节点是否存在""" 128 if self.is_empty(): 129 return False 130 cur = self.__head 131 if cur.item == item: 132 return True 133 while cur.next != self.__head: 134 cur = cur.next 135 if cur.item == item: 136 return True 137 return False 138 139 if __name__ == "__main__": 140 ll = SinCycLinkedlist() 141 ll.add(1) 142 ll.add(2) 143 ll.append(3) 144 ll.insert(2, 4) 145 ll.insert(4, 5) 146 ll.insert(0, 6) 147 print "length:",ll.length() 148 ll.travel() 149 print ll.search(3) 150 print ll.search(7) 151 ll.remove(1) 152 print "length:",ll.length() 153 ll.travel()
栈
栈结构实现
栈可以用顺序表实现,也可以用链表实现。
栈的操作
Stack() 创建一个新的空栈 push(item) 添加一个新的元素item到栈顶 pop() 弹出栈顶元素 peek() 返回栈顶元素 is_empty() 判断栈是否为空 size() 返回栈的元素个数
实现
1 class Stack(object): 2 """栈""" 3 def __init__(self): 4 self.items = [] 5 6 def is_empty(self): 7 """判断是否为空""" 8 return self.items == [] 9 10 def push(self, item): 11 """加入元素""" 12 self.items.append(item) 13 14 def pop(self): 15 """弹出元素""" 16 return self.items.pop() 17 18 def peek(self): 19 """返回栈顶元素""" 20 return self.items[len(self.items)-1] 21 22 def size(self): 23 """返回栈的大小""" 24 return len(self.items) 25 26 if __name__ == "__main__": 27 stack = Stack() 28 stack.push("hello") 29 stack.push("world") 30 stack.push("itcast") 31 print stack.size() 32 print stack.peek() 33 print stack.pop() 34 print stack.pop() 35 print stack.pop()
执行过程如下:

队列
队列是一种先进先出的(First In First Out)的线性表,简称FIFO。允许插入的一端为队尾,允许删除的一端为队头。
操作
Queue() 创建一个空的队列 enqueue(item) 往队列中添加一个item元素 dequeue() 从队列头部删除一个元素 is_empty() 判断一个队列是否为空 size() 返回队列的大小
实现
1 class Queue(object): 2 """队列""" 3 def __init__(self): 4 self.items = [] 5 6 def is_empty(self): 7 return self.items == [] 8 9 def enqueue(self, item): 10 """进队列""" 11 self.items.insert(0,item) 12 13 def dequeue(self): 14 """出队列""" 15 return self.items.pop() 16 17 def size(self): 18 """返回大小""" 19 return len(self.items) 20 21 if __name__ == "__main__": 22 q = Queue() 23 q.enqueue("hello") 24 q.enqueue("world") 25 q.enqueue("itcast") 26 print q.size() 27 print q.dequeue() 28 print q.dequeue() 29 print q.dequeue()
双端队列
操作
Deque() 创建一个空的双端队列 add_front(item) 从队头加入一个item元素 add_rear(item) 从队尾加入一个item元素 remove_front() 从队头删除一个item元素 remove_rear() 从队尾删除一个item元素 is_empty() 判断双端队列是否为空 size() 返回队列的大小
实现
1 class Deque(object): 2 """双端队列""" 3 def __init__(self): 4 self.items = [] 5 6 def is_empty(self): 7 """判断队列是否为空""" 8 return self.items == [] 9 10 def add_front(self, item): 11 """在队头添加元素""" 12 self.items.insert(0,item) 13 14 def add_rear(self, item): 15 """在队尾添加元素""" 16 self.items.append(item) 17 18 def remove_front(self): 19 """从队头删除元素""" 20 return self.items.pop(0) 21 22 def remove_rear(self): 23 """从队尾删除元素""" 24 return self.items.pop() 25 26 def size(self): 27 """返回队列大小""" 28 return len(self.items) 29 30 31 if __name__ == "__main__": 32 deque = Deque() 33 deque.add_front(1) 34 deque.add_front(2) 35 deque.add_rear(3) 36 deque.add_rear(4) 37 print deque.size() 38 print deque.remove_front() 39 print deque.remove_front() 40 print deque.remove_rear() 41 print deque.remove_rear()
排序与搜索
排序算法的稳定性
稳定性:稳定排序算法会让原本有相等键值的纪录维持相对次序。也就是如果一个排序算法是稳定的,当有两个相等键值的纪录R和S,且在原本的列表中R出现在S之前,在排序过的列表中R也将会是在S之前。
冒泡排序
冒泡排序(英语:Bubble Sort)是一种简单的排序算法。它重复地遍历要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
冒泡排序算法的运作如下:
比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。 针对所有的元素重复以上的步骤,除了最后一个。 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
实现
1 def bubble_sort(alist): 2 for j in range(len(alist)-1,0,-1): 3 # j表示每次遍历需要比较的次数,是逐渐减小的 4 for i in range(j): 5 if alist[i] > alist[i+1]: 6 alist[i], alist[i+1] = alist[i+1], alist[i] 7 8 li = [54,26,93,17,77,31,44,55,20] 9 bubble_sort(li) 10 print(li)
时间复杂度
最优时间复杂度:O(n) (表示遍历一次发现没有任何可以交换的元素,排序结束。) 最坏时间复杂度:O(n2) 稳定性:稳定
冒泡排序的演示

选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
实现
1 def selection_sort(alist): 2 n = len(alist) 3 # 需要进行n-1次选择操作 4 for i in range(n-1): 5 # 记录最小位置 6 min_index = i 7 # 从i+1位置到末尾选择出最小数据 8 for j in range(i+1, n): 9 if alist[j] < alist[min_index]: 10 min_index = j 11 # 如果选择出的数据不在正确位置,进行交换 12 if min_index != i: 13 alist[i], alist[min_index] = alist[min_index], alist[i] 14 15 alist = [54,226,93,17,77,31,44,55,20] 16 selection_sort(alist) 17 print(alist)
时间复杂度
最优时间复杂度:O(n2) 最坏时间复杂度:O(n2) 稳定性:不稳定(考虑升序每次选择最大的情况)
选择排序演示

插入排序
插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
实现
1 def insert_sort(alist): 2 # 从第二个位置,即下标为1的元素开始向前插入 3 for i in range(1, len(alist)): 4 # 从第i个元素开始向前比较,如果小于前一个元素,交换位置 5 for j in range(i, 0, -1): 6 if alist[j] < alist[j-1]: 7 alist[j], alist[j-1] = alist[j-1], alist[j] 8 9 alist = [54,26,93,17,77,31,44,55,20] 10 insert_sort(alist) 11 print(alist)
时间复杂度
最优时间复杂度:O(n) (升序排列,序列已经处于升序状态) 最坏时间复杂度:O(n2) 稳定性:稳定
插入排序演示

快速排序
快速排序(英语:Quicksort),又称划分交换排序(partition-exchange sort),通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
实现
1 def quick_sort(alist, start, end): 2 """快速排序""" 3 4 # 递归的退出条件 5 if start >= end: 6 return 7 8 # 设定起始元素为要寻找位置的基准元素 9 mid = alist[start] 10 11 # low为序列左边的由左向右移动的游标 12 low = start 13 14 # high为序列右边的由右向左移动的游标 15 high = end 16 17 while low < high: 18 # 如果low与high未重合,high指向的元素不比基准元素小,则high向左移动 19 while low < high and alist[high] >= mid: 20 high -= 1 21 # 将high指向的元素放到low的位置上 22 alist[low] = alist[high] 23 24 # 如果low与high未重合,low指向的元素比基准元素小,则low向右移动 25 while low < high and alist[low] < mid: 26 low += 1 27 # 将low指向的元素放到high的位置上 28 alist[high] = alist[low] 29 30 # 退出循环后,low与high重合,此时所指位置为基准元素的正确位置 31 # 将基准元素放到该位置 32 alist[low] = mid 33 34 # 对基准元素左边的子序列进行快速排序 35 quick_sort(alist, start, low-1) 36 37 # 对基准元素右边的子序列进行快速排序 38 quick_sort(alist, low+1, end) 39 40 41 alist = [54,26,93,17,77,31,44,55,20] 42 quick_sort(alist,0,len(alist)-1) 43 print(alist)
时间复杂度
最优时间复杂度:O(nlogn) 最坏时间复杂度:O(n2) 稳定性:不稳定
快速排序演示

希尔排序
希尔排序(Shell Sort)是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。 希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
实现
1 def shell_sort(alist): 2 n = len(alist) 3 # 初始步长 4 gap = n // 2 5 while gap > 0: 6 # 按步长进行插入排序 7 for i in range(gap, n): 8 j = i 9 # 插入排序 10 while j>=gap and alist[j-gap] > alist[j]: 11 alist[j-gap], alist[j] = alist[j], alist[j-gap] 12 j -= gap 13 # 得到新的步长 14 gap = gap // 2 15 16 alist = [54,26,93,17,77,31,44,55,20] 17 shell_sort(alist) 18 print(alist)
时间复杂度
最优时间复杂度:根据步长序列的不同而不同 最坏时间复杂度:O(n2) 稳定性:不稳定
希尔排序演示

归并排序
归并排序是采用分治法的一个非常典型的应用。归并排序的思想就是先递归分解数组,再合并数组。 将数组分解最小之后,然后合并两个有序数组,基本思路是比较两个数组的最前面的数,谁小就先取谁,取了后相应的指针就往后移一位。然后再比较,直至一个数组为空,最后把另一个数组的剩余部分复制过来即可。
实现
1 def merge_sort(alist): 2 if len(alist) <= 1: 3 return alist 4 # 二分分解 5 num = len(alist)//2 6 left = merge_sort(alist[:num]) 7 right = merge_sort(alist[num:]) 8 # 合并 9 return merge(left,right) 10 11 def merge(left, right): 12 '''合并操作,将两个有序数组left[]和right[]合并成一个大的有序数组''' 13 #left与right的下标指针 14 l, r = 0, 0 15 result = [] 16 while l<len(left) and r<len(right): 17 if left[l] < right[r]: 18 result.append(left[l]) 19 l += 1 20 else: 21 result.append(right[r]) 22 r += 1 23 result += left[l:] 24 result += right[r:] 25 return result 26 27 alist = [54,26,93,17,77,31,44,55,20] 28 sorted_alist = mergeSort(alist) 29 print(sorted_alist)
时间复杂度
最优时间复杂度:O(nlogn) 最坏时间复杂度:O(nlogn) 稳定性:稳定
常见排序算法效率比较

搜索
二分法查找
实现
1 (非递归实现) 2 def binary_search(alist, item): 3 first = 0 4 last = len(alist)-1 5 while first<=last: 6 midpoint = (first + last)//2 7 if alist[midpoint] == item: 8 return True 9 elif item < alist[midpoint]: 10 last = midpoint-1 11 else: 12 first = midpoint+1 13 return False 14 testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,] 15 print(binary_search(testlist, 3)) 16 print(binary_search(testlist, 13)) 17 18 (递归实现) 19 def binary_search(alist, item): 20 if len(alist) == 0: 21 return False 22 else: 23 midpoint = len(alist)//2 24 if alist[midpoint]==item: 25 return True 26 else: 27 if item<alist[midpoint]: 28 return binary_search(alist[:midpoint],item) 29 else: 30 return binary_search(alist[midpoint+1:],item) 31 32 testlist = [0, 1, 2, 8, 13, 17, 19, 32, 42,] 33 print(binary_search(testlist, 3)) 34 print(binary_search(testlist, 13))
时间复杂度
最优时间复杂度:O(1) 最坏时间复杂度:O(logn)
树与树算法
树的概念
树(英语:tree)是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点: * 每个节点有零个或多个子节点; * 没有父节点的节点称为根节点; * 每一个非根节点有且只有一个父节点; * 除了根节点外,每个子节点可以分为多个不相交的子树;
树的术语
节点的度:一个节点含有的子树的个数称为该节点的度; 树的度:一棵树中,最大的节点的度称为树的度; 叶节点或终端节点:度为零的节点; 父亲节点或父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点; 孩子节点或子节点:一个节点含有的子树的根节点称为该节点的子节点; 兄弟节点:具有相同父节点的节点互称为兄弟节点; 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推; 树的高度或深度:树中节点的最大层次; 堂兄弟节点:父节点在同一层的节点互为堂兄弟; 节点的祖先:从根到该节点所经分支上的所有节点; 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。 森林:由m(m>=0)棵互不相交的树的集合称为森林;
树的种类
无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;
有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;
二叉树:每个节点最多含有两个子树的树称为二叉树;
完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);
霍夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;
B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。
树的存储与表示
顺序存储:将数据结构存储在固定的数组中,然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。
常见的一些树的应用场景
1.xml,html等,那么编写这些东西的解析器的时候,不可避免用到树 2.路由协议就是使用了树的算法 3.mysql数据库索引 4.文件系统的目录结构 5.所以很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构
二叉树
二叉树的基本概念
二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)
二叉树的性质(特性)
性质1: 在二叉树的第i层上至多有2^(i-1)个结点(i>0) 性质2: 深度为k的二叉树至多有2^k - 1个结点(k>0) 性质3: 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1; 性质4:具有n个结点的完全二叉树的深度必为 log2(n+1) 性质5:对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)
二叉树的节点表示以及树的创建
通过使用Node类中定义三个属性,分别为elem本身的值,还有lchild左孩子和rchild右孩子
1 class Node(object): 2 """节点类""" 3 def __init__(self, elem=-1, lchild=None, rchild=None): 4 self.elem = elem 5 self.lchild = lchild 6 self.rchild = rchild
树的创建,创建一个树的类,并给一个root根节点,一开始为空,随后添加节点
1 class Tree(object): 2 """树类""" 3 def __init__(self, root=None): 4 self.root = root 5 6 def add(self, elem): 7 """为树添加节点""" 8 node = Node(elem) 9 #如果树是空的,则对根节点赋值 10 if self.root == None: 11 self.root = node 12 else: 13 queue = [] 14 queue.append(self.root) 15 #对已有的节点进行层次遍历 16 while queue: 17 #弹出队列的第一个元素 18 cur = queue.pop(0) 19 if cur.lchild == None: 20 cur.lchild = node 21 return 22 elif cur.rchild == None: 23 cur.rchild = node 24 return 25 else: 26 #如果左右子树都不为空,加入队列继续判断 27 queue.append(cur.lchild) 28 queue.append(cur.rchild)
二叉树的遍历
深度优先遍历
1 先序遍历 在先序遍历中,我们先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树 2 根节点->左子树->右子树 3 def preorder(self, root): 4 """递归实现先序遍历""" 5 if root == None: 6 return 7 print root.elem 8 self.preorder(root.lchild) 9 self.preorder(root.rchild) 10 11 中序遍历 在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树 12 左子树->根节点->右子树 13 def inorder(self, root): 14 """递归实现中序遍历""" 15 if root == None: 16 return 17 self.inorder(root.lchild) 18 print root.elem 19 self.inorder(root.rchild) 20 21 后序遍历 在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点 22 左子树->右子树->根节点 23 def postorder(self, root): 24 """递归实现后续遍历""" 25 if root == None: 26 return 27 self.postorder(root.lchild) 28 self.postorder(root.rchild) 29 print root.elem
广度优先遍历(层次遍历)
1 从树的root开始,从上到下从从左到右遍历整个树的节点 2 3 def breadth_travel(self): 4 """利用队列实现树的层次遍历""" 5 if root == None: 6 return 7 queue = [] 8 queue.append(root) 9 while queue: 10 node = queue.pop(0) 11 print node.elem, 12 if node.lchild != None: 13 queue.append(node.lchild) 14 if node.rchild != None: 15 queue.append(node.rchild)
版权声明:本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
https://www.cnblogs.com/qq_841161825/

浙公网安备 33010602011771号