使用PHP实现堆排序

使用PHP实现堆排序
堆积排序(Heapsort)是指利用堆积树(堆)这种资料结构所设计的一种排序算法,可以利用数组的特点快速定位指定索引的元素。

“堆”定义
  n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质(简称为堆性质):
  (1) ki≤K2i且ki≤K2i+1 或(2)Ki≥K2i且ki≥K2i+1(1≤i≤ n) //ki相当于二叉树的非叶结点,K2i则是左孩子,k2i+1是右孩子
  若将此序列所存储的向量R[1..n]看做是一棵完全二叉树的存储结构,则堆实质上是满足如下性质的完全二叉树:
  树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

“筛选法”调整堆
  R[low]的左、右子树(若存在)均已是堆,这两棵子树的根R[2low]和R[2low+1]分别是各自子树中关键字最大的结点。若R[low].key不小于这两个孩子结点的关键字,则R[low]未违反堆[性质,以R[low]为根的树已是堆,无须调整;否则必须将R[low]和它的两个孩子结点中关键字较大者进行交换,即R[low]与R[large](R[large].key=max(R[2low].key,R[2low+1].key))交换。交换后又可能使结点R[large]违反堆性质,同样由于该结点的两棵子树(若存在)仍然是堆,故可重复上述的调整过程,对以R[large]为根的树进行调整。此过程直至当前被调整的结点已满足性质,或者该结点已是叶子为止。上述过程就象过筛子一样,把较小的关键字逐层筛下去,而将较大的关键字逐层选上来。因此,有人将此方法称为”筛选法”。
以上来自百度百科


以下为PHP的堆排序实现。
siftup函数是在x[1..n-1]为堆,在x[n]中放置一个任意的元素时,重新获得堆的操作。
它尽可能的将新元素向上筛选,向上筛选是通过交换该结点与其父结点来实现的。

siftdown函数是在x[1..n]是一个堆,在x[1]中放置一个任意的元素时,重新获得堆的操作。
它是一个向下筛选的过程,将顺序不对的元素和比它小的子结点交换。

通过siftup函数,加入n个元素,构造堆
通过siftdown函数,每次取堆顶端的数,然后重新构造堆,如此迭代,直到所有的数据都取出,因为堆顶一定是这个堆中最小的值,所以最后一定可以得到一个有序的序列。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<?php
/**
 * 堆排序
 * @author phppan.p#gmail.com  http://www.phppan.com
 * 哥学社成员(http://www.blog-brother.com/)
 * @package test
 */
 
/**
 * 向上筛选元素
 * 将元素$n添加到堆中,调整构建新的堆
 */
function siftup(&$seq, $n) {
	$i = $n;
 
	while($i > 1) {
		$p = floor($i / 2);
		if($seq[$p] <= $seq[$i]) {
			break;
		}
		list($seq[$p], $seq[$i]) = array($seq[$i], $seq[$p]);
 
		$i = $p;
	}
}
 
/**
 * 向下筛选元素
 */
function siftdown(&$seq, $n) {
	$i = 1;
 
	while(1) {
		$c = $i * 2;
 
		if($c > $n) {
			break;
		}
 
		/* $c 为左结点 $c + 1 为右结点*/
		if($c + 1 <= $n) {
			if($seq[$c + 1] < $seq[$c]) {
				$c++;
			}
		}
 
		if($seq[$i] <= $seq[$c]) {
			break;
		}
 
		/* 将$seq[$i]和它的两个孩子结点中关键字较大者进行交换 */
		list($seq[$c], $seq[$i]) = array($seq[$i], $seq[$c]);
 
		$i = $c;
	}
}
 
 
 
/**
 * 堆排序
 * @param	array	$seq	待排序的序列
 */
function heapSort(&$seq) {
	$n = count($seq);
 
	for($i = 2; $i <= $n; $i++) {
		siftup($seq, $i);
	}
 
	for($i = $n; $i >= 2; $i--) {
		list($seq[1], $seq[$i]) = array($seq[$i], $seq[1]);
		siftdown($seq, $i - 1);
	}
}
 
/* 测试 */
$seq = array(1 => 9, 7, 2, 3, 1, 6);
heapSort($seq);
print_r($seq);
 
die();

heapSort使用了n-1次siftup和n-1次siftdown操作,其时间复杂度为O(nlogn)

使用PHP实现堆排序》上有3条评论

  1. spam

    在起始索引为 0 的“堆”中:
    1) 堆的根节点将存放在位置 0
    2) 节点 i 的左子节点在位置 2 * i + 1
    3) 节点 i 的右子节点在位置 2 * i + 2
    4) 节点 i 的父节点在位置 floor( (i – 1) / 2 ) : 注 floor 表示“取整”操作

    在起始索引为 1 的“堆”中:
    1) 堆的根节点将存放在位置 1
    2) 节点 i 的左子节点在位置 2 * i
    3) 节点 i 的右子节点在位置 2 * i + 1
    4) 节点 i 的父节点在位置 floor( i / 2 ) : 注 floor 表示“取整”操作

    这样描述会会看起来简单清楚一些。虽然您对 heap 的描述非常专业,但是过于专业的结果就是难于理解。让我想起前几天看到的关于中国数学教育的贴子……

    回复
  2. Pingback引用通告: Thinking In LAMP Blog » Blog Archive » PHP每月通讯(2010年12月)

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>