算法之各种排序
本节内容
- 系统内置排序
- 冒泡排序
- 冒泡排序改进 双向排序
- 插入排序
- 希尔排序
- 归并排序
- 快速排序
1.系统内置排序
在python中使用内置方法对列表进行排序可以使用两种方法:
一种是列表提供的sort方法,该方法的特性是在原址进行排序,没有生成新的列表。
还有一种是python提供的内置方法sorted(),该方法接受一个序列,并返回一个排序好的序列,这种方式没有改变原序列的排序。
这里测试,使用的是list提供的sort内置方法。也就是原址排序。
在上面所列举的排序方式中,list内置方法排序所用时间是最短的,说明性能是最好的,至于其内部实现方式,需要看源码才能明白。
下面是列表内置方法排序的源代码
1 List.sort()
2.冒泡排序
冒泡排序原理:从左到右先找第一个值,然后拿这个值右边的值和它进行比较,假如右边的值比它小,那就将右边的这个值和它进行交换, 这样,就能够将整个列表中最小的值放到最左边,第一次排序完成,之后对第二个数也进行相同的操作,最终整个列表变成有序的。
冒泡排序法的时间复杂度是O(n**2),是一种稳定排序算法,逻辑比较简单。
冒泡排序算法的源代码:
1 def MaoPaoSort(List): 2 for i in range(0, len(List) - 1): 3 for j in range(i + 1, len(List)): 4 if List[i] > List[j]: 5 List[i], List[j] = List[j], List[i] 6 return List
3.冒泡排序改进 双向排序
冒泡双向排序的原理:冒泡排序的改进其实就是在对第一个值寻找最小值的同时也在对最后一个值寻找最大值,双向寻找,这样,一次排序下来,能得到左右两边两个排好序的值,这样,总的执行排序的次数将减少一半。
但是,在python中,双向排序的使用时间和冒泡排序使用的时间差不多,并没有将时间减半,这应该是由于python中list的实现方式的关系,python中的list内部实现机制其实还是数组,只不过数组里面还加了一块缓存空间,这样移动某一个元素,其他的元素也需要移动。所以并没有达到优化冒泡排序的目的(PS:原因分析是基于我自己的理解,可能有不对之处。。。)
双向排序的时间复杂度是O(N**2),是一种稳定排序算法,逻辑比较简单
双向排序算法的源代码:
1 def MaoPaoOptimizeSort(List): 2 for i in range(0, len(List) // 2): 3 for j in range(i, len(List) - i - 1): 4 if List[i] > List[j]: 5 List[i], List[j] = List[j], List[i] 6 if List[j] > List[-i - 1]: 7 List[j], List[-i - 1] = List[-i - 1], List[j] 8 return List
4.插入排序
插入排序原理:从第二个值开始,假定这个值的左边都是有序的序列,找到左边的有序序列中,该值应该插入的位置,然后将这个值插入到 相应的位置,使得左边的序列依旧有序,然后将这个值丢弃。这样执行下去,到最后将整个序列形成一个有序的序列。
插入排序算法在python中的执行时间比冒泡排序短,但是也是属于一个数量级的时间。至于为什么插入排序算法比冒泡排序算法的时间短一半左右,应该也是由于list的内部实现机制的关系,列表由数组组成,在将右边的无序序列中的第一个元素插入到左边的有序序列的时候,右边的元素位置不需要移动,只需要移动左边的序列元素,这样比冒泡排序每次移动所花费的时间要少,平均下来,每次移动的时间要少一半左右。
插入排序的时间复杂度是O(n**2),是一种稳定的排序算法,逻辑比较简单
插入排序算法的源代码:
1 def ChaRuSort(List): 2 for i in range(1, len(List)): 3 j = i 4 while j > 0 and List[j - 1] > List[i]: 5 j -= 1 6 List.insert(j, List.pop(i)) 7 return List
5.希尔排序
希尔排序原理:在序列中,找到一个中间的位置,然后将该序列分成左右两个序列,再将左边序列第一个值与右边序列第一个值进行比较,如果左边大于右边,则进行交换,持续下去,将左右两个序列比较完成后,左右两个序列又分别取中间位置,分成4个序列,再分别在这四个序列中取出第一个数,进行比较,使得四个序列的第一个数是按照从小到大排列的,这样依次下去,直到把四个序列的值都对比完成后,再从这四个序列中再取中间数,将四个序列拆成八个序列,在进行比较,到最后,拆成的序列数量为原来序列长度的一半的时候,原来序列中奇数位置的值都有序了,偶数位置的值也都有序了,这时候再进行最后一次比较,拆出的序列数量等于原来序列长度,这时候再经过一次排序,整个序列有序了。
希尔排序的时间复杂度是O(nlog2n),是一种不稳定的排序算法,逻辑比较复杂
希尔排序算法的源代码:
1 def XiErSort(List): 2 grep = len(List) // 2 3 while grep > 0: 4 for i in range(grep, len(List)): 5 j = i 6 while j >= grep and List[j - grep] > List[j]: 7 List[j - grep], List[j] = List[j], List[j - grep] 8 j -= grep 9 grep = grep // 2 10 return List
6.归并排序
归并排序原理:归并排序是基于对两个有序序列进行排序变成新的有序序列的方式进行排序的,假如有两个有序序列,从第一个值开始一个一个比较,小的就插入到新的序列中,并使得其位置加一,最后,两个序列中会有某一个序列还剩下最大值,将该最大值插入到新的序列中就完成了排序。归并排序使用递归方式将一个序列从中间分成左右两个序列,再将左右两个序列从中间分成四个序列。递归下去最后分到每个序列中只有一个元素,将这个元素返回,到上一级调用两序列排序方式,将两个值排序,返回新的排序好的序列,就这样一层一层递归上来,最后使得序列有序。该方式是使用空间换取排序时间的算法。
归并排序的时间复杂度是O(nlog2n),是一种稳定的算法,逻辑比较复杂
但是归并排序是使用递归实现的,递归次数较多的话,也会影响排序的时间,但是相对于上面的排序算法,该算法的时间消耗将大大缩短。
归并排序算法的源代码:
1 class GuiBingSort(object): 2 def MergeTwoList(self, List1, List2): 3 i, j, tmp = 0, 0, [] 4 while i < len(List1) and j < len(List2): 5 if List1[i] <= List2[j]: 6 tmp.append(List1[i]) 7 i += 1 8 else: 9 tmp.append(List2[j]) 10 j += 1 11 tmp.extend(List1[i:]) 12 tmp.extend(List2[j:]) 13 return tmp 14 15 def GuiBingSort(self, List): 16 if len(List) <= 1: 17 return List 18 List1 = self.GuiBingSort(List[:len(List) // 2]) 19 List2 = self.GuiBingSort(List[len(List) // 2:]) 20 return self.MergeTwoList(List1, List2)
7.快速排序
快速排序原理:在序列中找到一个定位点(一般是第一个点)然后将比这个点的值大的值挪到它的右边,比这个值小的值挪到它的左边,最后返回 该点的位置。通过递归调用该方法,将序列分成左右两个序列,然后再细分下去,到最后每个序列中只有两个元素,这两个元素被排好序后返回到上一层,最后返回的将是排序好的序列。
快速排序的时间复杂度是O(nlog2n),最坏情况时间复杂度O(n**2),是一种不稳定的算法,逻辑比较复杂
其实快速排序想要进行优化的话,要找到一个合适的定位点,这个定位点如果总是第一个点的话,当这个点取到的值是最大的值,之后假如每次递归取到的第一个点都是最大的值,那么,这就是快速排序最坏的情况了,这时候。。。递归树就变成了一个序列了。。。是一颗非常不平衡的树。但是如果每次找这个点之前能找到一个合适的点的话,就能够使得递归树变成一颗平衡树,这样就能达到快速排序的最好情况。所以,优化就在这个定位点的选取上,如果总是能把定位点选取合适,那么快速排序是一个非常好的稳定的排序算法。
快速排序算法速度非常快,但是还是比list内置排序法时间上慢了一个数量级,但是,比归并排序少了一半左右的时间,至于为什么比快速排序要快。。。这个我目前还没有找到好的解释办法。。。
快速排序算法的源代码:
1 def Partition(List, low, high): 2 tmp = List[low] 3 while low < high: 4 while low < high and List[high] >= tmp: 5 high = high - 1 6 List[low] = List[high] 7 while low < high and List[low] <= tmp: 8 low = low + 1 9 List[high] = List[low] 10 List[low] = tmp 11 return low 12 13 14 def KuaiSuSort(List, low, high): 15 if low < high: 16 pivotpos = Partition(List, low, high) 17 KuaiSuSort(List, low, pivotpos - 1) 18 KuaiSuSort(List, pivotpos + 1, high)
下面是我写的一个demo测试以上排序算法在分别对10000个随机数排序过程中所花费的时间,单位是秒,可供参考:
下面是这个demo的源代码,有不足之处还望参考指正:
1 #!/usr/bin/env python 2 # -*- coding: utf-8 -*- 3 import random 4 import time 5 6 7 # 列表生成函数 8 def CreateList(Num): 9 List = [] 10 i = 0 11 while i < Num: 12 List.append(random.randint(1, Num)) 13 i += 1 14 return List 15 16 17 # 文件追加 18 def AddFile(Information, FileName): 19 f = open(FileName, 'a', encoding="utf-8") 20 f.write(str(Information) + ' ') 21 f.close 22 23 24 # 清空文件 25 def ClearFile(FileName): 26 f = open(FileName, 'w') 27 f.close() 28 29 30 # 时间计算 31 def CalaulateteTime(Num, FileName): 32 ClearFile(FileName) 33 # 系统内置排序 34 List = CreateList(Num) 35 TimeStart = time.time() 36 List.sort() 37 TimeStop = time.time() 38 AddFile('系统内置排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 39 # 冒泡排序 40 List = CreateList(Num) 41 TimeStart = time.time() 42 MaoPaoSort(List) 43 TimeStop = time.time() 44 AddFile('冒泡排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 45 # 冒泡排序改进 46 List = CreateList(Num) 47 TimeStart = time.time() 48 MaoPaoOptimizeSort(List) 49 TimeStop = time.time() 50 AddFile('冒泡排序改进,双向排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 51 # 插入排序 52 List = CreateList(Num) 53 TimeStart = time.time() 54 ChaRuSort(List) 55 TimeStop = time.time() 56 AddFile('插入排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 57 # 希尔排序 58 List = CreateList(Num) 59 TimeStart = time.time() 60 XiErSort(List) 61 TimeStop = time.time() 62 AddFile('希尔排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 63 # 归并排序 64 List = CreateList(Num) 65 TimeStart = time.time() 66 GuiBing = GuiBingSort() 67 List = GuiBing.GuiBingSort(List) 68 TimeStop = time.time() 69 AddFile('归并排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 70 # 快速排序 71 List = CreateList(Num) 72 TimeStart = time.time() 73 KuaiSuSort(List, 0, len(List) - 1) 74 TimeStop = time.time() 75 AddFile('快速排序: 时间:' + str(TimeStop - TimeStart) + ' ' + str(List), str(FileName)) 76 77 78 # 列表排序 79 # 1冒泡排序 80 ''' 81 冒泡排序原理就是从左到右先找第一个值,然后拿这个值右边的值和它进行比较,假如右边的值比它小,那就将右边的这个值和它进行交换, 82 这样,就能够将整个列表中最小的值放到最左边,第一次排序完成,之后对第二个数也进行相同的操作,最终整个列表变成有序的。 83 ''' 84 85 86 def MaoPaoSort(List): 87 for i in range(0, len(List) - 1): 88 for j in range(i + 1, len(List)): 89 if List[i] > List[j]: 90 List[i], List[j] = List[j], List[i] 91 return List 92 93 94 # 2冒泡排序的改进(双向排序) 95 ''' 96 冒泡排序的改进其实就是在对第一个值寻找最小值的同时也在对最后一个值寻找最大值,双向寻找,这样,一次排序下来,能得到左右两边两个 97 排好序的值,这样,总的执行排序的次数将减少一半。 98 ''' 99 100 101 def MaoPaoOptimizeSort(List): 102 for i in range(0, len(List) // 2): 103 for j in range(i, len(List) - i - 1): 104 if List[i] > List[j]: 105 List[i], List[j] = List[j], List[i] 106 if List[j] > List[-i - 1]: 107 List[j], List[-i - 1] = List[-i - 1], List[j] 108 return List 109 110 111 # 插入排序 112 ''' 113 插入排序的原理就是从第二个值开始,假定这个值的左边都是有序的序列,找到左边的有序序列中,该值应该插入的位置,然后将这个值插入到 114 相应的位置,使得左边的序列依旧有序,然后将这个值丢弃。这样执行下去,到最后将整个序列形成一个有序的序列。 115 ''' 116 117 118 def ChaRuSort(List): 119 for i in range(1, len(List)): 120 j = i 121 while j > 0 and List[j - 1] > List[i]: 122 j -= 1 123 List.insert(j, List.pop(i)) 124 return List 125 126 127 # 希尔排序 128 ''' 129 在序列中,找到一个中间的位置,然后将该序列分成左右两个序列,再将左边序列第一个值与右边序列第一个值进行比较,如果左边大于右边,则 130 进行交换,持续下去,将左右两个序列比较完成后,左右两个序列又分别取中间位置,分成4个序列,再分别在这四个序列中取出第一个数,进行比 131 较,使得四个序列的第一个数是按照从小到大排列的,这样依次下去,直到把四个序列的值都对比完成后,再从这四个序列中再取中间数,将四个 132 序列拆成八个序列,在进行比较,到最后,拆成的序列数量为原来序列长度的一半的时候,原来序列中奇数位置的值都有序了,偶数位置的值也都 133 有序了,这时候再进行最后一次比较,拆出的序列数量等于原来序列长度,这时候再经过一次排序,整个序列有序了。 134 ''' 135 136 137 def XiErSort(List): 138 grep = len(List) // 2 139 while grep > 0: 140 for i in range(grep, len(List)): 141 j = i 142 while j >= grep and List[j - grep] > List[j]: 143 List[j - grep], List[j] = List[j], List[j - grep] 144 j -= grep 145 grep = grep // 2 146 return List 147 148 149 # 归并排序 150 ''' 151 归并排序是基于对两个有序序列进行排序变成新的有序序列的方式进行排序的,假如有两个有序序列,从第一个值开始一个一个比较,小的就插入 152 到新的序列中,并使得其位置加一,最后,两个序列中会有某一个序列还剩下最大值,将该最大值插入到新的序列中就完成了排序。归并排序使用 153 递归方式将一个序列从中间分成左右两个序列,再将左右两个序列从中间分成四个序列。递归下去最后分到每个序列中只有一个元素,将这个元素 154 返回,到上一级调用两序列排序方式,将两个值排序,返回新的排序好的序列,就这样一层一层递归上来,最后使得序列有序。该方式是使用空间 155 换取排序时间的算法。 156 ''' 157 158 159 class GuiBingSort(object): 160 def MergeTwoList(self, List1, List2): 161 i, j, tmp = 0, 0, [] 162 while i < len(List1) and j < len(List2): 163 if List1[i] <= List2[j]: 164 tmp.append(List1[i]) 165 i += 1 166 else: 167 tmp.append(List2[j]) 168 j += 1 169 tmp.extend(List1[i:]) 170 tmp.extend(List2[j:]) 171 return tmp 172 173 def GuiBingSort(self, List): 174 if len(List) <= 1: 175 return List 176 List1 = self.GuiBingSort(List[:len(List) // 2]) 177 List2 = self.GuiBingSort(List[len(List) // 2:]) 178 return self.MergeTwoList(List1, List2) 179 180 181 # 快速排序 182 ''' 183 快速排序的原理是在序列中找到一个定位点(一般是第一个点)然后将比这个点的值大的值挪到它的右边比这个值小的值挪到它的左边,最后返回 184 该点的位置通过递归调用该方法,将序列分成左右两个序列,然后再细分下去,到最后每个序列中只有两个元素,这两个元素被排好序后返回倒上 185 一层,最后返回的将是排序好的序列。 186 ''' 187 188 189 def Partition(List, low, high): 190 tmp = List[low] 191 while low < high: 192 while low < high and List[high] >= tmp: 193 high = high - 1 194 List[low] = List[high] 195 while low < high and List[low] <= tmp: 196 low = low + 1 197 List[high] = List[low] 198 List[low] = tmp 199 return low 200 201 202 def KuaiSuSort(List, low, high): 203 if low < high: 204 pivotpos = Partition(List, low, high) 205 KuaiSuSort(List, low, pivotpos - 1) 206 KuaiSuSort(List, pivotpos + 1, high) 207 208 209 if __name__ == "__main__": 210 ClearFile('排序结果.txt') 211 CalaulateteTime(10000, '排序结果.txt') 212 print('calculate is successful,the file is test.txt')