# 排序算法(上)

# 一、生活中的排序

当我们在使用京东、淘宝这类购物软件,或者哔哩哔哩、抖音这类视频网站时,几乎都需要用到筛选与排序功能。比如按点击数排序、最多弹幕等等。其他的网站也有类似的功能。

图片

那么,你有没有想过在数十万甚至数百万的商品或视频中快速高效地排序是如何实现的呢?这背后其实是用到了一种叫做排序算法的技术,它可以让计算机快速地把一组数据按照某种规则进行排序。排序算法在计算机科学中是非常重要和常用的,它可以帮助我们解决很多实际的问题,比如搜索、数据分析、机器学习等等。

但是,如果你完全不会计算机,你可能会觉得排序算法是很难理解和掌握的,甚至觉得它跟你没有什么关系。其实不然,排序算法并不是只有精通计算机才能懂得,它其实跟我们日常生活中的很多场景都有关联。比如,当你要整理你的衣柜时,你可能会按照衣服的颜色、大小、季节等不同的标准来排列它们,这就是一种排序算法。当你要给一堆书籍编号时,你可能会按照书名的字母顺序、出版年份、作者姓名等不同的方式来排序,这也是一种排序算法。

那么,计算机是怎么实现排序算法的呢?它又有哪些不同的排序方法呢?在这篇文章中,我将为大家讲解一些基本的排序算法,让你能够了解它们的原理和特点,并且用一些简单和有趣的例子来帮助你理解和记忆它们。希望通过这篇文章,你能够对排序算法有一个初步的认识和兴趣,并且能够在日常生活中运用它们来解决一些问题。

(检索算法以后说吧,开始挖坑.jpg)╰( ̄ω ̄o)

# 二、十大排序算法

我想大家可能在各个渠道听说过一些又名、常用的排序算法,例如冒泡排序、选择排序、插入排序、快速排序等等,这些排序算法各有优缺点,适用于不同的场景和数据规模。下面我为大家介绍一下这十种排序算法的基本思想和特点 (。・ω・。)

# 0. 时间复杂度(O)

若想了解算法,则必须要先了解时间复杂度。

时间复杂度是一种衡量算法运行效率的指标,它表示算法执行的基本操作次数与输入规模的函数关系。时间复杂度越低,算法越高效,越能节省时间和资源。或者说时间复杂度是用来描述算法执行所需时间与问题规模之间的关系。举个例子,如果我们要查找一个数是否在一个有序列表中,一种简单的方法是从列表的第一个数开始,一直比较到找到这个数或者遍历完整个列表。这种方法的时间复杂度是 O (n),其中 n 是列表中元素的个数。也就是说,随着列表中元素个数的增加,这个查找方法需要的时间也会相应增加。常见的时间复杂度有 O (1)、O (log n)、O (n)、O (n log n)、O (n²) 等,其中 O (1) 表示算法的执行时间与问题规模无关,是最优的时间复杂度;O (n²) 表示算法的执行时间与问题规模的平方成正比,是极差的时间复杂度。

# 1. 冒泡排序(Bubble Sort)

说到冒泡算法,不少人会疑惑为什么他叫 “冒泡”?而说到冒泡其实不难想到烧水时咕嘟咕嘟冒的气泡,而水中的气泡是从底部向上冒出来的,而冒泡算法也是类似的,他的基本思想是:“两两比较相邻位”,它通过不断地比较和交换相邻的两个数,将较大的数像气泡一样 “冒” 到顶部,较小的数则 “沉” 到底部。这样,经过一轮比较后,最大的数就会 “冒” 到顶部。然后,算法会继续进行下一轮比较,直到所有的数都被按照从小到大的顺序排好。

若要形象地解释冒泡算法,不妨我们来举个例子,当我有如下序列时我应如何用冒泡排序将其由小到大排列好呢?

7,3,8,1,6

一开始先选定最右边的数字:7,3,8,1,6

然后不断向右比较。然后就会惊奇地发现没得比哈哈哈,那就不用动啦

选定下一位数:7,3,8,1,6

嗯 “1” 比 “6” 小,所以停止移动

选定下一位数:7,3,8,1,6

“8” 比 “1” 大,因此与 “1” 交换位置:7,3,1,8,6

“8” 比 “6” 大,因此与 “6” 交换位置:7,3,1,6,8

将 “8” 排好了,选定下一位数:7,3,1,6,8

“3” 比 “1” 大,因此与 “1” 交换位置:7,1,3,6,8

“3” 比 “6” 小,因此停止移动

总之就像这样一步步走下去最后求得 1,3,6,7,8

