插入排序原理:它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
插入排序核心:假设第一个元素排好,之后的元素对排好的部分从后向前比较并逐一移动。
插入排序实现:
插入排序过程动画:
选择排序:
选择排序和插入排序类似,都是in-place comparison sort。
选择排序核心思想:从未排好的部分的第一个作为最小(最大)的,然后依次和剩余的比较,如果有更小(更大)的,记下下标,以此作为新的最小(最大)值,继续比较,一趟结束后,然后和第一个进行交换。
选择排序实现:
选择排序过程动画:
思想
插入排序的思想有点像打扑克抓牌的时候,我们插入扑克牌的做法。想象一下,抓牌时,我们都是把抓到的牌按顺序放在手中。因此每抓一张新牌,我们都将其插入到已有的排好序的手牌当中,注意体会刚才的那句话。也就是说,插入排序的思想是,将新来的元素按顺序放入一个已有的有序序列当中。
举个例子可能更容易理解一些,假设有这样一系列数字:
8 2 4 9 3 6 首先我们考虑数字2,假设后面的数字不存在(手中只有一张8,又抓来了2),那么显然2应该放在8的前面。
2 8 4 9 3 6 又抓来了一张4,现在大家都知道应该怎么办了吧?
2 4 8 9 3 6 又来了个9,没错,正好不用换顺序
2 4 8 9 3 6 同样的道理,考虑3该放的位置,显然放在2和4的中间
2 3 4 8 9 6 最后一个也是一样,最后得到从小到大的序列
2 3 4 6 8 9 完成排序
怎么样,这下全明白了吧,其实很简单。
代码
printline("before sort:", v);
for (int i=1; i<v.size(); i++){
int key = v[i];
int j = i-1;
while (j >= 0 && v[j] > key){
v[j+1] = v[j];
j--;
}
v[j+1] = key;
}
printline("after sort:", v);
代码中,key相当于每次要比较的元素。
分析
插入排序的思路很简单,很清晰,是一种最常见最简单的排序方法。但是可以看出,由于需要两层循环,外层循环n-1次,内层循环每次递增一次。当输入完全从小到大有序时,只需要常数的时间,这当然是最好的情况。但是我们不能期望输入,当输入完全逆序时,最坏的情况就出现了,显然时间复杂度是O(n*n)的。我们都很清楚,这个时间复杂度在排序中并不能算好的。这也是为什么插入排序虽然简单,但并没有被广泛应用的原因所在。
简单选择排序
此文章前半部分--》》简单选择排序是另一位大哥写的博客:http://blog.csdn.net/hguisu/article/details/7776068
但是我这想要说的是后半部分双向选择排序,因为这个排序虽然那位大哥也写了,但是bug大多,而且下面评论的人有回复和纠正,但是似乎纠正的代码也是错的,所以才想着写这篇博客。
思想:
在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后一个数)比较为止。
操作方法:
第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;
第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;
以此类推.....
第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,
直到整个序列按关键码有序。 算法实现:
简单选择排序的改进——二元选择排序
简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:
算法实现一:那位大哥的写法
i从0开始,不然第一个数据,就没有参与比较, 这是漏洞之一
算法实现二:评论大哥(暂且这么叫吧)的写法
这里的漏洞是 因为max值本来就是在比较下重动态更新的,所以将
还是等于 i 到 n - i - 1 之间的任何一个都是无所谓的;
最大的bug还是运行这两个算法基本上都会有错误,也许是因为那两位大哥举例子用的数组的缘故,所以没被发现程序思想没错,实现的方式也没错,想要解决bug需要解决两点
第一点:选出min和max的位置
按照上面的算法写的话,我举个例子
数组:10 8 9 7
i = 0 ; //从第一个数开始
这里错的地方是因为 j = i + 1 ,从上面的数组很容易看出当第一位数是这组待排序的数中最大的时候,j = i + 1 就会直接跳到第一个数的后面去选择后面中的最大最小,所以当第一位不管是这组数最大还是最小值时,此上面两种算法绝对出错
换而言之,解决问题的办法就是将第一位数也拉进来比较。
第一点:min和max的位置都出来了, 关键的地方是先和第n - i - 1个位置交换max呢?还是先和第 i 个位置交换min呢
为什么要这么考虑?下面我同样举个例子
还是数组:[...排好序的数..] 10 8 7 5 6 9 [...排好序的数...],无疑此数组中max的位置是10的下表, min无疑是5的位置,假如按照上面大哥的写法,无论什么情况都是先交换最小的,然后再交换最大的数组一趟排序后的结果是[...排好序的数..9] 8 7 10 6 [5...排好序的数...],因为5的位置先和10的位置交换,但是max的值还是原先10的位置,也就是交换后5的位置,之后再将max位置上的5和n - i - 1位置上的9交换.由于排好序的数是不会参与比较的,所以此算法无论如何都是得不到正确结果的,这里只举出了这一种特殊的交换位置,还有两种特殊的交换位置自己想象一下立马就可以出来了
好了,到了这里,问题全部跑出来了,
因为要考虑交换位置的情况,所以代码的臃肿度立马提升了不少,下面给出我自己的写的代码,如果以后想到更好的解决办法,会优化的,
如果是那位大哥大姐能有更好的想法,好心的话也可以回复一下我
算法实现三: