算法:通過堆排序,獲取前N個最大數


算法:通過堆排序,獲取前N個最大數

在一組無序數組中,比如{1,9,8,2,7,3,6,4,5}

將數組看做是一個,也可以用二叉樹來表示

 

但是這個堆現在還不是大頂堆,大頂堆的特點是父節點永遠大於左右子節點

第一步需要將這個堆構建成大頂堆

構建前需要知道的幾點:

  • 二叉樹的最后一個非葉子節點,計算公式是:array.length / 2 - 1

  • 非葉子節點都是相鄰的,這就是為什么buildMaxHeap方法中的i的步進方式是減1

  • 父節點左子節點的計算公式是:2 * i + 1

  • 父節點右子節點的計算公式是:2 * i + 2

  • buildMaxHeap方法的length參數意義:因為構建大頂堆后,根節點就成了最大值,此時將根節點數組尾元素交換,就能將最大值移動到數組末尾,那么第一大的值已經確定,現在需要確定第二大的值,那么就需要在剩下的元素當中再次構建大頂堆,所以就需要控制后續構建大頂堆時的數組長度,也就是length

/**
* 構建大頂堆
*
* @param array  原始數組
* @param length 需要構建的長度
*/
private static void buildMaxHeap(int[] array, int length) {
    //從最后一個非葉子節點開始
    for (int i = length / 2 - 1; i >= 0; i--) {
        adjustHeap(array, i, length);
    }
}
​
/**
* 調整大頂堆
*
* @param array  被調整數組
* @param i      非葉子節點
* @param length 需要調整的長度
*/
private static void adjustHeap(int[] array, int i, int length) {
    //獲取當前非葉子節點的值
    int temp = array[i];
    //依次遍歷非葉子節點的左子節點
    for (int j = 2 * i + 1; j < length; j = 2 * j + 1) {
        //讓j指向左右子節點較大的哪個
        if (j + 1 < length && array[j] < array[j + 1]) {
            j++;
        }
        //如果子節點>父節點
        if (array[j] > temp) {
            //讓當前非葉子節點的值等於子節點的值
            array[i] = array[j];
            //讓當前非葉子節點的下標指向當前字節點的下標
            i = j;
        } else {
            //因為大頂堆是從下到上構建的,所以如果父節點是最大的那個的話就可以直接退出循環
            break;
        }
        //讓大的子節點等於之前非葉子節點的值
        array[j] = temp;
    }
}

 

當無序數組第一次執行完buildMaxHeap方法后,已經可以確定根節點就是最大值,然后將根節點的值與元素末尾的值交換

然后循環這個過程

/**
* 獲取前n個最大的數
*
* @param array 原始數組
* @param n     前n個
*/
public static int[] heapSort(int[] array, int n) {
    int size = n;
    //第一次構建大頂堆
    int length = array.length;
    buildMaxHeap(array, length);
    //此時頂端是數組中最大的節點,將頂端與數組末尾交換,然后在剩下的數組中再次構建大頂堆
    while (n > 0 && n <= length) {
        // 交換首尾元素
        int temp = array[0];
        array[0] = array[length - 1];
        array[length - 1] = temp;
        n--;
        length--;
        buildMaxHeap(array, length);
    }
    int[] result = new int[size];
    System.arraycopy(array, array.length - size, result, 0, size);
    return result;
}

 

重點看這個地方

while (n > 0 && n <= length) {
        // 交換首尾元素
        int temp = array[0];
        array[0] = array[length - 1];
        array[length - 1] = temp;
        n--;
        length--;
        buildMaxHeap(array, length);
    }
  1. 交換根節點和數組末尾元素后,意思就是已經確定最大值,然后將n減1,length減1,n用來控制構建大頂堆的次數,構建n次,就能確定前n個最大值

  2. 然后再次構建大頂堆,直到n=0跳出循環

while循環結束,那么數組的后n項,已經是排序好了的

假如n=3那么結果就是{7,8,9}

假如n=9那么結果就是{1,2,3,4,5,6,7,8,9},也就是最終堆排序的結果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM