排序
(1) 冒泡排序
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
作为最简单的排序算法之一,冒泡排序给我的感觉就像 Abandon 在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉。冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但这种改进对于提升性能来
说并没有什么太大作用。
1. 算法步骤
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
2. 动图演示
3. 什么时候最快
当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊)。
4. 什么时候最慢
当输入的数据是反序时(写一个 for 循环反序输出数据不就行了,干嘛要用你冒泡排序呢,我是闲的吗)。
6. Python 代码实现
def buddle_sort(li):
flag = True
for i in range(len(li) - 1):
flag = False
for j in range(len(li) - i - 1):
if li[j] > li[j + 1]:
li[j], li[j + 1] = li[j + 1], li[j]
flag = True
if not flag:
break
print(f"第{i + 1}轮排序结果为{li}")
li = [9, 1, 5, 8, 3, 7, 4, 6, 2]
print(f"待排序列为:{li}")
buddle_sort(li)
运行结果:
待排序列为:[9, 1, 5, 8, 3, 7, 4, 6, 2]
第1轮排序结果为[1, 5, 8, 3, 7, 4, 6, 2, 9]
第2轮排序结果为[1, 5, 3, 7, 4, 6, 2, 8, 9]
第3轮排序结果为[1, 3, 5, 4, 6, 2, 7, 8, 9]
第4轮排序结果为[1, 3, 4, 5, 2, 6, 7, 8, 9]
第5轮排序结果为[1, 3, 4, 2, 5, 6, 7, 8, 9]
第6轮排序结果为[1, 3, 2, 4, 5, 6, 7, 8, 9]
第7轮排序结果为[1, 2, 3, 4, 5, 6, 7, 8, 9]
时间复杂度:O(n2)
(2) 选择排序
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
python代码实现:
def select_sort(li):
for i in range(len(li)):
min_num = i
for j in range(i + 1, len(li)):
if li[j] < li[min_num]:
min_num = j
if i != min_num:
li[i], li[min_num] = li[min_num], li[i]
print(f"第{i + 1}轮排序结果为{li}")
li = [9, 1, 5, 8, 3, 7, 4, 6, 2]
print(f"待排序列为:{li}")
select_sort(li)
运行结果:
待排序列为:[9, 1, 5, 8, 3, 7, 4, 6, 2]
第1轮排序结果为[1, 9, 5, 8, 3, 7, 4, 6, 2]
第2轮排序结果为[1, 2, 5, 8, 3, 7, 4, 6, 9]
第3轮排序结果为[1, 2, 3, 8, 5, 7, 4, 6, 9]
第4轮排序结果为[1, 2, 3, 4, 5, 7, 8, 6, 9]
第5轮排序结果为[1, 2, 3, 4, 5, 7, 8, 6, 9]
第6轮排序结果为[1, 2, 3, 4, 5, 6, 8, 7, 9]
第7轮排序结果为[1, 2, 3, 4, 5, 6, 7, 8, 9]
第8轮排序结果为[1, 2, 3, 4, 5, 6, 7, 8, 9]
第9轮排序结果为[1, 2, 3, 4, 5, 6, 7, 8, 9]
时间复杂度:O(n2)
(3) 插入排序
插入排序(英语:Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
python代码实现:
def insert_sort(li):
for i in range(1, len(li)):
temp = li[i]
j = i - 1
while j >= 0 and li[j] > temp:
li[j + 1] = li[j]
j -= 1
li[j + 1] = temp
print(f"第{i}轮排序结果为{li}")
li = [9, 1, 5, 8, 3, 7, 4, 6, 2]
print(f"待排序列为:{li}")
insert_sort(li)
运行结果:
待排序列为:[9, 1, 5, 8, 3, 7, 4, 6, 2]
第1轮排序结果为[1, 9, 5, 8, 3, 7, 4, 6, 2]
第2轮排序结果为[1, 5, 9, 8, 3, 7, 4, 6, 2]
第3轮排序结果为[1, 5, 8, 9, 3, 7, 4, 6, 2]
第4轮排序结果为[1, 3, 5, 8, 9, 7, 4, 6, 2]
第5轮排序结果为[1, 3, 5, 7, 8, 9, 4, 6, 2]
第6轮排序结果为[1, 3, 4, 5, 7, 8, 9, 6, 2]
第7轮排序结果为[1, 3, 4, 5, 6, 7, 8, 9, 2]
第8轮排序结果为[1, 2, 3, 4, 5, 6, 7, 8, 9]
时间复杂度:O(n2)
(4) 希尔排序
希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
python代码实现:
def ShellSort(li):
n = len(li)
increment = int(n / 2)
while increment > 0:
for i in range(increment, n):
temp = li[i]
j = i
while j >= increment and temp < li[j-increment]:
li[j] = li[j - increment]
j -= increment
li[j] = temp
increment = int(increment / 2)
if __name__ == '__main__':
li = [9, 1, 5, 8, 3, 7, 4, 6, 2]
print(f"待排序列为:{li}")
ShellSort(li)
print(f"排序后的序列为{li}")
运行结果:
待排序列为:[9, 1, 5, 8, 3, 7, 4, 6, 2]
排序后的序列为[1, 2, 3, 4, 5, 6, 7, 8, 9]
时间复杂度:O(nlogn)
(5) 堆排序
堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。堆排序可以说是一种利用堆的概念来排序的选择排序。
python代码实现:
import random
def shift(li, low, high):
"""
:param li:列表
:param low:堆的根节点位置
:param high:堆的最后一个元素
:return:
"""
i = low # 最开始指向根节点
j = 2 * i + 1 # 开始是左孩子
temp = li[low] # 把堆顶存起来
while j <= high: # 只要j位置有数
if j + 1 <= high and li[j + 1] > li[j]: # 如果存在右孩子并且比较大
j = j + 1 # j指向右孩子
if li[j] > temp:
li[i] = li[j]
i = j # 往下看一层
j = 2 * i + 1
else: # temp更大,把temp放到i的位置上
li[i] = temp
break
else:
li[i] = temp # 把temp放到叶子节点上
def heap_sort(li):
n = len(li)
for i in range((n - 2) // 2, -1, -1):
# i表示建堆的时候调整部分的根的下标
shift(li, i, n - 1)
# 建堆完成
print(f"所建的堆为:{li}")
for i in range(n - 1, -1, -1):
li[0], li[i] = li[i], li[0]
shift(li, 0, i - 1)
print(f"排序结果为:{li}")
if __name__ == '__main__':
li = [i for i in range(10)]
random.shuffle(li)
print(f"待排序列为:{li}")
heap_sort(li)
运行结果:
待排序列为:[4, 8, 9, 5, 2, 3, 1, 7, 0, 6]
所建的堆为:[9, 8, 4, 7, 6, 3, 1, 5, 0, 2]
排序结果为:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
此处用到了完全二叉树的性质,不知道的话可以去了解一下,这里不再说明。
时间复杂度:O(nlogn)
(6) 归并排序
归并排序(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
分治法:
- 分割:递归地把当前序列平均分割成两半。
- 集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。
python代码实现:
def merge(a, b):
c = []
h = j = 0
while j < len(a) and h < len(b):
if a[j] < b[h]:
c.append(a[j])
j += 1
else:
c.append(b[h])
h += 1
if j == len(a):
for i in b[h:]:
c.append(i)
else:
for i in a[j:]:
c.append(i)
return c
def merge_sort(lists):
if len(lists) <= 1:
return lists
middle = len(lists) // 2
left = merge_sort(lists[:middle])
right = merge_sort(lists[middle:])
return merge(left, right)
if __name__ == '__main__':
a = [9, 1, 5, 8, 3, 7, 4, 6, 2]
print(merge_sort(a))
运行结果:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
时间复杂度:O(nlogn)
(7) 快速排序
快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为较小和较大的2个子序列,然后递归地排序两个子序列。
步骤为:
- 挑选基准值:从数列中挑出一个元素,称为"基准"(pivot);
- 分割:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(与基准值相等的数可以到任何一边)。在这个分割结束之后,对基准值的排序就已经完成;
- 递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。
递归到最底部的判断条件是数列的大小是零或一,此时该数列显然已经有序。
选取基准值有数种具体方法,此选取方法对排序的时间性能有决定性影响。
python代码实现:
def partition(li, left, right):
temp = li[left]
while left < right:
while left < right and li[right] >= temp:
right -= 1
li[left] = li[right]
print(li)
while left < right and li[left] <= temp:
left += 1
li[right] = li[left]
print(li)
li[left] = temp
print(li)
return left
def quick_sort(li, left, right):
if left < right:
mid = partition(li, left, right)
quick_sort(li, left, mid - 1)
quick_sort(li, mid + 1, right)
li = [5, 3, 7, 1, 2, 4, 9, 6, 8, 10]
print(li)
quick_sort(li, 0, len(li) - 1)
print(li)
运行结果:
[5, 3, 7, 1, 2, 4, 9, 6, 8, 10]
[4, 3, 7, 1, 2, 4, 9, 6, 8, 10]
[4, 3, 7, 1, 2, 7, 9, 6, 8, 10]
[4, 3, 2, 1, 2, 7, 9, 6, 8, 10]
[4, 3, 2, 1, 2, 7, 9, 6, 8, 10]
[4, 3, 2, 1, 5, 7, 9, 6, 8, 10]
[1, 3, 2, 1, 5, 7, 9, 6, 8, 10]
[1, 3, 2, 1, 5, 7, 9, 6, 8, 10]
[1, 3, 2, 4, 5, 7, 9, 6, 8, 10]
[1, 3, 2, 4, 5, 7, 9, 6, 8, 10]
[1, 3, 2, 4, 5, 7, 9, 6, 8, 10]
[1, 3, 2, 4, 5, 7, 9, 6, 8, 10]
[1, 2, 2, 4, 5, 7, 9, 6, 8, 10]
[1, 2, 2, 4, 5, 7, 9, 6, 8, 10]
[1, 2, 3, 4, 5, 7, 9, 6, 8, 10]
[1, 2, 3, 4, 5, 6, 9, 6, 8, 10]
[1, 2, 3, 4, 5, 6, 9, 9, 8, 10]
[1, 2, 3, 4, 5, 6, 9, 9, 8, 10]
[1, 2, 3, 4, 5, 6, 9, 9, 8, 10]
[1, 2, 3, 4, 5, 6, 7, 9, 8, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 8, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 8, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
时间复杂度:最优情况下O(nlogn),最坏情况下O(n2)