手记

常用的排序算法(二)--插入排序(PHP实现)

常用的排序算法系列


插入排序

插入排序是一种逻辑上非常好理解的排序方式,整个排序的核心就是不断在当前已经排好部分数据的数组里,找到合适的位置插入新数据。就像抓扑克牌,抓一张,然后再手里已经部分已经排好序的手牌的找到位置插进去。

举例说明:

首先,常见的取数据方式是,取数组第一位作为开始排序的数据。(取最末尾也可以,但是操作顺序需要调成对称相反的模式)

假设当前需要排序的数组如下,排序原则是从小到大:

ar = [ 34, 5, 5, 555, 5, 4, 14, 5, 88, 89, 54]

我们定义一个数组叫 order[] , 用来表示部分已经排好序的数据,也就像是抓扑克牌的那只手里的牌。最开始的时候,手里是没有牌的, order[] 也就是一个空数组。

现在开始遍历需要排序的数组,也就是开始一张张抽牌,我们把第 i 次从ar数组中抽牌,,抽进来的这个数据叫做 ar[i]

如果最开始手里没有牌,$order[] 是一个空数组,那么进来的第一个数据,就可以直接放进order[]数组里面。

之后手里就有牌了,需要把新拿的牌和已有的牌一个个对比。因为排序规则是从小到大,那么就只需要遍历order[]数组,找到一个下标位置j,恰好 order[j-1] < 抽进来的数据 <=order[j] , 这时候就把抽进来的数据 ar[i] 插入到 order[k] 的位置即可。

为了方便,我们把已经排好序里对比的数据叫做data , 也就是data = order[j]。那么简化的判断条件就是,抽进来的数据ar[i] <= 已经排好序里对比的数据data

因为是遍历order[]数组,如果这个抽进来的数据 ar[i] ,比 order[j] 数据每一个都大,那么就直接插入到数组末尾就好了。又因为order[j] 是部分已经从小到大排好的数据,所以直接拿order[末尾]来比较就好了。

if( 抽进来的数据ar[i] > order[末尾] ){
	// 也就是 `ar[i]` 比 `order[j]` 每一个都大
	order[末尾+1] = 抽进来的数据ar[i] 
}

至此,整个排序流程就很明晰了,遍历需要排序的数组ar[] , 一个个抽取数据 ar[i],然后和已部分排序的order[] 对比,一次次按照大小顺序,找到位置插进order[] 数组里 。最后得到的order[] 数组,就是排序的结果。

数组插入的问题

关于插入的部分,还有一个小细节的问题。

现在有一个数组 { a0, a1, … a(j-1), a(j) …a(n) }, 如何在 j 这个位置插入一个数据,使其变成新数组 { a0, a1, … a(j-1), a(x) ,a(j) …a(n) } 呢?

下面提供两种基于数组操作的思路。

通过逐个向后移动来插入

从末尾的 a(n) 开始向前遍历,逐个往后移动一个位置。直到遇到需要插入的位置 j ,这就刚好腾出来了一个空位,这个空位直接放入需要插入的 a(x) 即可。

通过切分再连接来插入

把数组根据位置j拆分成两个部分,前一个是 { a0, a1, … a(j-1)} ,后一个是{ a(j) …a(n) } ,在前一个数组的末尾添加插入的 a(x) ,然后再连接前后两个数组形成一个新数组即可。

看起来这两种操作差不多,但是实际上,通过逐个向后移动来插入会更快一些。因为数组合并的操作,怼人封装成了系统的函数,但本质上也就是一个个数据赋值的过程。

除了基于数组操作,还可以基于链表来实现插入。不过php没有指针变量,但也可以用对象来做一个伪链表。链表还是用C++来写会更好,基于链表的插入,我们下一篇再谈~

PHP代码运行如图:

PHP版本的实现代码如下:


<?php

$ar = [34, 5, 5, 555, 5, 4, 14, 5, 88, 89, 54];
echo 'input:';
print_r(json_encode($ar));
echo "<br/>";
echo "<br/>";

$order = insertAr($ar);
echo "<br/>";
echo "<br/>";
echo 'result:';
print_r(json_encode($order));



/** 基于数组实现的插入排序
 * @param array $ar
 * @param $len
 * @param $insert_way 0数组移动插入数据  1裁切插入数组数据
 */
function insertAr(array $ar, $len = null, $insert_way = 0)
{

    if ($len === null) {
        $len = sizeof($ar);
    }

    $order = [];

    //fill the order[]
    for ($i = 0; $i < $len; $i++) {//i for ar index
        echo "<br/>";
        print_r(json_encode($order));

        $size = sizeof($order);

        if ($size == 0 || $ar[$i] >= $order[$size - 1]) {//empty order OR ar[i] is biggest

            $order[] = $ar[$i];

        } else {//compare one by one

            for ($j = 0; $j < $size; $j++) {

                if ($ar[$i] <= $order[$j]) {//add into

                    if ($insert_way == 1) {
                        cutAdd($order, $j, $ar[$i]);
                    } else {
                        moveAdd($order, $j, $ar[$i]);
                    }

                    break;

                }//end add into

            }//end for of order[j]

        }//end compare one by one

    }//end fill the order[]

    return $order;

}

/** 通过区分数组来插入一个指定位置的数据
 * @param $ar
 * @param $index
 * @param $data
 */
function cutAdd(& $ar, $index, $data)
{
    $left = array_slice($ar, 0, $index);
    $right = array_slice($ar, $index);

    $left[] = $data;

    $ar = array_merge($left, $right);

}


/** 通过逐个移动来插入一个指定位置的数据
 * @param $ar
 * @param $index
 * @param $data
 */
function moveAdd(& $ar, $index, $data)
{
    $len = sizeof($ar);
    $ar[$len] = null;
    for ($i = $len - 1; $i >= $index; $i--) {
        $ar[$i + 1] = $ar[$i];
    }

    $ar[$index] = $data;
}


8人推荐
随时随地看视频
慕课网APP