几个排序

原文地址:https://github.com/PuShaoWei/arithmetic-php/tree/master/package/Sort


一、冒泡排序

<?php
/**
 * -------------------------------------------------------------
 * 思路分析:就是像冒泡一样,每次从数组当中 冒一个最大的数出来。 
 * -------------------------------------------------------------
 * 你可以这样理解:(从小到大排序)存在10个不同大小的气泡,
 * 由底至上的把较少的气泡逐步地向上升,这样经过遍历一次后最小的气泡就会被上升到顶(下标为0)
 * 然后再从底至上地这样升,循环直至十个气泡大小有序。
 * 在冒泡排序中,最重要的思想是两两比较,将两者较少的升上去
 *
 * @param  array $container
 * @return array
 */
function BubbleSort(array $container)
{
    $count = count($container);
    for ($j = 1; $j < $count; $j++) {
        for ($i = 0; $i < $count - $j; $i++) {
            if ($container[$i] > $container[$i + 1]) {
                $temp = $container[$i];
                $container[$i] = $container[$i + 1];
                $container[$i + 1] = $temp;
            }
        }
    }
    return $container;
}

var_dump(BubbleSort([4, 21, 41, 2, 53, 1, 213, 31, 21, 423]));

/*
array(10) {
  [0] =>
  int(1)
  [1] =>
  int(2)
  [2] =>
  int(4)
  [3] =>
  int(21)
  [4] =>
  int(21)
  [5] =>
  int(41)
  [6] =>
  int(41)
  [7] =>
  int(53)
  [8] =>
  int(213)
  [9] =>
  int(423)
}
 */

// +----------------------------------------------------------------------
// |                        方法二
// +----------------------------------------------------------------------
function BubbleSortV2(array $container)
{
    $len = count($container);
    // 也可以用foreach
    for ($i = 0; $i < $len - 1; $i++) {
        for ($j = $i + 1; $j < $len; $j++) {
            if ($container[$i] > $container[$j]) {
                list($container[$i], $container[$j]) = array($container[$j], $container[$i]);
            }
        }
    }

    return $container;
}

print_r(BubbleSort([4, 21, 41, 2, 53, 1, 213, 31, 21, 423]));
/*
Array
(
    [0] => 1
    [1] => 2
    [2] => 4
    [3] => 21
    [4] => 21
    [5] => 31
    [6] => 41
    [7] => 53
    [8] => 213
    [9] => 423
)
 */

二、堆排序

<?php

/**
 * -------------------------------------------------------------
 * 思路分析:堆排序利用了大根堆堆顶记录的关键字最大(或最小)这一特征
 * -------------------------------------------------------------
 *  堆排序(HeapSort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,它是选择排序的一种。
 *  可以利用数组的特点快速定位指定索引的元素。堆分为大根堆和小根堆,是完全二叉树。
 *  大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,
 *  因为根据大根堆的要求可知,最大的值一定在堆顶。
 */
class HeapSort
{
    /**
     * @var int
     */
    protected $count;

    /**
     * @var array
     */
    protected $data;

    /**
     * HeapSort constructor.
     *
     * @param array $data
     */
    public function __construct(array $data)
    {
        $this->count = count($data);
        $this->data  = $data;
    }

    /**
     * Action
     *
     * @return array
     */
    public function run()
    {
        $this->createHeap();
        while ($this->count > 0) {
            /* 这是一个大顶堆 , 所以堆顶的节点必须是最大的
               根据此特点 , 每次都将堆顶数据移到最后一位
               然后对剩余数据节点再次建造堆就可以 */
            $this->swap($this->data[0], $this->data[--$this->count]);
            $this->buildHeap($this->data, 0, $this->count);
        }
        return $this->data;
    }

    /**
     * 创建一个堆
     */
    public function createHeap()
    {
        $i = ceil($this->count / 2) + 1;
        while ($i--) {
            $this->buildHeap($this->data, $i, $this->count);
        }
    }

    /**
     * 从 数组 的第 $i 个节点开始至 数组长度为0 结束 , 递归的将其 ( 包括其子节点 ) 转化为一个小顶堆
     *
     * @param $data
     * @param $i
     * @param $count
     */
    public function buildHeap(array &$data, $i, $count)
    {
        if (false === $i < $count) {
            return;
        }
        // 获取左 / 右节点
        $right = ($left = 2 * $i + 1) + 1;
        $max   = $i;
        // 如果左子节点大于当前节点 , 那么记录左节点键名
        if ($left < $count && $data[$i] < $data[$left]) {
            $max = $left;
        }
        // 如果右节点大于刚刚记录的 $max , 那么再次交换
        if ($right < $count && $data[$max] < $data[$right]) {
            $max = $right;
        }
        if ($max !== $i && $max < $count) {
            $this->swap($data[$i], $data[$max]);
            $this->buildHeap($data, $max, $count);
        }
    }

