php实现堆排序

先上堆排序代码:

<?php

$arr = [4,1,3,2,16,9,10,1,14,9,8,7,];//注意有重复值。
$arr = heap_sort($arr);
var_dump($arr);
//排序结果如下:
// [1,1,2,3,4,7,8,9,9,10,14,16,]


/**
* 从小到大 的排序。
*
* 1、初始化最大堆,把一维数组改变成映射为最大堆的一维数组。
* 2、把堆顶的最大值和堆最后一个结点交换。(于是最大值出现,并放好位置了。)
* 3、排除最后一个结点,把堆重新调整为最大堆。
* 4、把堆顶和倒数第2个结点交换。(于是次大值出现,也放好位置了。)
* 5、排除最后两个结点,把堆调整为最大推。
* 6、重复2和3,直到最后一个结点即堆顶。堆顶不需排,因为必然最小。
*
* @param array $arr
* @return array
*/
function heap_sort($arr) {
$len = count($arr);
//初始化,i從最後一個父節點開始調整
// 初始化的目的是建立一个最大堆。
// 要点是,必须自底向上,而不能自顶向下,否则bug。
for ($index = ceil($len/2) - 1; $index >= 0; $index--) {
max_heapify($arr, $index, $len);
}

// 推排序是很有规律的,先找出最大值,放到堆的最后,然后假装堆的结点数减少,
// 继续排序,找出次大的,等等。
// 巧妙利用堆和数组的映射,实现排序。
for ($index = $len - 1; $index > 0; $index--) {
$arr = swap ( $arr, 0, $index );
max_heapify($arr, 0, $index);
}
return $arr;
}

/**
* 堆排序核心算法,
* 对于一个给定的堆,给定的起始下标和结束下标,进行堆排序,会递归调用。
* 事实上,只排序起始下标指向的父节点,结束下标仅仅用于递归判断的返回。
* 这是指针形式的调用,这样的话,代码速度快,且代码容易理解。
*
* @param array $arr
* @param int $start_index
* @param int $end_index
*/
function max_heapify(&$arr, $start_index, $end_index) {
//建立父節點指標和子節點指標
$dad_index = $start_index;
$son_index = $dad_index * 2 + 1;
//若子節點指標超過範圍直接跳出函數
if ($son_index >= $end_index) {
return;
}
//先比較兩個子節點大小,選擇最大的
if ($son_index + 1 < $end_index && $arr[$son_index] < $arr[$son_index + 1]) {
$son_index++;
}
//如果父節點小於子節點時,交換父子內容再繼續子節點和孫節點比較
if ($arr[$dad_index] < $arr[$son_index]) {
$arr = swap($arr, $dad_index, $son_index);
max_heapify($arr, $son_index, $end_index);
}
}

/**
* 交换一个数组中两个不同下标的值。
* @param array $arr
* @param int $index1
* @param int $index2
* @return array
*/
function swap($arr, $index1, $index2) {
list( $arr[$index1], $arr[$index2] ) = [ $arr[$index2], $arr[$index1] ];
return $arr;
}


其实可以看到,堆排序代码并不算多,关键在于理解。

本文有参考:http://bubkoo.com/2014/01/14/sort-algorithm/heap-sort/
作者不知是谁,描述的很清楚。

[list]
[*]堆是一棵二叉数。
[*]堆是一棵完全二叉数,于是,堆可以轻松用数组表达
[*]假设下标是i,则父节点和子节点都有下标计算公式。
[*]堆排序算法是先初始化成最大堆,然后交换堆顶和堆最后结点,然后结点数减1,再依次操作,就排序好了。
[/list]

下图仅供参考,注意开始已经初始化过最大堆了。

[img]http://dl2.iteye.com/upload/attachment/0130/7979/f6ed3596-a460-3f0e-b0bb-f59ecd323bee.png[/img]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值