排序算法之堆排序

堆排序是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或大于)它的父结点。

思路:

1、将待排序序列构造成一个大顶堆

2、此时,整个序列的最大值就是堆顶的根节点

3、将其与末尾元素进行交换,此时末尾就为最大值

4、然后将剩余n-1个元素重新构造成一个堆,这样就会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了

注:每个结点的值都大于或等于其左右子结点的值,称为大顶堆

每个结点的值都小于或等于其左右子结点的值,称为小顶堆

小顶堆(降序)

<?php
//因为是数组,下标从0开始,所以,下标为n根结点的左子结点为2n+1,右子结点为2n+2;  
//初始化值,建立初始堆
$arr=array(49,38,65,97,76,13,27,50);
$arrSize=count($arr);

//将第一次排序抽出来,因为最后一次排序不需要再交换值了。
buildHeap($arr,$arrSize);

for($i=$arrSize-1;$i>0;$i--){
    swap($arr,$i,0);
    $arrSize--;
    buildHeap($arr,$arrSize);   
}
print_r($arr);

//用数组建立最小堆
function buildHeap(&$arr,$arrSize){
    //计算出最开始的下标$index,比较每一个子树的父结点和子结点,将最小值存入父结点中
    //从$index处对一个树进行循环比较,形成最小堆
    for($index=intval($arrSize/2)-1; $index>=0; $index--){
        //如果有左节点,将其下标存进最小值$min
        if($index*2+1<$arrSize){
            $min=$index*2+1;
            //如果有右子结点,比较左右结点的大小,如果右子结点更小,将其结点的下标记录进最小值$min
            if($index*2+2<$arrSize){
                if($arr[$index*2+2]<$arr[$min]){
                    $min=$index*2+2;
                }
            }
            //将子结点中较小的和父结点比较,若子结点较小,与父结点交换位置,同时更新较小
            if($arr[$min]<$arr[$index]){
                swap($arr,$min,$index);
            }   
        }
    }
}

//此函数用来交换下数组$arr中下标为$one和$another的数据
function swap(&$arr,$one,$another){
    $tmp=$arr[$one];
    $arr[$one]=$arr[$another];
    $arr[$another]=$tmp;
}
?>

大顶堆(升序)

<?php 
    /**
     * 将数组初始化构建堆
     * 从最后一个非叶子节点开始,递归到第0个, index = n/2 -1
     * 
     */
    function buildHeap(&$arr)
    {
        
        $noLeafNodeIndex = floor(count($arr)/2) - 1;
        for ($i = $noLeafNodeIndex; $i>=0; $i--) {
            adjustHeap($arr,$i,count($arr));
        }
    }
    
    /**
     * 交换头尾,在构建堆
     * 
     */
    function sortHeap(&$arr)
    {
        $k = 1;
        for ($i = count($arr) - 1; $i > 0 ; $i--) {
            //每次构建好堆后,交换头尾
            $temp = $arr[$i];
            $arr[$i] = $arr[0];
            $arr[0] = $temp;
            if ($k == 3) {
                adjustHeap1($arr,0,$i);
            }
            //交换头尾后,按照堆规则,重新构建堆
            adjustHeap($arr,0,$i);
            $k++;
        }
        
    }
    
    /**
     * 按照堆规则,重新构建堆
     * 
     * @param int $parentIndex
     * @param int $length
     */
    function adjustHeap(&$arr,$parentIndex,$length)
    {
        $childIndex = 2*$parentIndex + 1;
        while ($childIndex < $length) {
            if ($childIndex+1 < $length && isset($arr[$childIndex+1]) && $arr[$childIndex+1] >$arr[$childIndex]) {
                $childIndex++;
            }
            
            if ($arr[$parentIndex] >= $arr[$childIndex]) {
                break;
            }
            $temp = $arr[$parentIndex];
            $arr[$parentIndex] = $arr[$childIndex];
            $arr[$childIndex] = $temp;
            
            $parentIndex = $childIndex;
            $childIndex = 2*$parentIndex + 1;
        }
        
    }
    
    /**
     * 
     * 用来做特殊调试的
     * @param int $parentIndex
     * @param int $length
     */
    function adjustHeap1(&$arr,$parentIndex,$length)
    {
        
        $childIndex = 2*$parentIndex + 1;
        while ($childIndex < $length) {
            if ($childIndex+1 < $length && isset($arr[$childIndex+1]) && $arr[$childIndex+1] >$arr[$childIndex]) {
                $childIndex++;
            }
            
            if ($arr[$parentIndex] >= $arr[$childIndex]) {
                break;
            }
            $temp = $arr[$parentIndex];
            $arr[$parentIndex] = $arr[$childIndex];
            $arr[$childIndex] = $temp;
            
            $parentIndex = $childIndex;
            $childIndex = 2*$parentIndex + 1;
        }
    }

$arr = [20,50,10,30,70,20,80];
buildHeap($arr);
sortHeap($arr);
print_r($arr);
?>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值