像这样的冒泡算法的应用非常广泛,其实我们在生活中也经常用到它。比如,你可能会在买菜的时候,把价格依次从小到大排一遍序,然后再决定买哪些菜。这就是一种简单的冒泡排序。

虽然冒泡算法在某些情况下非常有用,但它也有一些缺点,相信大家也一定在上面的例子中发现,由于它需要比较和交换相邻的数,因此它的时间复杂度比其他一些排序算法要高。当需要排序的数据规模很大时,冒泡算法的效率就会很低,不太适用。

# 2. 选择排序(Selection Sort)

选择排序是一种简单直观的排序算法,它也是一种交换排序算法,和冒泡排序有一定的相似度,可以认为选择排序是冒泡排序的一种改进。

选择排序是一种简单直观的排序算法,它的基本思想是:每次从未排序的数列中选出最小(或最大)的一个数,放在已排序的数列的末尾,直到所有数都被排序完成。具体的实现方法是:从数列中找出最小的数,放在数列的起始位置,然后再从剩余的数列中找出最小的数,放在已排序数列的末尾,以此类推,直到所有数都被排序完成。选择排序的时间复杂度是 O (n²),虽然它的时间复杂度比快速排序、归并排序等算法高,但是它的简单易懂、代码简单,适用于小规模数据的排序。

上面的长段定义一定看的有点烦吧,下面就是一个例子,他的应用就像你在挑选衣服时的选择一样。当你在某个服装店看到一堆衣服时,你会不停地选择其中的最小或最大值,直到你挑选出一件最合适你的衣服。这就是选择排序算法!

总之,选择排序算法是一个基本的排序算法,常见于生活中。但是在实际应用中,选择其他高效的算法可能更为合适。(●'◡'●)

# 3. 插入排序(Insertion Sort)

如果直接说插入排序算法,大家可能一时是想不起他在生活中的应用的,但是 “扑克牌” 这种纸牌游戏大家几乎每个人都玩过,在摸牌、理牌的过程中将新拿到的排插入之前摆好的牌扇中是在正常不过的。而这个过程就是使用了插入排序法

图片

就比如你现在的牌扇是里的牌分别是 3,3,7,8,10,然后又摸到一张 6,如果要从小到大排序,那自是一个一个比较,然后放到 3、7 之间。插入排序算法就像是我们整理扑克牌的过程一样,我们一张一张地拿起牌,找到合适的位置插入,然后再拿下一张牌继续进行这个过程,直到所有的牌都被排序好了。

虽然插入排序算法相对简单,但是它还是有一些缺点的。插入排序算法的缺点是,对于数据量比较大的情况,它的时间复杂度会很高,排序的速度会比较慢。此外,它是一种稳定的排序算法,也就是说,如果有两个数的大小相等,排序后它们的相对位置不会改变。但是,插入排序算法的稳定性是以牺牲空间复杂度为代价的,因为在排序的过程中需要使用额外的空间来存储一些临时变量。

总的来说,插入排序算法适用于一些小规模的数据排序,但对于大规模的数据排序,还是需要使用更加高效的算法。

# 4. 希尔排序(Shell Sort)

在我们日常生活中,有时我们需要对一些事物进行排序。例如,我们可以按照价格对一些物品进行排序,或者按照年龄对一些人进行排序。这时候,希尔排序就可以帮助我们快速地对数据进行排序。

希尔排序(Shell Sort)[也称 “缩小增量排序”(Diminishing Increment Sort) ] 得名于它的发明者,Donald Shell。希尔排序在当时被认为是一种划时代的算法,因为它可以显著提高数据排序的速度。完全区别于前面所介绍的时间复杂度为 O (n²) 的 “龟速算法”,开创了排序算法新的时代。

希尔排序是插入排序算法的一种改进版本。它的优点在于可以提高插入排序的速度,并且对于不同的数据集合也可以使用不同的步长来进行排序。在希尔排序中,数据会被分成若干个子序列,每个子序列进行插入排序,最后再整体进行一次插入排序,直到所有的数据都被排好序。

假设我们要对数组 [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] 进行希尔排序。

首先,我们按照增量为 5 来分组,得到两个子序列 [9, 4] 和 [8, 3, 7, 2, 6, 1, 5, 0]。

然后我们对每个子序列进行插入排序,得到 [4, 9] 和 [0, 1, 2, 3, 5, 6, 7, 8]。

接着我们按照增量为 2 来分组,得到四个子序列 [4, 0], [9, 1], [2, 5], [3, 6, 7, 8]。

再次对每个子序列进行插入排序,得到 [0, 4], [1, 9], [2, 5], [3, 6, 7 ,8]。

最后我们按照增量为 1 来分组,也就是对整个数组进行插入排序,得到 [0 ,1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9]。这样就完成了希尔排序。

