排序算法

1. 冒泡排序

以由大到小排序为例,从 i = 0 的位置开始数组间相邻的数值进行比较,若不满足排序规则(由大到小,或者由小到大)交换两个数,这样做就让最大值沉到了最底下。然后 i 自增,重复上面的操作,对剩余未确定位置的值进行对比操作,让最大值沉下去,直到全部数组确定位置,排序完成。

<?php
// 冒泡排序
function bubbleSort (&$arr) {
    $n = count($arr);
    for ($i = 0; $i < $n; $i++) {
        for ($j = 0; $j < $n - $i - 1; $j++) {
            if ($arr[$j] > $arr[$j+1]) {
                $tmp = $arr[$j];
                $arr[$j] = $arr[$j+1];
                $arr[$j+1] = $tmp;
            }
        }
    }
}


$num_arr = [6,4,2,1,3,8,9];
bubbleSort($num_arr);
echo '<pre>';
var_dump($num_arr);

2. 快速排序

以从小到大排序为例,以一个数为基准 a 位置为 p1(一般为左侧第一个数) 从右到左依次与其进行对比,遇到的小于该值的数 b 位置为 p2,将 b 的值付给 p1,然后再从左到右依次与 a 对比,遇到大于 a 的值 c 位置为 p3,将 c 赋值到 p2,然后将 a 赋值给 p3,如果未遇到满足条件的数则不动,目的是为了把小于 a 的值全部整理到 a 的左侧,大于 a 的值全部整理到 a 的右侧,循环重复该操作直到实现上面的目标,然后以 a 的位置为基准分别将左右两侧的数值递归重复上述操作,直到排序完成。

<?php

function quickSort(&$arr, $low, $high)
{
    if ($low >= $high) {
        return;
    }
    $left = $low;
    $right = $high;
    $key = $arr[$left];     /* 用数组的第一个记录作为分区元素 */
    while ($left != $right) {
        while ($left < $right && $arr[$right] > $key) {
            --$right;
        }
        $arr[$left] = $arr[$right];
        while ($left < $right && $arr[$left] < $key) {
            ++$left;
        }
        $arr[$right] = $arr[$left];
    }
    $arr[$left] = $key;
    quickSort($arr, $low, $left - 1);
    quickSort($arr, $left+1, $high);
}
$num_arr = [6,4,2,1,3,8,9];
quickSort($num_arr, 0, 6);
echo '<pre>';
var_dump($num_arr);

3. 插入排序

以从大到小排序为例,从数组左侧第 i(i 的初始值为 2) 个数 a 开始,将 a 存入临时变量中,将 a 与左侧的数依次对比,遇到大于 a 的数则向右移动一位,当遇到小于 a 的值 b 时则停止,此时 b 右侧有一个空位,将 a 插入这个空位,则 i 位置左侧 (包括 i )的排序为由小到大,i 自增重复上述操作,知道到达数组尾部

<?php

function insertSort(&$arr, $n) {
    for ($i = 1; $i < $n; $i++) {
        $temp = $arr[$i];
        $j = $i - 1;
        while ($j >= 0 && $arr[$j] > $temp) {
            $arr[$j+1] = $arr[$j];
            $j--;
        }
        $arr[$j+1] = $temp;
    }
}

$num_arr = [6,4,2,1,3,8,9];
insertSort($num_arr, 7);
echo '<pre>';
var_dump($num_arr);

4. 希尔排序

希尔排序相当于优化后的插入排序,举一个例子,假如数组的最小值被放在了最末,那么想把它排到它在数组中应有的位置需要移动整个数组的数据,这样效率会比较低,希尔排序设立了一个间隔,相当于缩小了数组,将间隔上的数值进行插入排序,这样可以用比较小的交换次序就能让每个数值靠近自己应该存在的位置,缩小间隔,依旧按照上述规则整理着数组,最后一次排序时就相当于最原始的插入排序了,但是由于已经经过了上面的操作,此时的数组已经很接近有序数组了,再排序就很容易了。

<?php

function shellSort(&$arr, $n) {
    $d = intval($n / 2);
    for ($step = $d; $step > 0; $step = intval($step/2)) {
        for ($i = $step; $i < $n; $i++) {
            $j = $i - $step;
            $temp = $arr[$i];
            while ($j >= 0 && $temp < $arr[$j]) {
                $arr[$j + $step] = $arr[$j];
                $j -= $step;
            }
            $arr[$j + $step] = $temp;
        }
    }
}

