前言

各种排序算法不光是面试的常问知识点
也在各种语言底层经常使用,如何在不同的场景下选择最优的排序算法?本文总结各种常用的排序,试图进一步深入理解排序算法

注:下面的描述中n代表数组长度,代码会封装一些在排序算法中常用的比较,交换方法

定义的排序算法抽象类,排序算法需继承该抽象类实现抽象方法

<?php
namespace SortAlgorithm;

/**
 * 抽象排序类
 * Class SortAlg
 */
abstract class SortAlg
{
    /**
     * 抽象方法-排序的具体实现
     * @param $array
     * @return mixed
     */
    abstract static function sort($array);

    /**
     * 元素比较
     * @param $param1
     * @param $param2
     * @return bool
     */
    public static function compare($param1, $param2)
    {
        if ($param1 > $param2) {
            return true;
        }
        return false;
    }

    /**
     * 元素交换
     * @param $a
     * @param $i
     * @param $j
     */
    public static function exchange(&$a, $i, $j)
    {
        $temp  = $a[$i];
        $a[$i] = $a[$j];
        $a[$j] = $temp;
    }

    /**
     * 打印排序数组
     * @param $a
     */
    public static function show($a)
    {
        var_dump($a);
    }
}

选择排序

概念

step1:在数组中找到最小的元素
step2:最小的元素和第第一个元素交换
step3:在剩下的数组的中再找最小的元素和第二个元素交换
... 如此往复,直到数组有序

image.png

算法分析

  • 为了在剩余元素中找到最小的元素,需要都需要扫描剩余元素进行比较,比较次数=(n-1)+(n-2)...+1=n(n-1)/2 ~ n*n/2
  • 需要交换n-1次

代码实现

<?php

namespace SortAlgorithm;

/**
 * 插入排序
 * Class Select
 * @package SortAlgorithm
 */
class Select extends SortAlg
{

    /**
     * 核心思想
     * 1.找到最小元素和第一个元素交换
     * 2.找到第二小的元素和第二个元素交换,以此类推。。。
     * 需交换n-1 次
     * 需比较(n-1)+(n-2)...+2+1=n(n-1)/2 ~ n*n/2 次
     * @param $array
     * @return mixed
     */
    public static function sort($array)
    {
        $count = count($array);
        for ($i = 0; $i < $count; $i++) {
            $min = $i;
            for ($j = $i; $j < $count; $j++) {
                if (self::compare($array[$i], $array[$j])) {
                    $min = $j;
                }
            }
            self::exchange($array, $i, $min);
        }
        return $array;
    }
}

插入排序

概念

比如将8插入到有序数组[1,3,5,7,9]中,8先和9进行比较8<9则继续和7比较,8>7结束比较,将8插入到7后面
image.png

算法分析

  • 插入排序只会将新元素和之前有序的数组进行比较,若新元素>最后一个元素会之间结束比较,选择排序每次都要遍历剩余数组找到最小元素,所以一般来说插入排序效率要高于选择排序
  • 对于一个本来就部分有序的数组,会减少插入排序比较次数,提供插入排序效率

代码实现

<?php


namespace SortAlgorithm;

/**
 * 插入排序
 * Class Insert
 * @package SortAlgorithm
 */
class Insert extends SortAlg
{

    /**
     * 核销思想
     * 假设数组是有序的,新增的元素和有序的数组进行比较
     * 比较次数:n*n/4
     * 移动次数:n*n/4
     * 对于一个部分的有序的数组,使用插入排序效率很高,移动次数 < n次
     * @param $array
     * @return mixed
     */
    public static function sort($array)
    {
        $count = count($array);
        for ($i = 1; $i < $count; $i++) {
            for ($j = $i; $j > 0; $j--) {
                if (self::compare($array[$j - 1], $array[$j])) {
                    self::exchange($array, $j - 1, $j);
                }
            }
        }
        return $array;
    }
}

希尔排序

概念

对于大规模乱序数组插入排序很慢,因为它只会交换相邻元素,因此元素需要从数组的一端一点点的移动到另一端,如果最小的元素的在数组的尽头,把它移动到正确的位置就需要n-1次移动
希尔排序交换不相邻的元素对数组进行局部排序,提高了插入排序效率

算法分析

  • 希尔排序使任意间隔h的元素就使有序的,这样的数组也被成为h有序数组,如果h很大的话,就可以把元素移动到很远的地方,节省了移动次数
  • 如果选择h?算法的性能不仅取决h,还取决h之间的数组性质,这里不再深究
  • 希尔排序比快速排序快很多,数组越大优势越大

image.png

代码实现

<?php


namespace SortAlgorithm;

/**
 * 希尔排序
 * Class Shell
 * @package SortAlgorithm
 */
class Shell extends SortAlg
{

    /**
     * 插入排序的优化方案
     * 1.将数组拆成m个子数组,将子数组进行插入排序,实现数组的部分有序
     * @param $array
     * @return mixed
     */
    public static function sort($array)
    {
        $count = count($array);

        $h = 1;
        // 间隔h的选择
        while ($h < $count / 3) {
            $h = $h * 3 + 1;
        }
        while ($h >= 1) {
            for ($i = $h; $i < $count; $i++) {
                for ($j = $i; $j >= $h && self::compare($array[$j - $h], $array[$j]); $j -= $h) {
                    self::exchange($array, $j, $j - $h);
                }
            }
            $h = $h / 3;
        }

        return $array;
    }
}

归并排序

概念

将一个数组进行排序,可以将它分成2个数组,分别进行排序,然后将2个数组结果归并起来,这也是一种分治思想

image.png

