今天给大家分享五个入门的排序算法,希望通过案例的形式能让算法变得更加容易理解,让学习的过程变得有趣,题目中所举的例子仅供学习使用,切勿模仿。
一、冒泡排序
故事的主人公是法外狂徒张三,张三仗着有罗老师替他辩护,横行无忌为所欲为。
星期一,张三去市场上买菜,这时候市场上已经排起了一条长长的队伍。张三拉起最后一个人认他为大哥,然后往前插队。说:“你去后面排着去”。那人见张三插队,不爽,但是看到张三认的大哥身强体壮, 忍了忍,于是被成功插队。就这样子张三一个一个往前插队,凡是没他大哥强壮的,战斗力不如他大哥的都被他插队了。直到前面出现了个2米高180多斤的壮汉,张三想插队但是认的大哥被对方三两下放倒,于是插队失败了。但是机智的张三认了新的壮汉做大哥,吼了一嗓子:“打不过我大哥的都往后面排队去”。这样子又得以往前挪了几个位置,直到遇到更大只战斗力更强的。这样子一轮过后,张三跟着最强壮的大哥成功买到了菜。但是张三发现他只买了菜没有买肉,于是第二轮开始,又是从最后一个人开始,认大哥,打不过他大哥的往后排队,又跟着队伍中的第二强者就能成功买到菜。但是他又忘了买......, 经过n轮后就能实现战斗力从高到低的一个排序,这也就是冒泡排序。
冒泡排序就是从第一个元素开始,两两比对,把更大的数值往前传递(这取决于需求是从大到小排序还是从小到大排序, 此处例子选择后者),遇到更大的值就从更大的值开始继续往后比对,每一轮把最大的值放到好的队列中去。
专业解释:
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
二、选择排序
星期二,张三又来到市场上买菜, 但是他想啊,昨天那种方法好像不太行,每一轮得认好多个大哥,他张三不要面子的吗,于是他花了重金租了个可以扫描战斗力的检测仪。他从队伍最后面开始,扫描每个人的战斗力。一轮过后,他找到了战斗力最高的那个小伙子,果断的认了大哥。他嚣张的带着自己的大哥走到队伍最前面的那个人面前,拍了拍他对他说:“你去我大哥的位置排着去”。说罢让大哥亮了亮九块腹肌。排第一的人无奈的摇摇头,走到了那个刚空出来的位置。张三成功买到了菜后,觉得好玩又继续扫描仪找队伍中战斗力第二、第三、第......的人认大哥为所欲为。这样子n轮过后,同样实现了战斗力从高到低的一个排序,这也就是选择排序。
选择排序就是每一轮找出一个最大值,把它换到当前未排序数组的第一项去,同时原本未排序数组的第一项去到这个元素原本的位置。
专业解释:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。
再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
重复第二步,直到所有元素均排序完毕。
三、插入排序
星期三,张三接着来到市场上买菜,今天他没钱租检测仪了,但是他今天带了个变声器。他先是走到排在第二位的人的旁边,用变声器装成第二位的声音去挑事,成功引发了第二位和前面第一位的冲突。果不其然,他们两个掐在了一块。战斗结束后,赢的排在了第一位,输的排在了第二位。张三又来到第三位的位置装成他继续挑事,让他一直往前插队,打赢了就往前挪一个位置,输了就停留在当前的位置,然后继续找后面的人往前插队。直到找到最后一位。这时候所有人已经到达了自己所能打过的人的最前方。再次实现了战斗力从高到低的一个排序,这也就是插入排序。
插入排序就是从第二位开始,每次当前这一位如果比前面的数值大就往前排,直到找到每个人当前所能走到的位置。类似于打扑克牌你原本有9865, 来了张7,会把它放到6的前面去,直到它比8小,就无法往前了。
专业解释:
将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
四、希尔排序
星期四,张三同样来到市场,今天他不想认大哥了,他要捡回自己丢掉的面子。他想了个法子帮大家按照自己的战斗力排好位置。首先他数了数人数,统计了一下队伍一共有20人。他取了个中间值,让排在中间值前面的和排在中间值后面的两两PK。 如果前面的人输了就要跟赢家交换位置,如果赢了则保持不变。这样子一轮过后,战斗力强的基本都排在前面了,然后第二轮他又让前后紧挨着的兄弟两两进行PK,同样如果前面的人输了就要跟赢家交换位置。最后大体上排的差不多了,让大家从第二位开始进行昨天的插入排序,这样子能保证让每个人的比对次数尽可能的小。这就是希尔排序。
希尔排序就是先通过不同的间隔,让原本队列中的人进行两两比对交换,先让队伍整体有个大概的顺序,然后再通过一轮插入排序实现数组的整体有序。当然我案例中的间隔可能取的比较大,数值大了可能在算法的时间复杂度上还是得再进一步的优化,实际使用中可以通过将数组分成一个比较细化的间隔,让插入排序前数组尽可能的变得有序。
专业解释
选择一个增量序列 t1,t2,……,tk,其中 ti > tj, tk = 1;
按增量序列个数 k,对序列进行 k 趟排序;
每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。
五、快速排序
星期五,张三照旧到市场买菜的一天。今天他有点心累了,想到自己昨天让大家排个队折腾大半天,他今天索性摊牌了。打得过我的站到我前面,打不过的排到我后面。快速把队伍分成了两个部分。让他们各自照着周一冒泡排序的规则,每轮两两PK胜者排前面继续往前排。直到两个队伍都排出了顺序。他终于长长的呼出了口气。感叹了声:“早这样子不就完事了吗,啥都靠我带不动啊”。这就是快速排序。
快速排序就是在数组中找出一个基准值。可以拿随机的三个数字进行比对取中位数。然后快速把数组分成左右两个部分,分别进行冒泡排序。
专业解释
1 从数列中挑出一个元素,称为 "基准"(pivot);
2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序;
总结:
冒泡排序:就是把大的往前冒,两两比对一直到把最大值排在最前;
选择排序:跟冒泡有点像,但是选择是直接先找到最大值,拿最大值跟最前面的值交换位置;
插入排序:跟冒泡也有点像,但是它从第二位开始,冒泡是从最后一位开始,而且它是每次冒泡到自己冒不动的位置后,就停在那里了;
希尔排序:是插入排序的优化版,只是提前通过设置间隔,让间隔中的数字两两比对,先让数组形成一个基本的有序状态,再进行一次插入排序,尽可能的减少插入排序时每轮的比对次数;
快速排序:找个基准值,把数组分成大于基准值的部分和小于基准值的部分,分别做冒泡排序最后组合。
另外,从排序的稳定性来说,只有冒泡和插入排序是稳定的。其他三个不稳定。稳定性指的是当两个元素相等的时候,原本排在前面的排序过后必然还会排在前面。
彩蛋:
排序的算法不止这五个,但是我只举了五个例子,可能有小伙伴会有疑惑为什么。我的回答是:因为星期六和星期天张三休息,他没有去买菜,他点外卖哈哈!
觉得好玩并且有所收获的小伙伴们可以点个关注点个赞,跟着我一起从算法入门向前学习,一起冒泡呀!
文中插图及专业解释引用自菜鸟教程: www.runoob.com/w3cnote/ten…