$num_arr = [6,4,2,1,3,8,9];
shellSort($num_arr, 7);
echo '<pre>';
var_dump($num_arr);

5. 选择排序

从数组左侧开始位置为 i = 0,从左向右依次对比,找出最小值,将最小值与 i 位置的值进行交换,i自增,重复上述操作,依次将从剩余未排序的数中找出最小值交换到对应的位置,查找到末尾时排序完成。

<?php

function selectSort(&$arr, $n)
{
    for ($i = 0; $i < $n; $i++) {
        $k = $i;        // 最小值下标
        for ($j = $i + 1; $j < $n; $j++) {
            if ($arr[$k] > $arr[$j]) {
                $k = $j;
            }
        }
        if ($k != $i) {
            $temp = $arr[$i];
            $arr[$i] = $arr[$k];
            $arr[$k] = $temp;
        }
    }
}

$num_arr = [6,4,2,1,3,8,9];
selectSort($num_arr, 7);
echo '<pre>';
var_dump($num_arr);

    二元选择排序

    二元选择排序是对选择排序的优化,在选择排序中我们找到了剩余数值的最小值并确定了他的位置,其实我们可以做得更多,因为最大值及它的位置也是很容易就可以确定的,它的位置是最小值对应的相反的位置,最大最小值是可以同时找到的,这样我们就可以节省一半的时间,所以在外层 for 语句中只遍历到数组的一半整个数组的排序就已经完成了。

<?php

function selectSort(&$arr, $n)
{
    for ($i = 0; $i <= $n / 2; $i++) {
        $min = $i;
        $max = $i;
        for ($j = $i + 1; $j < $n - $i; $j++) {
            if ($arr[$min] > $arr[$j]) {
                $min = $j;
            }
            if ($arr[$max] < $arr[$j]) {
                $max = $j;
            }
        }
        $tmp = $arr[$i];
        $arr[$i] = $arr[$min];
        $arr[$min] = $tmp;
        $tmp = $arr[$n - $i - 1];
        $arr[$n - $i - 1] = $arr[$max];
        $arr[$max] = $tmp;
    }
}

$num_arr = [6,4,2,1,3,8,9];
selectSort($num_arr, 7);
echo '<pre>';
var_dump($num_arr);

6. 堆排序

大顶堆:父节点的值都大于子节点的值

小顶堆:父节点的值都小于子节点的值

以从小到大排序,通过构建大顶堆来获取数组中未确定位置的数值的最大值,由于大顶堆满足父节点的值都是大于子节点的,所以堆顶的节点的值肯定是最大的,将其取出来放入数组末尾处,这是他所处的位置即为它的最终位置,这时就不将该值算在堆内了,剩余的数已经不一定满足大顶堆的性质了,继续按照上述方法将剩余数值整理成大顶堆,知道整个数组排序完毕。

大顶堆额整理方法:构建大顶堆时,从堆的底层开始对比三个父子节点中最大的放到父节点,然后向上依次遍历父节点,并以该父节点为根节点构建大顶堆,确定本来在该节点的值应该在的层级。

<?php
function heapify(&$arr, $i, $n)
{
    $child = 2 * $i + 1;        // 左节点

    while($child < $n) {
        if ($child + 1 < $n && $arr[$child] < $arr[$child + 1]) {       // 如果右节点大于左节点,交换的节点变为右节点
            ++$child;
        }
        if ($arr[$i] < $arr[$child]) {
            swap($arr, $i, $child);
            $i = $child;
            $child = 2 * $i + 1;
        } else {
            break;        // 一旦满足条件后面一定都满足条件了,因为下面的堆已经经历过排序了,已经满足条件了
        }
    }
}

function buildHeap(&$arr, $n)
{
    // 最后一个有孩子的节点的位置    intval(($n - 1) / 2)
    for ($i = intval(($n - 1) / 2); $i >= 0; $i--) {
        heapify($arr, $i, $n);
    }
}

function heapSort (&$arr)
{
    $n = count($arr);
    buildHeap($arr, $n);
    // 从最后一个元素开始对序列进行调整
    for ($i = $n - 1; $i > 0; $i--) {
        swap($arr, 0,$i);
        heapify($arr, 0, $i);
    }
}
// 交换数值
function swap(&$arr, $i, $j)
{
    $tmp = $arr[$i];
    $arr[$i] = $arr[$j];
    $arr[$j] = $tmp;
}

