排序基础(插入排序和选择排序)
(以下代码均以javascript实现)
选择排序:
首先来介绍一下选择排序,假如我们有一个数组[1,4,7,2,5,9]。
1、现在我们选择出这个数组中最小的数,和下标为0的数作交换。从数组中看就是1,所以不用做交换了。
2、剩下[4,7,2,5,9],我们从这里面选出最小的数,和原数组下表为1的数作交换。交换后即[2,7,4,5,9]。第二次交换完成后,数组为[1,2,7,4,5,9]。
......
我们就是不断的在剩下数组中找到最小的数,然后和对应的数进行交换。在这个算法中,我们要进行双循环,并且每次都是循环到底。
代码如下:
function SelectSort( arr ){ if(Object.prototype.toString.apply(arr) === "[object Array]"){ //开始进入循环 for(let i = 0; i < arr.length; i++){ //标记查询索引 let MinInt = i; let temp = null; for(let j = i + 1; j < arr.length; j++){ //判断大小 if( arr[j] < arr[MinInt]){ //如果有比MinInt更小的数就更换索引 MinInt = j; } } if( i !== MinInt){ //如果最小数是本身不做交换 temp = arr[MinInt]; arr[MinInt] = arr[i]; arr[i] = temp; } } } }
选择排序算法很简单,我们只需要流程是什么样就可以立刻写出代码,接下来我们看插入排序。
插入排序就像摸牌,一开始你的手上没有牌,抽取第一张牌放到手里,抽取第二张的时候你要和第一张做对比,如果比第一张大就放到第一张后面,如果比第一张小就放到第一张的前面。然后继续摸第三张牌,分别和前面两张作对比,然后这样继续下去。
接下来我用数组来详细说:我们有一个数组[1,7,4,2,5,9]
1、首先我们摸第一张牌1放到手里;
2、然后摸第二张牌7,和1做比较,发现7更大,那么就放到1的后面不做交换;
3、摸第三张4,和7比较更小,那么4和7做交换;然后4再和1做比较,比1更大,那么不做交换;第三次摸牌结果为[1,4,7,2,5,9];
......
接下来都是一样的,当前一个数比摸的牌更大,就做交换,当遇到比摸的牌更小的数,就可以不动了,因为手上的牌都是已经排序的,如果有一个数比手上的数更小,那么它前面的数肯定比手上的数更小。
按照这种逻辑思路,我们来写出代码:
for(let i = 1; i < arry.length; i++){ //开始摸第一张牌 for(let j = i; j > 0; j--){ //和手上的牌进行对比,如果更小就进行交换 if(arry[j]>arry[j-1]){ let temp = arry[j]; arry[j] = arry[j-1]; arry[j-1] = temp; }else{ break; } }
我们来对比一个选择排序算法和插入排序算法,通过console.time("xxx")来看看各自的运行时间。
这里我随机生成10000个1到100000的数,随机生成函数代码贴出来,具体就不做解释了
function random(rangeL, rangeR, n){//[rangeL,rangeR]范围,总共有n个数 let arr = []; for( let i = 0; i < n; i++ ){ let num = parseInt(Math.random()*(rangeR-rangeL+1)+rangeL,10); arr.push(num); } return arr; }
然后使用console.time("xxx")和console.timeEnd("xxx")来包裹我的循环,在Chrome测试出了两个算法各自运行时间
selectsort: 141.809ms
InsertSort: 70.291ms
得出插入排序算法比选择排序算法更快。
但是好好想想插入排序算法是否可以进行优化,因为不停地交换位置非常的耗时间,我们可以把手中的牌拿出来,先作对比,然后找到合适的位置再放进去,这样不就节省了很多时间吗?
我们回到这个数组:[1,7,4,2,5,9]
1、我们手中第一个牌是1,摸第二张牌7,和1比较不用交换。
2、接下来摸第三张牌4,单独用一个变量存储4,然后和7比较,发现需要放到7的前面,这个时候我们要做的是把7复制到4的位置。然后和1比较,发现不用交换了,这个时候让前面放置4的变量放到7的位置上,这个时候数组变成了[1,4,7,2,5,9]。
3、接下来第四张牌2,和7做对比需要交换,这个时候把7复制到2的位置[1,4,7,7,5,9],然后和4做对比也需要交换位置,把4复制到7的位置[1,4,4,7,5,9],然后再和1比较不需要交换位置,最后把存储2的变量放到4的位置[1,2,4,7,5,9]。
以此类推,不断的挪动位置,给手中的牌腾出空间。这样我们就不用总是交换了,节省了很大的时间,代码实现如下(可以用while,也可以用for):
for(let i = 1; i <arry.length; i++){ let num = arry[i];//标记摸到的牌到变量 let j = i - 1;//初始化手中的牌 for(; j >= 0 && arry[j] > num; j--){//进行比较并且复制 arry[j+1] = arry[j]; } //找到合适的位置然后赋值 arry[j+1] = num; }
我们再来测试一下这个插入排序算法和选择排序算法的性能
selectsort: 124.689ms
insertsort1: 44.823ms
我们可以看到插入排序算法又快了很多。
若是数组顺序不乱,那么插入排序会更加的快!!因为插入排序遇到了合适的位置就不遍历了,但是选择排序会遍历到底,找到最小数。
所以在性能上来说,插入排序是优于选择排序的~!
今天就是我学习的算法基础,明天若有新的进度继续更新