[100天每天一个算法--第4天] 堆排序(最大堆)

写在前面:
堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。首先简单了解下堆结构。
定义:堆是具有以下性质的完全二叉树:每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。
堆排序利用了大根堆(或小根堆)堆顶记录的关键字最大(或最小)这一特征,使得在当前无序区中选取最大(或最小)关键字的记录变得简单。
(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]调整为堆。
……
直到无序区只有一个元素为止。
PHP实现代码如下


/*
堆排序(最大堆)
 */
$arr = [19,23,12,6,8,10];
//初始化大顶堆,从下往上调成大顶堆 从最后一个有孩子的节点开始往上调整 
for ($i=intval(count($arr)/2)-1;$i>=0;$i--)
{
    sift_down($arr,$i,count($arr)-1);
}
var_dump($arr);//初始大顶堆
for ($i=count($arr)-1;$i>0;$i--)
{
    swap($arr[0],$arr[$i]);//交换堆顶元素和堆尾元素
    sift_down($arr,0,$i-1);//堆长度减一,再从上往下调成大顶堆
}
var_dump($arr);//排序完毕
/*
   $array  数组
   $start  父节点下标
   $end    结束下标
   return  无
 */
function sift_down (&$array = [],$start,$end) 
{
    while(true) {
        //当列表第一个是以下标0开始,结点下标为i,左孩子则为2*i+1,右孩子下标则为2*i+2
        $lchild = 2*$start+1;
        if ($lchild > $end) {//控制界限
            break;
        }
        if ($lchild+1 <= $end && $array[$lchild+1] > $array[$lchild])//如果右节点存在且大于左节点
        {
            $lchild +=1;//等于右节点下标
        }
        if ($array[$lchild] > $array[$start]) //如果子节点大于父节点
        {
            swap($array[$lchild], $array[$start]);//交换
            $start = $lchild; //交换之后以交换子结点为根的堆可能不是大顶堆,需重新调整
        } else {
            break;
        }

    }
}
function swap (&$a,&$b) 
{
    $temp = $a;
    $a = $b;
    $b = $temp;
}

你可能感兴趣的:(PHP,算法)