php實現堆排序(完全二叉樹,可用一維數組存儲)

轉, 原文:https://www.cnblogs.com/iampeter/p/3223487.html

------------------------------------------

 

堆排序有點小複雜,分成三塊:

第一塊,什麼是堆,什麼是最大堆

第二塊,怎麼將堆調整爲最大堆,這部分是重點

第三塊,堆排序介紹

 

第一塊,什麼是堆,什麼是最大堆

什麼是堆

這裏的堆(二叉堆),指得不是堆棧的那個堆,而是一種數據結構。

堆可以視爲一棵完全的二叉樹,完全二叉樹的一個“優秀”的性質是,除了最底層之外,每一層都是滿的,這使得堆可以利用數組來表示,每一個結點對應數組中的一個元素.

數組與堆之間的關係

二叉堆一般分爲兩種:最大堆和最小堆。

什麼是最大堆

堆中每個父節點的元素值都大於等於其孩子結點(如果存在),這樣的堆就是一個最大堆

因此,最大堆中的最大元素值出現在根結點(堆頂)

節點與數組索引關係

對於給定的某個結點的下標i,可以很容易的計算出這個結點的父結點、孩子結點的下標,而且計算公式很漂亮很簡約

第二塊,怎麼將堆調整爲最大堆,這部分是重點

整個過程如下圖所示

在4,14,7這個小堆裏邊,父節點4小於左孩子14,所以兩者交換

在4,2,8這個小堆裏邊,父節點4小於右孩子8,所以兩者交換

上圖展示了一趟調整的過程,這個過程遞歸實現,直到調整爲最大堆爲止

第三塊,堆排序介紹

堆排序就是把堆頂的最大數取出,

將剩餘的堆繼續調整爲最大堆,具體過程在第二塊有介紹,以遞歸實現

剩餘部分調整爲最大堆後,再次將堆頂的最大數取出,再將剩餘部分調整爲最大堆,這個過程持續到剩餘數只有一個時結束

下邊三張圖詳細描述了整個過程

 

/**
 * 使用異或交換2個值,原理:一個值經過同一個值的2次異或後,原值不變
 * @param int $a
 * @param int $b
 */
function swap(&$a,&$b){
    $a = $a^$b;
    $b = $a^$b;
    $a = $a^$b;
}
 
/**
 * 整理當前樹節點($n),臨界點$last之後爲已排序好的元素
 * @param int $n
 * @param int $last
 * @param array $arr
 * 
 */
function adjustNode($n,$last,&$arr){
    $l = $n<<1;   // 左孩子
    if( !isset($arr[$l])||$l>$last ){
        return ;
    }
    $r = $l+1;  // 右孩子
    // 如果右孩子比左孩子大,則讓父節點與右孩子比
    if( $r<=$last&&$arr[$r]>$arr[$l] ){
        $l = $r;
    }
    // 如果其中子節點$l比父節點$n大,則與父節點$n交換
    if( $arr[$l]>$arr[$n] ){
        swap($arr[$l],$arr[$n]);
        // 交換之後,父節點($n)的值可能還小於原子節點($l)的子節點的值,所以還需對原子節點($l)的子節點進行調整,用遞歸實現
        adjustNode($l, $last, $arr);
    }
}
 
/**
 * 堆排序(最大堆)
 * @param array $arr
 */
function heapSort(&$arr){
    // 最後一個蒜素位
    $last = count($arr);
    // 堆排序中常忽略$arr[0]
    array_unshift($arr, 0);
    // 最後一個非葉子節點
    $i = $last>>1;
    // 整理成最大堆,最大的數放到最頂,並將最大數和堆尾交換,並在之後的計算中,忽略數組最後端的最大數(last),直到堆頂(last=堆頂)
    while(true){
        adjustNode($i, $last, $arr);
        if( $i>1 ){
            // 移動節點指針,遍歷所有節點
            $i--;
        }
        else{
            // 臨界點$last=1,即所有排序完成
            if( $last==1 ){
                break;
            }
            swap($arr[$last],$arr[1]);
            $last--;
        }
    }
    // 彈出第一個元素
    array_shift($arr);
}

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章