代码成就万世基积沙镇海,梦想永在凌云意意气风发。

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秒
View Code

算法的概念

算法是计算机处理信息的本质,因为计算机程序本质上是一个算法来告诉计算机确切的步骤来执行一个指定的任务。一般地,当算法在处理信息时,会从输入设备或数据的存储地址读取数据,把结果写入输出设备或某个存储地址供以后再调用。

算法是独立存在的一种解决问题的方法和思想。

对于算法而言,实现的语言并不重要,重要的是思想。

算法可以有不同的语言描述实现版本(如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秒
View Code

对于同一问题,我们给出了两种解决算法,在两种算法的实现中,我们对程序执行的时间进行了测算,发现两段程序执行的时间相差悬殊(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 ""
View Code

头部添加元素

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
View Code

尾部添加元素

 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
View Code

指定位置添加元素

 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
View Code

删除节点

 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
View Code

查找节点是否存在

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
View Code

测试

 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()
View Code

 

双向链表

操作

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
View Code

指定位置插入节点

 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
View Code

删除元素

 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
View Code

测试

 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()
View Code 

单向循环链表

操作

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()
View Code

栈结构实现

栈可以用顺序表实现,也可以用链表实现。

栈的操作

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()
View Code

执行过程如下:

 

队列

队列是一种先进先出的(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()
View Code

双端队列

操作

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()
View Code

 

排序与搜索

排序算法的稳定性

稳定性:稳定排序算法会让原本有相等键值的纪录维持相对次序。也就是如果一个排序算法是稳定的,当有两个相等键值的纪录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)
View Code

时间复杂度

最优时间复杂度: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)
View Code

时间复杂度

最优时间复杂度: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)
View Code

时间复杂度

最优时间复杂度: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)
View Code

时间复杂度

最优时间复杂度: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)
View Code

时间复杂度

最优时间复杂度:根据步长序列的不同而不同
最坏时间复杂度: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)
View Code

时间复杂度

最优时间复杂度: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))
View Code

时间复杂度

最优时间复杂度: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
View Code

树的创建,创建一个树的类,并给一个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)
View Code

二叉树的遍历

深度优先遍历
 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
View Code
广度优先遍历(层次遍历)
 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)
View Code

 

posted @ 2017-12-10 20:03  Tomorrow1  阅读(216)  评论(0)    收藏  举报