用Java實現【堆排序】

一、堆排序基本介紹

  1. 堆排序是利用堆這種數據結構而設計的一種排序算法,堆排序是一種選擇排序,它的最壞,最好,平均時間複雜度均爲O(nlogn),它也是不穩定排序。
  2. 堆是具有以下性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱爲大頂堆, 注意 : 沒有要求結點的左孩子的值和右孩子的值的大小關係。
  3. 每個結點的值都小於或等於其左右孩子結點的值,稱爲小頂堆
  4. 大頂堆舉例說明
    大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2] , i 對應第幾個節點,i從0開始編號

在這裏插入圖片描述
6. 小頂堆舉例說明
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2] , i 對應第幾個節點,i從0開始編號

在這裏插入圖片描述

二、基本思想

  1. 將待排序序列構造成一個大頂堆
  2. 此時,整個序列的最大值就是堆頂的根節點。
  3. 將其與末尾元素進行交換,此時末尾就爲最大值。
  4. 然後將剩餘n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值。如此反覆執行,便能得到一個有序序列了。

可以看到在構建大頂堆的過程中,元素的個數逐漸減少,最後就得到一個有序序列了.

三、思路步驟

思路

  1. 將無序序列構建成一個堆,根據升序降序需求選擇大頂堆或小頂堆;
  2. 將堆頂元素與末尾元素交換,將最大元素"沉"到數組末端;
  3. 重新調整結構,使其滿足堆定義,然後繼續交換堆頂元素與當前末尾元素,反覆執行調整+交換步驟,直到整個序列有序。

步驟一

構造初始堆。將給定無序序列構造成一個大頂堆(一般升序採用大頂堆,降序採用小頂堆)。

  1. 假設給定無序序列結構如下
    在這裏插入圖片描述

  2. 此時我們從最後一個非葉子結點開始(葉結點自然不用調整,第一個非葉子結點 arr.length/2-1=5/2-1=1,也就是下面的6結點),從左至右,從下至上進行調整。
    在這裏插入圖片描述

  3. 找到第二個非葉節點4,由於[4,9,8]中9元素最大,4和9交換。
    在這裏插入圖片描述

  4. 這時,交換導致了子根[4,5,6]結構混亂,繼續調整,[4,5,6]中6最大,交換4和6。
    在這裏插入圖片描述
    此時,我們就將一個無序序列構造成了一個大頂堆。

步驟二

將堆頂元素與末尾元素進行交換,使末尾元素最大。然後繼續調整堆,再將堆頂元素與末尾元素交換,得到第二大元素。如此反覆進行交換、重建、交換。

  1. 將堆頂元素9和末尾元素4進行交換
    在這裏插入圖片描述
  2. 重新調整結構,使其繼續滿足堆定義
    在這裏插入圖片描述
  3. 再將堆頂元素8與末尾元素5進行交換,得到第二大元素8.
    在這裏插入圖片描述
  4. 後續過程,繼續進行調整,交換,如此反覆進行,最終使得整個序列有序
    在這裏插入圖片描述

四、代碼實現

import java.util.Arrays;
// 堆排序
public class HeapSort {
    public static void main(String[] args) {
        //速度測試 
        long begin = System.currentTimeMillis();
        int[] array = {3, 6, 2, 4, 5, 7, 1};
//        int[] array = new int[8];
//        for (int i = 0; i < array.length; i++) {
//            array[i] = (int) (Math.random() * 8000000);
//        }

        heapSort(array);

        long end = System.currentTimeMillis();
        System.out.println("用時:" + (end - begin));
        System.out.println(Arrays.toString(array));
    }

    public static void heapSort(int[] array) {
        int temp = 0;

        //1.將無序序列構建一個堆,根據升序降序需求選擇大頂堆或小頂堆
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustHeap(array, i, array.length);
        }
//        System.out.println("完全二叉樹"+Arrays.toString(array));

        //2.把對頂元素與末尾元素交換,將最大的元素沉到數組末端;
        //3.重新調整結構,使其滿足堆定義
        for (int j = array.length - 1; j > 0; j--) {
            //交換
            temp = array[j];
            array[j] = array[0];
            array[0] = temp;
            adjustHeap(array, 0, j);
        }
//        System.out.println("排序後"+Arrays.toString(array));
    }

    /**
     * 將一個數組(二叉樹)調整爲大頂堆
     *
     * @param array  待調整的數組
     * @param index  非葉子節點在數組中的索引
     * @param length 表示對多少個元素繼續調整
     */
    public static void adjustHeap(int[] array, int index, int length) {
        //先取出當前元素的值,保存到當前臨時變量
        int temp = array[index];
        //k = index * 2 + 1 是index結點的左子節點
        for (int k = index * 2 + 1; k < length; k = k * 2 + 1) {
            //左子節點的值小於右子節點的值
            if (k + 1 < length && array[k] < array[k + 1]) {
                //k 指向右子節點
                k++;
            }
            // 如果子節點的值大於父結點的值則進行值的交換
            if (array[k] > temp) {
                array[index] = array[k];
                //index指向 k ,繼續循環比較
                index = k;
            } else {
                break;
            }
            //把temp放到調整後的位置
            array[index] = temp;
        }
    }
}

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