使用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)
在起始索引为 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 的描述非常专业,但是过于专业的结果就是难于理解。让我想起前几天看到的关于中国数学教育的贴子……
抱歉,没看到“来自百度百科”……
Pingback引用通告: Thinking In LAMP Blog » Blog Archive » PHP每月通讯(2010年12月)