欢迎光临散文网 会员登陆 & 注册

《漫画算法: 小灰的算法之旅》第4章 排序算法

2023-03-23 20:31 作者:方程星  | 我要投稿

引言

  1. 时间复杂度为O(n^2)的排序算法

    冒牌排序、选择排序、插入排序、希尔排序(它的性能略优于O(n^2),但又比不上O(nlogn))

  2. 时间复杂度为O(nlogn)的排序算法

    快速排序、归并排序、堆排序

  3. 时间复杂度为线性的排序算法

    计数排序、桶排序、基数排序

排序算法还可以根据其稳定性,划分为稳定排序和不稳定排序

如果值相同的元素在排序后仍然保持着排序前的顺序,则这样的排序算法是稳定排序;

如果值相同的元素在排序后打乱了排序前的顺序,则这样的排序算法是不稳定排序

稳定排序和不稳定排序

什么是冒泡排序

冒泡排序(bubble sort):是一种基础的交换排序

希望从小到大排序
把相邻的元素两两比较,当一个元素大于右侧相邻元素时,交换它们的位置;当一个元素小于或等于右侧相邻元素时,位置不变

元素9作为数列中最大的元素,就像是汽水里的小气泡一样,
“漂”到了最右侧

冒泡排序的第1轮就结束了,以此类推,进行多轮冒泡


第二轮
多轮结束

冒泡排序是一种稳定排序,值相等的元素并不会打乱原本的顺序。由于该排序算法的每一轮都要遍历所有元素,总共遍历(元素数量-1)轮,所以平均时间复杂度是O(n^2)

鸡尾酒排序

鸡尾酒排序的元素比较和交换过程是双向的。

举例:

[2, 3, 4, 5, 6, 7, 8, 1] 使用鸡尾酒排序法

8和1交换
从右往左比较并进行交换

在鸡尾酒排序的第3轮,需要重新从左向右比较并进行交换;

1和2比较,位置不变;

2和3比较,位置不变;

3和4比较,位置不变……

6和7比较,位置不变。

没有元素位置进行交换,证明已经有序,排序结束。

鸡尾酒排序的思路:排序过程就像钟摆一样,第1轮从左到右,第2轮从右到左,第3轮再从左到右……

鸡尾酒排序的优点是能够在特定条件下,减少排序的回合数;

而缺点也很明显,就是代码量几乎增加了1倍;

它能发挥出优势的场景,是大部分元素已经有序的情况

什么是快速排序

快速排序也属于交换排序,通过元素之间的比较和交换位置来达到排序的目的;

冒泡排序在每一轮中只把1个元素冒泡到数列的一端,而快速排序则在每一轮挑选一个基准元素,并让其他比它大的元素移动到数列一边,比它小的元素移动到数列的另一边,从而把数列拆解成两个部分

快排的思路:分治法
快速排序的流程

快速排序算法总体的平均时间复杂度是O(nlogn)

基准元素(pivot),在分治过程中,以基准元素为中心,把其他元素移动到它的左右两边

可以随机选择一个元素作为基准元素,并且让基准元素和数列首元素交换位置

选择基准元素

快速排序的平均时间复杂度是O(nlogn),最坏情况下的时间复杂度是O(n^2)

快速排序的实现方法:

  1. 双边循环法


原始序列
第1次循环:从right指针开始,让指针所指向的元素和基准元素做比较。如果大于或等于pivot,则指针向左移动;如果小于pivot,则right指针停止移动,切换到left指针;在当前数列中,1<4,所以right直接停止移动,换到left指针,进行下一步行动
轮到left指针行动,让指针所指向的元素和基准元素做比较。如果小于或等于pivot,则指针向右移动;如果大于pivot,则left指针停止移动;由于left开始指向的是基准元素,判断肯定相等,所以left右移1位
由于7>4,left指针在元素7的位置停下。这时,让left指针和right指针所指
向的元素进行交换。
多次循环

2.单边循环法

原始数组,要求从小到大排序
选定基准元素pivot,设置一个mark指针指向数列起始位置,这个mark指针代表小于基准元素的区域边界

从基准元素的下一个位置开始遍历数组;

如果遍历到的元素大于基准元素,就继续往后遍历;

如果遍历到的元素小于基准元素,则需要做两件事:

第一,把mark指针右移1位,因为小于pivot的区域边界增大了1;

第二,让最新遍历到的元素和mark指针所在位置的元素交换位置,因为最新遍历的元素归属于小于pivot的区域

7>4
3<4, mark右移一位
7,3 互换
继续互换

堆排序

堆排序算法:

  1. 把无序数组构建成二叉堆。需要从小到大排序,则构建成最大堆;需要从大到小排序,则构建成最小堆。

  2. 循环删除堆顶元素,替换到二叉堆的末尾,调整堆产生新的堆顶。

堆排序的时间复杂度为O(nlogn)

堆排序和快速排序的平均时间复杂度都是O(nlogn),并且都是不稳定排序;

至于不同点,快速排序的最坏时间复杂度是O(n^2),而堆排序的最坏时间复杂度稳定在O(nlogn);

快速排序递归和非递归方法的平均空间复杂度都是O(logn),而堆排序的空间复杂度是O(1)

计数排序和桶排序

[9,3,5,4,9,1,2,7,8,1,3,6,5,3,4,0,10,9,7,9]  20个随机整数


计数数组,下标表示整数,数组表示该整数的大小
第1个整数是9,那么数组下标为9的元素加1
第2个整数是3,那么数组下标为3的元素加1

依此类推

遍历结束
直接遍历数组,输出数组元素的下标值,元素的值是几,就输出几次

优化版本的计数排序属于稳定排序

计数排序的时间复杂度

O(n+m)、空间复杂度为O(m)


计数排序的局限性:

  1. 当数列最大和最小值差距过大时,并不适合用计数排序

  2. 当数列元素不是整数时,也不适合用计数排序

桶排序

桶排序同样是一种线性时间的排序算法

每一个桶(bucket)代表一个区间范围

非整数列表: [4.5,0.84,3.25,2.18,0.5]

创建的桶数量(5)等于原始数列的元素数量(5);最后一个桶只包含数列最大值外,前面各个桶的区间按照比例来确定;区间跨度=(最大值-最小值)/(桶的数量-1)[ (4.5-4)/(5-1)=1 ]
遍历原始数列,把元素对号入座放入各个桶中
对每个桶内部的元素分别进行排序
遍历所有的桶,输出所有元素

桶排序的总体时间复杂度为O(n);

至于空间复杂度就很容易得到了,同样是O(n)

小结

算法性能汇总


《漫画算法: 小灰的算法之旅》第4章 排序算法的评论 (共 条)

分享到微博请遵守国家法律