原文地址: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]));