    /**
     * 交换空间
     *
     * @param $left
     * @param $right
     */
    public function swap(&$left, &$right)
    {
        list($left, $right) = array ($right, $left);
    }
}

$array  = array (4, 21, 41, 2, 53, 1, 213, 31, 21, 423, 56);
$result = (new HeapSort($array))->run();
var_dump($result);

三、插入排序

<?php

/**
 * -------------------------------------------------------------
 * 思路分析:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。
 * -------------------------------------------------------------
 *
 * 算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。
 * 插入算法把要排序的数组分成两部分:第一部分包含了这个数组的所有元素,
 * 但将最后一个元素除外(让数组多一个空间才有插入的位置),而第二部分就只包含这一个元素(即待插入元素)。
 * 在第一部分排序完成后,再将这个最后元素插入到已排好序的第一部分中。
 *
 * @param $container
 * @return mixed
 */

function InsertSort(array $container)
{
    $count = count($container);
    for ($i = 1; $i < $count; $i++){
        $temp = $container[$i];
        $j    = $i - 1;
        // Init
        while ($container[$j] > $temp){
            $container[$j+1] = $container[$j];
            $container[$j]   = $temp;
            $j--;
            if ($j < 0) break;
        }
    }
    return $container;
}
var_dump(InsertSort([3, 12, 42, 1, 24, 5, 346, 7]));

/*
array (size=8)
  0 => int 1
  1 => int 3
  2 => int 5
  3 => int 7
  4 => int 12
  5 => int 24
  6 => int 42
  7 => int 346
 */

四、归并排序

<?php

/**
 * -------------------------------------------------------------
 * 思路分析:
 * -------------------------------------------------------------
 * 比较a[i]和b[j]的大小,若a[i]≤b[j],则将第一个有序表中的元素a[i]复制到r[k]中,
 * 并令i和k分别加上1;否则将第二个有序表中的元素b[j]复制到r[k]中,并令j和k分别加上1,
 * 如此循环下去,直到其中一个有序表取完,然后再将另一个有序表中剩余的元素复制到r中从下标k到下标t的单元。
 * 归并排序的算法我们通常用递归实现,先把待排序区间[s,t]以中点二分,接着把左边子区间排序,再把右边子区间排序,
 * 最后把左区间和右区间用一次归并操作合并成有序的区间[s,t]
 */
class MergeSort
{
    /**
     * MergeSort constructor.
     * 是开始递归函数的一个驱动函数
     *
     * @param array $arr 待排序的数组
     */
    public function __construct(array $arr)
    {
        $this->mSort($arr, 0, count($arr) - 1);
        var_dump($arr);
    }

    /**
     * 实际实现归并排序的程序
     *
     * @param $arr     array   需要排序的数组
     * @param $left    int     子序列的左下标值
     * @param $right   int     子序列的右下标值
     */
    public function mSort(&$arr, $left, $right)
    {
        if ($left < $right) {
            //说明子序列内存在多余1个的元素,那么需要拆分,分别排序,合并
            //计算拆分的位置,长度/2 去整
            $center = floor(($left + $right) / 2);
            //递归调用对左边进行再次排序:
            $this->mSort($arr, $left, $center);
            //递归调用对右边进行再次排序
            $this->mSort($arr, $center + 1, $right);
            //合并排序结果
            $this->mergeArray($arr, $left, $center, $right);
        }
    }

    /**
     * 将两个有序数组合并成一个有序数组
     *
     * @param &$arr   , 待排序的所有元素
     * @param $left   , 排序子数组A的开始下标
     * @param $center , 排序子数组A与排序子数组B的中间下标,也就是数组A的结束下标
     * @param $right  , 排序子数组B的结束下标(开始为$center+1)
     */
    public function mergeArray(&$arr, $left, $center, $right)
    {
        echo '| ' . $left . ' - ' . $center . ' - ' . $right . ' - ' . implode(',', $arr) . PHP_EOL;
        //设置两个起始位置标记
        $a_i  = $left;
        $b_i  = $center + 1;
        $temp = [];

        while ($a_i <= $center && $b_i <= $right) {
            //当数组A和数组B都没有越界时
            if ($arr[ $a_i ] < $arr[ $b_i ]) {
                $temp[] = $arr[ $a_i++ ];
            } else {
                $temp[] = $arr[ $b_i++ ];
            }
        }
        //判断 数组A内的元素是否都用完了,没有的话将其全部插入到C数组内:
        while ($a_i <= $center) {
            $temp[] = $arr[ $a_i++ ];
        }
        //判断 数组B内的元素是否都用完了,没有的话将其全部插入到C数组内:
        while ($b_i <= $right) {
            $temp[] = $arr[ $b_i++ ];
        }

        //将$arrC内排序好的部分,写入到$arr内:
        for ($i = 0, $len = count($temp); $i < $len; $i++) {
            $arr[ $left + $i ] = $temp[ $i ];
        }
    }
}