希尔排序算法的优点是适用于大规模数据排序,并且排序效率比较高。同时,它可以根据具体的数据集合进行优化,这使得希尔排序比其他算法更加灵活。不过,希尔排序的缺点是它的实现比较复杂,需要对步长进行选择和优化,这增加了算法的实现难度。

# ~. 猴子算法(Monkey Sort)

看了这么多个算法的介绍是不是有些累啦 (=・ω<=) 让我们来娱乐一下

假如这里有几张扑克牌,以及一只可爱的小猴子,你跟它说:“请你帮我把这些牌由大到小排起来。” 但是不论说什么他也是不可能听懂的,所以它完全不知道要干什么(哈哈哈哈)更听不懂希尔算法啥的。那么这只猴子只能无脑地随机打乱这几张扑克牌,然后看看它们是否排好序了。这就是猴子算法。

图片

猴子排序是一种非常有趣的排序算法,但它的实用性并不高。首先,它的时间复杂度非常不稳定,因为它的运行时间取决于随机排序的结果。如果数据规模很小,猴子排序可能很快就能排好序,但如果数据规模很大,它的运行时间可能会非常长,甚至无法完成排序。其次,猴子排序的平均时间复杂度非常高,通常为 O (n*n!),因此它无法用于处理大规模的数据排序。

但是!!!╰( ̄ω ̄o) 他也是有意义的。在一些特殊情况下,它可能会被用来测试算法的鲁棒性。例如,如果我们有一个排序算法,但不确定它在特定情况下是否能正确工作,我们可以使用猴子排序来测试它的鲁棒性。因为猴子排序是一种非常原始、简单的排序算法,它可以检验算法是否可以在极端情况下正确排序数据。

# 5. 堆排序(Heap Sort)

堆排序(Heap Sort)是一种基于完全二叉树(Heap)结构的排序算法。和其他排序算法不同的是,堆排序不需要对整个数据集进行多次遍历,而是通过构建堆来进行排序。让我们来看一下具体的实现过程。

首先,堆排序将待排序的数据集看作是一棵完全二叉树。这个二叉树由两个关键元素组成:根节点和子节点。每个节点可以有零个、一个或两个子节点。如果一个节点的值比它的子节点大(或者小),那么我们就称这个节点为 “堆顶”。

接下来,堆排序算法会按照以下步骤来进行排序:

1. 将待排序的数据集构建成一个最大堆(Max Heap)或最小堆(Min Heap)。
2. 每次将堆顶元素与堆底元素交换,然后将堆底元素从堆中移除。
3. 重新构建堆,使得剩余元素仍然保持堆的结构。
4. 重复步骤2和3,直到所有元素都被移除并排序完成。

堆排序算法在实际应用中也有很多用处。比如,有一场马术比赛,比赛共有 10 匹马参加,每匹马都有自己的成绩。为了确定比赛的名次,可以使用堆排序来对这些成绩进行排序。具体地,可以将成绩作为堆中的关键字,然后使用最大堆(Max Heap)来对成绩进行排序,最终得到的堆顶元素即为第一名,第二名以此类推。

另外,对于一个在线购物网站,需要将商品进行排序,以便更好地展示给用户。在这种场景下,可以使用堆排序来对商品进行排序。例如,可以将商品的销量、价格等作为堆中的关键字,然后使用最小堆(Min Heap)来对商品进行排序。这样就可以让用户更容易地找到自己想要的商品;又比如操作系统中的任务调度就可以使用堆排序来优化 CPU 的使用效率。此外,堆排序还可以用来进行大型数据集的排序,如数据库中的数据。

当然,堆排序也有一些优点和缺点。优点是它的时间复杂度是 O (n log n),效率较高,尤其适用于大数据集的排序。缺点是它的实现过程较为复杂,需要考虑的因素比其他排序算法多,而且在排序过程中会占用额外的空间。

总之,堆排序是一种基于完全二叉树结构的排序算法,它可以高效地对大型数据集进行排序,是一种十分实用的算法。

# 三、总结

非常感谢大家阅读我的博客文章。在这篇文章中,我介绍了一些常用的排序算法,包括冒泡排序、选择排序、插入排序、希尔排序、堆排序以及十分有趣的猴子算法。希望通过这篇文章,大家对排序算法有了一个初步的了解和掌握。

当然,排序算法的世界还有很多未知的领域等待我们去探索。在下一篇文章中,我将继续为大家介绍一些更高级的排序算法,例如归并排序、计数排序、基数排序、快速排序以及更多有意思的排序算法。我将比较它们的优缺点和适用场景,希望能够帮助大家更好地理解和应用这些算法。

感谢阅读,拜拜ヾ (・ω・`) o