每个算法都有详细的例题和清晰的图片,码字作图不易,麻烦点个赞加关注吧蟹蟹~😍
先序
排序算法评价的标准
时间:比较+移动记录的时间
空间:算法运行时需要的辅助空间
稳定性:对待排序序列里任何排序码相同的记录对 (Ri, Rj) ,排序都不会改变 Ri 与 Rj 前后顺序,这种排序算法就是稳定的(算法维持序列中排序码相同记录的相对位置)– 如果一个排序算法不能保证上述条件 ,就说它是不稳定的。
一、插入排序
直接插入排序
监视哨(哨兵)的作用:减少临时变量用于存储被比较的数;防止越界。
带监视哨的直接插入排序:在带排序的序列开头留一个位置用于存放每次被比较的数,也叫哨兵。第一个数属于有序部分,所以开始排序的位置是从序列中的第三个数开始,即下标为2的数。比较方法是一个for循环里嵌套一个while循环。for循环中循环的是序列中第2个数到最后一个数,用i表示;while循环则是从有序部分的最后一个数开始(即待比较数的前一个数),用j表示。如果比待比较数大,则该数往后移一位(代码中则是用该数覆盖后面的一位数),直到遇到比待比较数小的数,跳出while循环,用哨兵的数代替j位置的数。具体看代码一步步详细解释。
def straight_sort():
for i in range(2, len(data)):
if data[i] < data[i-1]:
# 这里加个判断是因为如果待比较的数小于有序部分最后一个数,那么就不用再排序了,直接换下一个数。
data[0] = data[i] # 设立哨兵
j = i # j是作为while循环中的参数
while data[j - 1] > data[0]: # 有序部分中的数大于哨兵
data[j] = data[j-1] # 该数大于哨兵,就往后移一位(覆盖后一位数)
j -= 1 # 挑选有序部分中的前一位数进行下一步比较
# 当while循环结束后,要么是中间有个数比哨兵小,要么哨兵插到最前面
data[j] = data[0] # 用哨兵代替之前
![](https://img-blog.csdnimg.cn/img_convert/454bc072f77ef6a43af3f45521882c91.png)
![](https://img-blog.csdnimg.cn/img_convert/0ac894af0dd0b7e015d4217a797ad6cf.png)
![](https://img-blog.csdnimg.cn/img_convert/053ccecfc33551b846d560e35459bdfe.png)
![](https://img-blog.csdnimg.cn/img_convert/a47e458e179787d791b05abe811a9007.png)
![](https://img-blog.csdnimg.cn/img_convert/fc597b9895a70ce685c1f16ca5570026.png)
![](https://img-blog.csdnimg.cn/img_convert/f539a4a576d427a5b08cb7437d84ccba.png)
![](https://img-blog.csdnimg.cn/img_convert/4bacb89b09d81e62e617f1b087568462.png)
希尔排序
概念:希尔排序是对直接插入排序的优化。它每次排序根据增量将序列分词对应的组数(增量为4,就是4组;增量为8就是8组),同时每一组中的元素个数为增量+1(增量为4,一个小组就有5个元素),同时每个小组是紧贴着的。如下图:
![](https://img-blog.csdnimg.cn/img_convert/fbbf17767b8868a01754bf8abe3363d5.png)
每次排序采用直接插入排序,同时每次的增量会逐渐递减,直到增量为1(增量必须为1)的时候对整个序列进行直接插入排序完成整个算法。
以上图为例,增量以4,2,1为例:
第一次排序的4个间隔为4的小组如下:
1-2
4-87
8-11
3-10
分别对其执行插入排序得到
1-2
4-87
8-11
3-10
这时序列整体为:1-4-8-3-2-87-11-10
第二次排序的2个间隔为2小组如下:
![](https://img-blog.csdnimg.cn/img_convert/22d3dd6c14c696b2beef1a6e3e68b2f3.png)
1-8-2-11
4-3-87-10
分别执行直接插入排序得到
1-2-8-11
3-4-10-87
这时序列整体为:1-3-2-4-8-10-11-87
第三次排序的1个间隔的1个小组如下:
1-3-2-4-8-10-11-87
执行直接插入排序得到:
1-2-3-4-8-10-1-87
二、交换排序
冒泡排序
概念:冒泡排序将序列看成无序和有序两部分,一开始没有有序部分。然后每次排序都可以确定一个有序数。每次排序从第一个数开始,与第二个数比较判断是否交换位置;然后第二个数与第三个数比较判断是否交换位置;就这样和倒数第一个数比较判断。第二次排序则只需要比到倒数第二个数;第三次排序比到倒数第三个数;以此类推,n个数只需要比较n-1次。
代码讲解:冒泡排序由两个循环组成,第一个循环是总共需要比较的次数(n-1次),第二次则是每次比较时要比较到最后哪个数(逐渐往前,从倒数第一个数一直往前走)。
def bullle_sort():
length = len(data) # 获得序列中元素个数(当然0位置用来做中间变量,总长度比实际数量多一)
for i in range(1, length - 1):
# 从1开始是因为0这个位置用来作为下面交换两个数的中间量,不用另设置temp变量
# length - 1则是只需要比较n-1次
for j in range(1, length - i):
if data[j + 1] < data[j]:
data[0].key = data[j].key # 保存较大的数
data[j].key = data[j + 1].key # 较小的数换掉较大的数
data[j + 1].key = data[0].key # 较大的数换到较小数的位置
![](https://img-blog.csdnimg.cn/img_convert/c85e33f20f2b71a469c132665186d4ea.png)
例题:
对{12,70,33,65,24,56,48,92,86,33}使用冒泡排序,给出每次排序结果
答案:
12,33,65,24,56,48,70,86,33,92
12,33,24,56,48,65,70,33,86,92
12,24,33,48,56,65,33,70,86,92
12,24,33,48,56,33,65,70,86,92
12,24,33,48,33,56,65,70,86,92
12,24,33,33,48,56,65,70,86,92
快速排序的一趟过程
介绍:快速排序时对冒泡排序的改进,每次比较的跨度更大,从而节省了更多时间。快速排序的中心思想是任取一个基准数(一般选取最左边第一个数),并且我们将序列第一个位置0用来存放基准数。
然后进入一个while循环(条件是i小于j),先进入另一个while循环(条件是i小于j并且j上的数大于基准数)从最右边(位置j)开始,j不断往左比较直到找到一个比基准数小的数然后就停在这个数的位置,把j上的数换掉此时i上的数。
之后进入另一个while循环(条件是i小于j并且i上的数小于基准数)从最左边(位置i)开始,i不断往右比较直到找到一个比基准数大的数然后就停在这个数的位置上,用i上的数替换掉此时j上的数;然后j又开始....一直重复上述过程直到i大于等于j为止跳出最外层得while循环。
最后要注意,在上述过程中始终有个数是重复出现的,所以在最后我们需要让0位置上的基准数替换掉这个重复的数(重复的数的位置在i上)
代码介绍:
while quick_sort():
i = 1 # 第一个元素位置是1,位置0存放基准数了
j = len(data) - 1 # 最后一个元素位置
data[0] = data[i] # 取最左边的数作为基准数存入位置0上
![](https://img-blog.csdnimg.cn/img_convert/1e1acc7d3bdf0cfc28af87a5b00ecc68.png)
初始状态
while i < j:
# 只要i小于j且j上的数大于基准数,则j减1
while i < j and data[j] >= data[0]:
j -= 1
![](https://img-blog.csdnimg.cn/img_convert/1977ecb1468dfa81961541adef3227c9.png)
第一次比较结果
data[i] = data[j] # j上的数小于基准数,则结束循环自己的循环并把j上的数给i上的数
# 只要i小于j且i上的数小于基准数,则i加1
while i < j and data[i] <= data[0]:
i += 1
data[j] = data[i] # i上的数大于基准数,把i上的数给到位置j上的数
![](https://img-blog.csdnimg.cn/img_convert/bb06d309dd99b54a00dbe0dd6e59df28.png)
第2次比较结束
![](https://img-blog.csdnimg.cn/img_convert/9f0d14d95e3c4bfb19a425c8c5d43482.png)
第3次比较结束
![](https://img-blog.csdnimg.cn/img_convert/f37af013b6ed4f34391b3cfc6b76e341.png)
第4次比较结束
![](https://img-blog.csdnimg.cn/img_convert/c7cc9556f3ae6bf59c1b75e9872268d8.png)
第5次比较结束
![](https://img-blog.csdnimg.cn/img_convert/f1b0ee961d390d9fce466ae3f5e849a5.png)
第6次比较
更正,是j减一
![](https://img-blog.csdnimg.cn/img_convert/e7c25a7587ba8eeb989ba7fc6ac89c51.png)
第7次比较
更正,是j减一
![](https://img-blog.csdnimg.cn/img_convert/3f6c631642e3f41b44165e78d8e81043.png)
第8次比较
![](https://img-blog.csdnimg.cn/img_convert/8a98ef6ba006b87a6e16d49a1c7cb608.png)
第9次比较
![](https://img-blog.csdnimg.cn/img_convert/37ff1d84a0542c67826aa9e275cbd263.png)
第10次比较
![](https://img-blog.csdnimg.cn/img_convert/499c249b9d753b21ced02ac70eb150c0.png)
结束一轮
data[i] = data[0] # 外层循环结束,把位置0上的基准数给到位置i上的数
完整代码如下
def quick_sort():
data[0] = data[i]
while i < j:
while i < j and data[j] >= data[0]:
j -= 1
data[i] = data[j]
while i < j and data[i] <= data[0]:
i += 1
data[j] = data[i]
data[i] = data[0]
return i # 返回基准数的位置
三、选择排序
简单选择排序
简单选择排序原理很简单,就是将序列看成有序部分和无序部分。一开始有序部分为0,无序部分为n。然后进入外层for循环,共循环n-1次。从第一个元素开始,记下它的位置i作为最小数的位置minindex。然后进入内层for循环,将第一个元素与它后面的数一个个比较,每次有比它小的数就将minindex换成这个更小的数的位置。等for循环结束后,得到的minindex就是最小的数的位置了。然后还要判断minindex和最初的位置i是否相同,如果相同说明i位置上的数就是最小的,那么什么都不用做;如果不相同,则把minindex位置上的数和i位置上的调换,就完成了一次排序。
例题:写出对关键字系列{51,38 ,49,27,62,5,16,38}进行选择排序的每一趟结果。
答案:
51,38 ,49,27,62,5,16,38(i=0)
5,38,49,27,62,51,16,38(i=1)
5,16,49,27,62,51,38,38(i=2)
5,16,27,49,62,51,38,38(i=3)
5,16,27,38,62,51,49,38(i=4)
5,16,27,38,38,51,49,62(i=5)
5,16,27,38,38,49,51,62
堆排序
定义:给定一个无序数组,首先将其按照层次遍历的顺序构建成一个完全二叉树。然后将完全二叉树构造成大根堆或者小根堆。大根堆排序方法是:每次交换根节点与最后一个末尾节点,然后重新调整结构使之再次成为大根堆(不用管被交换的根节点。),然后再次交换根节点与最后一个末尾节点,再次调整结构(也不用管新的被交换的根节点),直到结束。
![](https://img-blog.csdnimg.cn/img_convert/0dfaaf2c582c8f5ba11c15a8b9defc2f.png)
例题:对序列{40,55,49,73,12,27,98,81,64,36}构建大根堆和筛选。
四、归并排序
归并排序
归并排序看这两个例题就可明白
![](https://img-blog.csdnimg.cn/img_convert/eba24e5adc25482c510d2a53a03ec97c.png)
![](https://img-blog.csdnimg.cn/img_convert/17ff750bc112f63b00d158916296ab98.png)