堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(1)用大根堆排序的基本思想
① 先将初始文件R[1..n]建成一个大根堆,此堆为初始的无序区
② 再将关键字最大的记录R[1](即堆顶)和无序区的最后一个记录R[n]交换,由此得到新的无序区R[1..n-1]和有序区R[n],且满足R[1..n-1].keys≤R[n].key
③由于交换后新的根R[1]可能违反堆性质,故应将当前无序区R[1..n-1]调整为堆。然后再次将R[1..n-1]中关键字最大的记录R[1]和该区间的最后一个记录R[n-1]交换,由此得到新的无序区R[1..n-2]和有序区R[n-1..n],且仍满足关系R[1..n-2].keys≤R[n-1..n].keys,同样要将R[1..n-2]调整为堆。
……
直到无序区只有一个元素为止。
(2)大根堆排序算法的基本操作:
①建堆,建堆是不断调整堆的过程,从len/2处开始调整,一直到第一个节点,此处len是堆中元素的个数。建堆的过程是线性的过程,从len/2到0处一直调用调整堆的过程,相当于o(h1)+o(h2)…+o(hlen/2) 其中h表示节点的深度,len/2表示节点的个数,这是一个求和的过程,结果是线性的O(n)。
②调整堆:调整堆在构建堆的过程中会用到,而且在堆排序过程中也会用到。利用的思想是比较节点i和它的孩子节点left(i),right(i),选出三者最大(或者最小)者,如果最大(小)值不是节点i而是它的一个孩子节点,那边交互节点i和该节点,然后再调用调整堆过程,这是一个递归的过程。调整堆的过程时间复杂度与堆的深度有关系,是lgn的操作,因为是沿着深度方向进行调整的。
③堆排序:堆排序是利用上面的两个过程来进行的。首先是根据元素构建堆。然后将堆的根节点取出(一般是与最后一个节点进行交换),将前面len-1个节点继续进行堆调整的过程,然后再将根节点取出,这样一直到所有节点都取出。堆排序过程的时间复杂度是O(nlgn)。因为建堆的时间复杂度是O(n)(调用一次);调整堆的时间复杂度是lgn,调用了n-1次,所以堆排序的时间复杂度是O(nlgn)
php代码实现如下:
<?php
class heap_sort
{
private $arr;
function __construct(&$arr)
{
if(!is_array($arr))
{
return FALSE;
}
$this->arr = $arr;
$this->heapSort();
$this->output();
}
private function buildMaxHeap()
{
$length = count($this->arr);
for($i=floor($length/2)-1; $i>=0; $i--)
{
$this->heapAdjust($i, $length);
}
}
private function swap(&$i, &$j)
{
$tmp = $i;
$i = $j;
$j = $tmp;
}
private function heapSort()
{
$length = count($this->arr);
$this->buildMaxHeap();
for($i=$length-1; $i>0; $i--)
{
$this->swap($this->arr[0], $this->arr[$i]);
$this->heapAdjust(0, $i);
}
return $this->arr;
}
private function heapAdjust($i, $j)
{
$largest = $i;
$left = 2*$i+1;
$right = 2*$i+2;
if($left<$j && $this->arr[$largest]<$this->arr[$left])
{
$largest = $left;
}
if($right<$j && $this->arr[$largest]<$this->arr[$right])
{
$largest=$right;
}
if($largest!=$i)
{
$this->swap($this->arr[$i], $this->arr[$largest]);
$this->heapAdjust($largest, $j);
}
}
private function output()
{
$arr = $this->arr;
echo iconv('utf-8', 'gbk', "整数序列经选择排序后的结果如下:\n");
$str = '';
foreach($arr as $number)
{
$str .= $number.', ';
}
echo rtrim($str, ', ')."\n\n";
}
}
function read()
{
$input = trim(fgets(STDIN));
return $input;
}
function test()
{
$str = '49, 38, 65, 97, 76, 13, 27, 49, 55, 04';
$arr = explode(', ', $str);
new heap_sort($arr);
}
function main()
{
$flag = TRUE;
while($flag)
{
echo iconv('utf-8', 'gbk', "请输入整数序列,以英文半角逗号和空格分隔,例如49, 38, 65(退出请输入exit或quit)\n");
$str = read();
if($str == 'exit' || $str == 'quit')
{
echo 'Bye';
break;
}
$arr = explode(', ', $str);
$validity = TRUE;
foreach($arr as $number)
{
if(!is_numeric($number))
{
echo iconv('utf-8', 'gbk', "数字序列输入有误,请重新输入\n");
$validity = FALSE;
break;
}
}
if(!$validity)
{
continue;
}
new heap_sort($arr);
}
}
if(!empty($argv[1]) && $argv[1]=='test')
{
test();
}
else
{
main();
}
转载于:https://blog.51cto.com/hehe1987/1640875