八大排序之堆排序

1、基本思想html

  堆排序是在選擇排序基礎上改進的排序,首先創建大根堆(即任意根節點的值均不小於子節點),而後每次取出堆頂元素,從新調整堆,而後再取出堆頂元素,直至最後一個堆元素被取出,則整個排序也就完成了。它的思想就是每次取出堆中的最大值,使其天然成序。算法

2、實現步驟數組

  咱們用一個數組來創建堆,期間會用到徹底二叉樹的一些性質,好比根節點與子節點索引的關係,葉子節點與非葉子節點分別是哪些。dom

  由上面綜述可知,堆排序主要有三個步驟函數

  1)創建堆      : 創建堆咱們能夠當作是,對每個根節點進行調整,每一個根節點都知足堆的性質了,那麼整個堆也就創建成功了。詳見後面的調整堆。工具

  2)輸出堆頂  :如今堆已經創建好了,堆頂就是最大值。它在數組中的體現就是,a[0],而數組最後一個元素a[a.length-1]是未知的,將二者交換,這樣最大值就到最後去了。在下一次輸出時就應該是將a[0]與a[a.length-2]交換了,以此類推。測試

  3)調整堆     :採用遞歸的方法進行調整。一次調整隻針對當前的根節點和其子節點。若是子節點大於根節點,則交換之。進行了交換操做的子節點必然不能肯定是否知足堆的性質了,因而在對該子節點進行調整,以此類推。那麼,遞歸的出口呢?咱們知道,葉子節點是不用參與調整的,由於它沒有子節點了,不會對後面的堆產生影響。因此在進入遞歸函數時加一個判斷便可。當調整到達葉子節點時,遞歸結束。ui

3、實現代碼spa

  隨機數組測試工具類 點擊這裏code

  

package sort;

import sort.util.*;

/*
    堆排序總體思路:
    1.創建大根堆
    2.將最後一位與堆頂交換
    3.因爲此次交換,可能破壞堆的結構,進行調整
    
    時間複雜度:外層循環O(n),內層調整取決於堆的深度log2(n),綜上時間複雜度爲nlog2(n)
    空間複雜度:O(1),僅佔據一個交換單元
    穩定性:    不穩定,跳躍式交換
*/
public class HeapSort implements ISort{
    
    /*
        假設如今大根堆已經徹底創建了,交換最後一個元素與堆頂元素,至關於堆頂元素排序完成,
        則此時須要重新的根開始向下逐級檢查是否知足條件,而後作出調整。
    */
    private void adjustHeap(int[] a , int parent , int maxIndex) {
        if(maxIndex == 0) { return;}                          //堆中只剩一個元素了,不用調整了
        if(parent >= 0 && parent <= (maxIndex - 1) / 2 ){     //maxIndex-1 / 2 是非葉節點最大索引。葉子節點不用調整,遞歸結束
                                                              //且限制數組越界,故作此判斷
            int left = 2 * parent + 1;
            int right = left + 1;                             //獲取左右孩子的索引
            int maxChild = left;                              //默認左孩子最大
            if( right <= maxIndex && a[left] < a[right] ) {   //若是右孩子存在且右孩子大於左孩子
                maxChild = right;
            }
            if(a[maxChild] > a[parent]) {                     //若是孩子節點大於父節點,須要交換值。不然遞歸結束。
                int t = a[maxChild]; a[maxChild] = a[parent]; a[parent] = t;
                adjustHeap(a , maxChild , maxIndex);          //若是該孩子節點進行了交換,則必須對其進行檢查、調整。
            }
        }
    }
    
    public void buildHeap(int[] a) {                          //從最後一個非葉節點開始從下向上調整,創建堆。最大值必定是從下向上冒的。
        for(int i = (a.length - 2) / 2; i >= 0 ; i-- ){       
            adjustHeap(a , i , a.length - 1);
        }
    }
    
    //將堆頂元素與堆中的最後元素的值交換,最後一個元素就有序了,堆規模減一
    public void sort(int[] a) {
        buildHeap(a);                                         //創建堆
        for(int i = a.length-1 ; i >=1 ; i--){  
            int t = a[i]; a[i] = a[0]; a[0] = t;              //最後一個無序元素與堆頂元素交換
            adjustHeap(a , 0 , i-1);                          //調整堆,剛剛交換出來的那個已經不屬於堆中元素了
        }
    }
    
    public static void main(String[] args) {
        int[] array = RandomArrayGenerator.getRandomArray(100 , 30);
        SortTestHelper.test(new HeapSort() , array);
    }
}

 測試結果:

 

4、總結分析

   時間複雜度:O(n log n)

   空間複雜度:O(1)

  堆排序算法的時間主要用在創建堆和調整堆上面,而調整堆的複雜度是與堆的深度有關的,是log n 。因此適用於記錄較多的序列。

  本文我的編寫,水平有限,若有錯誤,懇請指出,歡迎討論分享。

相關文章
相關標籤/搜索