排序基础(插入排序和选择排序)

(以下代码均以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

我们可以看到插入排序算法又快了很多。

若是数组顺序不乱,那么插入排序会更加的快!!因为插入排序遇到了合适的位置就不遍历了,但是选择排序会遍历到底,找到最小数。

所以在性能上来说,插入排序是优于选择排序的~!

今天就是我学习的算法基础,明天若有新的进度继续更新

原文地址:https://www.cnblogs.com/claireyu1219/p/6607635.html