//do some test:
new mergeSort([4, 7, 6, 3, 9, 5, 8]);

/*

| 0 - 0 - 1 - 4,7,6,3,9,5,8
| 2 - 2 - 3 - 4,7,6,3,9,5,8
| 0 - 1 - 3 - 4,7,3,6,9,5,8
| 4 - 4 - 5 - 3,4,6,7,9,5,8
| 4 - 5 - 6 - 3,4,6,7,5,9,8
| 0 - 3 - 6 - 3,4,6,7,5,8,9

array(7) {
  [0] =>
  int(3)
  [1] =>
  int(4)
  [2] =>
  int(5)
  [3] =>
  int(6)
  [4] =>
  int(7)
  [5] =>
  int(8)
  [6] =>
  int(9)
}
 */

五、快速排序

<?php
/**
 * -------------------------------------------------------------
 * 思路分析:从数列中挑出一个元素,称为 “基准”(pivot) 
 * -------------------------------------------------------------
 * 重新排序数列,所有元素比基准值小的摆放在基准前面
 * 所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。
 * 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序
 *
 * @param  array $container
 * @return mixed
 */
function QulickSort( array $container ){
    $count = count( $container );
    if( $count <= 1 ) {
        return $container;
    }
    $left = $right = [];
    for ($i = 1; $i < $count; $i++) {
        if ($container[$i] < $container[0]) {
            $left[]  = $container[$i];
        } else {
            $right[] = $container[$i];
        }
    }
    $left  = QulickSort($left);
    $right = QulickSort($right);
    return array_merge($left,[$container[0]],$right);
}

var_dump( QulickSort([4,21,41,2,53,1,213,31,21,423]) );

六、选择排序

<?php

/**
 * -------------------------------------------------------------
 * 思路分析:选择排序是不稳定的排序方法
 * -------------------------------------------------------------
 * 它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
 * 选择排序是不稳定的排序方法(比如序列[5, 5, 3]第一次就将第一个[5]与[3]交换,导致第一个5挪动到第二个5后面)。
 *
 * @param $container
 * @return mixed
 */
function SelectSort(array $container)
{
    $count = count($container);
    for ($i = 0; $i < $count; $i++){
        $k = $i;
        for ($j = $i + 1; $j < $count; $j++){
            if($container[$j] < $container[$k]){
                $k = $j;
            }
        }
        if($k != $i){
            $temp          = $container[$i];
            $container[$i] = $container[$k];
            $container[$k] = $temp;
        }
    }
    return $container;
}

var_dump(SelectSort([3, 12, 42, 1, 24, 5, 346, 7]));

/*
 array(8) {
  [0] =>
  int(1)
  [1] =>
  int(3)
  [2] =>
  int(5)
  [3] =>
  int(7)
  [4] =>
  int(12)
  [5] =>
  int(24)
  [6] =>
  int(42)
  [7] =>
  int(346)
}
 */

七、希尔排序

<?php
/**
 * -------------------------------------------------------------
 * 思路分析:希尔排序是基于插入排序的,区别在于插入排序是相邻的一个个比较(类似于希尔中h=1的情形),而希尔排序是距离h的比较和替换。
 * -------------------------------------------------------------
 * 希尔排序中一个常数因子n,原数组被分成各个小组,每个小组由h个元素组成,很可能会有多余的元素。
 * 当然每次循环的时候,h也是递减的(h=h/n)。第一次循环就是从下标为h开始。希尔排序的一个思想就是,分成小组去排序。
 * @param array $container
 * @return array
 */
function ShellSort(array $container)
{
    $count = count($container);
    for ($increment = intval($count / 2); $increment > 0; $increment = intval($increment / 2)) {
        for ($i = $increment; $i < $count; $i++) {
            $temp = $container[$i];
            for ($j = $i; $j >= $increment; $j -= $increment) {
                if ($temp < $container[$j - $increment]) {
                    $container[$j] = $container[$j - $increment];
                } else {
                    break;
                }
            }
            $container[$j] = $temp;
        }
    }
    return $container;
}

//var_dump(ShellSort([6, 13, 21, 99, 18, 2, 25, 33, 19, 84]));




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值