算法分析

  • 自顶向下的归并排序:将大数组分割成小数组,小数组一层层merge到大数组
  • 自底向上的归并排序:先归并完最后一层的小数组,再归并倒数第二层的小数组。。。

代码实现

<?php


namespace SortAlgorithm;

/**
 * 归并算法-自顶向下
 * Class MergeToDown
 * @package SortAlgorithm
 */
class MergeToDown extends SortAlg
{

    /**
     * @param $array
     * @return mixed
     */
    public static function sort($array)
    {
        self::sortLoop($array, 0, count($array)-1);
        return $array;
    }

    /**
     * @param $a
     * @param $min
     * @param $max
     */
    public static function sortLoop(&$a, $min, $max)
    {
        if ($min >= $max) return;
        $mid = floor(($max + $min) / 2);
        self::sortLoop($a, $min, $mid);
        self::sortLoop($a, $mid + 1, $max);
        self::merge($a, $min, $mid, $max);//归并
    }

    /**
     * 将两个数组归并
     * @param $a
     * @param $min
     * @param $mid
     * @param $max
     */
    public static function merge(&$a, $min, $mid, $max)
    {
        // 将[$min,$mid],[$mid+1,$max]归并
        $i = $min;
        $j = $mid + 1;

        // 临时数组
        $temp = $a;
        for ($k = $min; $k <= $max; $k++) {
            if ($i > $mid) $a[$k] = $temp[$j++]; // 左边用尽,取右边数据
            elseif ($j > $max) $a[$k] = $temp[$i++];//右边用尽,取左边数据
            elseif (self::compare($temp[$i], $temp[$j])) $a[$k] = $temp[$j++];//左边元素数据大于右边元素数据,取右边
            else $a[$k] = $temp[$i++];
        }
    }

}

快速排序

概念

快速排序同样用了分治思想,将数组切分成子数组,两部分独立排序
基准元素如何选择,简单的可以将数组第一个元素选为基准元素
但如何遇到遇到一个已经排序好的逆序数组[5,4,3,1],选择第一个元素,将会导致每次排序不会将数组分成一半一半,每次排序只能将确定基准元素位置,复杂度变成o(n*n)
解决方法:随机选择一个基准元素

image.png

算法分析

快速排序的优化方案

  1. 由于快递排序中小数组也会递归进行排序,所以对于小数组来说,插入排序更快,修改代码if ($min >= $max) return;if ($min + M >= $max) {Insert.sort($a,$min,$max);return;},转换参数M和最佳值和系统相关,但是5-15之间的任意值大多数情况下都令人满意。
  2. 熵(shang)最优的排序,一个元素全部重复的子数组就不需要排序了,而快速排序依然会将它切分成子数组进行排序。当数组中有大量重复元素的数据,导致效率降低。简单的方案是将数组切分成三部分,分别对于>、 =、 <切分元素的数组元素,将=切分元素的元素归位。

代码实现

<?php


namespace SortAlgorithm;

/**
 * 快速排序
 * Class Quick
 * @package SortAlgorithm
 */
class Quick extends SortAlg
{

    /**
     * @param $array
     * @return mixed
     */
    public static function sort($array)
    {
        shuffle($array);//打乱数组,消除对输入的影响
        self::sortLoop($array, 0, count($array) - 1);
        return $array;
    }

    /**
     * @param $a
     * @param $min
     * @param $max
     */
    public static function sortLoop(&$a, $min, $max)
    {
        if ($min >= $max) return;
        $mid = self::getMid($a, $min, $max);
        self::sortLoop($a, $min, $mid - 1);
        self::sortLoop($a, $mid + 1, $max);
    }

    /**
     * 指针交换法
     * @param $a
     * @param $min
     * @param $max
     * @return int
     */
    public static function getMid(&$a, $min, $max)
    {
        $i = $min;
        $j = $max;
        $v = $a[$min];
        while ($i != $j) {
            while ($i < $j && $a[$j] > $v) {
                $j--;
            }
            while ($i < $j && $a[$i] <= $v) {
                $i++;
            }
            if ($i < $j) {
                self::exchange($a, $i, $j);
            }
        }
        self::exchange($a, $min, $j);
        return $j;
    }

}
<?php


namespace SortAlgorithm;

/**
 * 快速排序优化之三向切分
 * Class Quick
 * @package SortAlgorithm
 */
class Quick3Way extends SortAlg
{

    /**
     * 三向切分的快速排序
     * @param $array
     * @return mixed
     */
    public static function sort($array)
    {
        shuffle($array);//打乱数组,消除对输入的影响
        self::sortLoop($array, 0, count($array) - 1);
        return $array;
    }

    /**
     * @param $a
     * @param $min
     * @param $max
     */
    public static function sortLoop(&$a, $min, $max)
    {
        if ($min >= $max) return;
        $lt = $min;
        $gt = $max;
        $i  = $min + 1;
        $v  = $a[$min];
        while ($i <= $gt) {
            $res = $a[$i] - $v;
            if ($res > 0) {
                // 若 $i元素 > 基准元素 则将$i和最后元素交换
                self::exchange($a, $i, $gt--);
            } elseif ($res < 0) {
                // 若 $i元素 < 基准元素 $i和最左元素交换位置
                self::exchange($a, $i++, $lt++);
            } else {
                $i++;
            }
        }
        self::sortLoop($a, $min, $i - 1);
        self::sortLoop($a, $gt + 1, $max);
    }

}

堆排序

概念

算法分析

代码实现

总结

参考:图灵设计层数,算法(第四版)


小小的太阳
123 声望7 粉丝

reloading...


« 上一篇
protobuf 学习
下一篇 »
使用gdb调试php