转载

插入排序以及优化

插入排序是一种非常重要的排序算法,对于接近有序的数组来说,使用插入排序进行排序可能要比使用归并排序等O(NlogN)级别的排序算法还要快,很多高级的算法也都采用了插入排序的思想。这里总结一下,插入排序的思想以及插入排序的优化方式。

记得我一开始学习插入的排序的时候,总是习惯这么写插入排序(用PHP来实现,用其他语言也是一样):

<?php
$rand_arr = [6,5,1,7,3];
$array_count = count($rand_arr);
for($i=1;$i<$array_count;$i++){
    //每次取出数组中的第i个元素与数组中的index小于i的元素进行比较
    //如果需要比较的数据大于第i个元素,就进行数据的交换,将较大值放到第i的位置
    //从第0个元素开始向左进行运动,内部循环一次取出前i个元素中的某一个元素与第i个元素进行比较
    for($j=0;$j<$i;$j++){
        if($rand_arr[$j] > $rand_arr[$i]){
            $temp = $rand_arr[$j];
            $rand_arr[$j] = $rand_arr[$j+1];
            $rand_arr[$j+1] = $temp;
        }
    }
}
print_r($rand_arr);
?>

排序的顺序如下:

开始阶段:   [ 6   5   1   7   3 ] : i =1,指向index=1的元素,也就是5,j=0,指向index=0的元素,也就是6

开始第一次外部循环: 6和5进行比较,因为5<6,所以进行数据交换的操作(6和5进行交换),得到一下的结果 [ 5 , 6 , 1 , 7 , 3 ],此时j+1 = 1, j=i,所以内部循环结束,开始第二轮外部循环。

开始第二轮外部循环: i=2,指向index=2的元素,也就是1,j=0,指向index=0的元素 ,也就是5。因为1小于5,所以进行交换,得到以下的结果[ 1 , 6 , 5 , 7 , 3 ],i就等于5。此时j+=1,j=1,继续进行内部循环,因为5小于6,所以继续进行交换,依次内推。整个循环过程如下:

[  6   5   1   7   3  ]

[  5  6   1   7   3  ]

[ 1   6   5   7   3  ]

[ 1   5   6   7   3  ]

[ 1   3    6   7   5 ]

[ 1   3    5   7   6 ]

[ 1   3    5   6   7 ]

经过上面的分析,大家可以看出,当第i个元素要与第0个元素到第i-1个元素进行比较的时候,第0个元素到第i-1个元素总是保持有序的(这一点,非常的总要,这也是插入排序的重要特点之一)。好了,既然是有序的,当我取出第i个元素进行比较的时候,如果第i个元素,小于第j个元素,那就意味者第i个元素要小于第j个元素到第i-1个元素了,后面的内部循环的比较就可以不进行比较了,这样我们就可以节省下进行数据不比较的比较的资源的消耗了。改写的代码如下:

<?
$rand_arr = [ 随机数组 ];
$array_count = count($rand_arr);
for($i=1;$i<$array_count;$i++){
    //改为从右到左进行比较排序
    for($j=$i-1;$j>=0;$j--){
        if($rand_arr[$j]>$rand_arr[$j+1]){
            //进行数据的交换
            $temp = $rand_arr[$j];
            $rand_arr[$j] = $rand_arr[$j+1];
            $rand_arr[$j+1] = $temp;
        }else{ //提前退出内层循环可以减少数据的比较
            break;
        }
    }
}
?>

好了,上面的优化方案,会帮助我们减少不比较的数据比较,但是插入排序中数据的交换次数还是很多,一次数据交换,相当于三次赋值操作,如果我们能够降低数据的交换操作,用别的方式来实现数据的交换,就肯定能提高插入的排序的执行效率。下面我直接贴出代码,大家先看一下:

<?php
$rand_arr = [随机数组];
$array_count = count($rand_arr);
for($i=1;$i<$array_count;$i++){
    $compare_value = $rand_arr[$i];
    for($j=$i;$j>0;$j--){
        if($compare_value < $rand_arr[$j-1]){
            $rand_arr[$j] = $rand_arr[$j-1];
        }else{
            $rand_arr[$j] = $compare_value;
            break;
        }
    }
    if($j==0){
        $rand_arr[0]=$compare_value;
    }
}
?>

我说一下,上面代码的思路:加入需要的排序的数组还是上面的 [ 6   5   1   7   3 ]。在初始的状态下,i = j =1,都指向5,这时候取出第i个元素5,将该值赋予给比较变量compare_value,以这个变量为基准与第j-1的元素到第o个元素进行比较(从右到左),如果compare_value的值小于第j-1个元素,就将第j个原始设置为第j-1个元素的值。如果compare_value的值大于或者第j-1个元素,就将第j个元素设置为compare_value的值,根据插入排序的第0个元素到第j-1的元素的有序性,下面就不需要进行比较了。 排序的过程如下:

初始状态下:[  6  5  1  7  3 ] — i=j=1,都指向元素5,取出第i个元素作为比较值,也就是比较值等于5。使用比较值5与第j-1个元素(也就是第0个元素)进行比较,5<6,所以第j个元素(也就是第1个元素),赋值为6,得到以下结果[ 6  6  1  7  3 ]。此时j-=1,j=0,退出内部循环,所以第o个元素赋值为compare_value,也就是5,得到以下结果[ 5  6  1  7  3 ],这样就完成了一层外部循环,而没有采用一次数据的交换。然后依此类推,完成排序。这样我们就做到了,减少数据交换的次数来提升执行效率的目的了。

下面,我用一个数组长度为10000,数组中的元素范围在0-10000的随机数组作为测试用例,来测试一下,以上三种插入排序方式各自完成排序所需要的时间情况。

使用第一种方式:执行时间:24.062285900116

使用第二种方式:执行时间:14.136403083801

使用第三种方式:执行时间:6.7661621570587

可以说是,差距惊人,当数组长度更大的时候,这种差距还回更大。

原文  http://sunms.codefly.top/2016/12/10/插入排序以及优化/
正文到此结束
Loading...