$num_arr = [6,4,2,1,3,8,9];
heapSort($num_arr);
echo '<pre>';
var_dump($num_arr);

7. 归并排序

要归并的话,当然就先要分,把需要排序的数组分成一个个子数组,如何分呢?就像折掉一根筷子,一分二,二分四,四分八。。。。最后粒度会细到每一个数组只包含一个数值,这时我们给被分成的这两数组进行排序,就两个数很好排序吧,然后向上倒回去,上个被分成的两个子数组,进行对比,然后排好序,以此类推,最后整个数组排序完成(包含多个数值的两个数组如何排好序,具体实现请看代码 mergeArray 方法)。

<?php
function mergeArray(&$arr, $first, $mid, $last) {
    $i = $first;        // 子数组 1 的开始下标
    $j = $mid + 1;      // 子数组 2 的开始下标
    $m = $mid;          // 子数组 1 的截止下标
    $n = $last;         // 子数组 2 的截止下标
    $k = 0;

    // 对比两个子数组,将较小的值推入新数组,知道至少一个数组已经全部推入新数组
    while ($i <= $m && $j <= $n)
    {
        if ($arr[$i] < $arr[$j])
            $tmp[$k++] = $arr[$i++];
        else
            $tmp[$k++] = $arr[$j++];
    }

    // 将另一个未全部推入的数组推入新数组,如果两条数组的值大于 1, 那么它肯定经历过排序操作,所以接下来的操作不需要考虑大小对比
    while ($i <= $m)
        $tmp[$k++] = $arr[$i++];
    while ($j <= $n)
        $tmp[$k++] = $arr[$j++];
    // 将旧数组的对应位置替换成已经排好顺序的数值
    for ($i = 0; $i < $k; $i++){
        $arr[$first + $i] = $tmp[$i];
    }
}

function merge(&$arr, $first, $last)
{
    if ($first < $last) {
        $mid = intval(($first + $last) / 2);
        merge($arr, $first, $mid);
        merge($arr, $mid + 1, $last);
        mergeArray($arr, $first, $mid, $last);
    }
}

function mergeSort(&$arr)
{
    $n = count($arr);
    merge($arr, 0, $n - 1);
}

$num_arr = [6,4,2,1,3,8,9];
echo '<pre>';
print_r($num_arr);
mergeSort($num_arr);
print_r($num_arr);

8. 基数排序

获取整个数组的大数值的长度,将其他所有数值都格式化成这个长度,然后先以个位为准将整个数组进行排序,将其放入以 0 - 9为关键值的二维数组中,然后遍历这个数组,依次读取数值到新二维数组中,这样只看个位的话这个这个数组是按照大小依次排序的,接着以十位为准,在个位已经拍好的序列的基础上,十位同样按照大小进行排序,在十位相同的情况下,不要破坏个位已经拍好的顺序,以此类推,直到最高位排序完成。

<?php

function radixSort(&$arr)
{
    $n = count($arr);
    // 获取数组中数值中的最长长度
    $maxLength = 0;
    foreach ($arr as $v) {
        $len = strlen($v);
        if ($len > $maxLength) {
            $maxLength = $len;
        }
    }

    for ($pos = $maxLength; $pos > 0; $pos--) {
        // 入桶
        $bucket = [];
        $result = [];
        for ($i = 0; $i <= 9; $i++) {
            foreach ($arr as $v) {
                // 将数值不足的位数补0,使其长度等于最长长度
                $v_f = sprintf('%0'. $maxLength . 'd', $v);
                $num = substr($v_f,$pos - 1, 1);
                if ($num == $i) {
                    if (empty($bucket[$i])) $bucket[$i] = [];
                    array_push($bucket[$i], $v);
                }
            }
        }
        // 出桶
        for ($i = 0; $i <= 9; $i++) {
            if (!empty($bucket[$i])) {
                $l = count($bucket[$i]);
                for ($j = 0; $j < $l; $j++) {
                    $result[] = $bucket[$i][$j];
                }
            }
        }
        $arr = $result;
    }
}

$num_arr = [6,46,210,1,3,8,9];
echo '<pre>';
print_r($num_arr);
radixSort($num_arr);
print_r($num_arr);


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值