1. 程式人生 > >資料結構與演算法分析(Java語言描述)(12)—— 堆排序與陣列建堆

資料結構與演算法分析(Java語言描述)(12)—— 堆排序與陣列建堆

基本的堆排序

HeapSortBasic.java

package com.algorithm.sort;

import com.dataStructure.heap.MaxHeap;

public class HeapSortBasic {
    private HeapSortBasic() {
    }

    // 對整個arr陣列使用 HeapSortBasic 排序
    // HeapSortBasic, 將所有的元素依次新增到堆中, 在將所有元素從堆中依次取出來, 即完成了排序
    // 無論是建立堆的過程, 還是從堆中依次取出元素的過程, 時間複雜度均為O(nlogn)
// 整個堆排序的整體時間複雜度為O(nlogn) public static void sort(Integer[] arr) { int n = arr.length; MaxHeap maxHeap = new MaxHeap(n); // 將 arr 中的元素依次取出,插入最大堆中 for (Integer num: arr){ maxHeap.insert(num); } // 取出最大堆中最大的元素,賦值給 arr 中的最後一個元素 for
(int i = n-1; i>=0; i--){ arr[i] = maxHeap.extractMax(); } } public static void main(String [] args){ int N = 1000000; Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000); System.out.println("排序前的陣列為:"); SortTestHelper.printArray(arr); Long start = System.currentTimeMillis(); HeapSortBasic.sort(arr); Long end = System.currentTimeMillis(); System.out
.println("-------------------------------------------"); System.out.println("排序後的陣列為:"); SortTestHelper.printArray(arr); System.out.println("-------------------------------------------"); System.out.println("排序後的陣列是否有序:"); if (SortTestHelper.isSorted(arr)) { System.out.println("陣列有序~"); } else { System.out.println("陣列無序!"); } System.out.println("-------------------------------------------"); System.out.println("排序演算法的執行時間為" + " : " + (end - start) + "ms"); } }

使用 Heapify 方式的堆排序

HeapSortHeapify.java

package com.algorithm.sort;

import com.dataStructure.heap.MaxHeap;

public class HeapSortHeapify {
    private HeapSortHeapify() {
    }

    // 對整個arr陣列使用 HeapSortHeapify 排序
    // HeapSortHeapify, 藉助我們的heapify過程建立堆
    // 此時, 建立堆的過程時間複雜度為O(n), 將所有元素依次從堆中取出來, 實踐複雜度為O(nlogn)
    // 堆排序的總體時間複雜度依然是O(nlogn), 但是比 HeapSortBasic 效能更優, 因為建立堆的效能更優
    public static void sort(Integer[] arr) {
        int n = arr.length;
        MaxHeap maxHeap = new MaxHeap(arr);

        for (int i = n - 1; i>=0;i--){
            arr[i] = maxHeap.extractMax();
        }
    }

    public static void main(String[] args){
        int N = 1000000;
        Integer[] arr = SortTestHelper.generateRandomArray(N, 0, 100000);

        System.out.println("排序前的陣列為:");
        SortTestHelper.printArray(arr);
        Long start = System.currentTimeMillis();
        HeapSortHeapify.sort(arr);
        Long end = System.currentTimeMillis();
        System.out.println("-------------------------------------------");

        System.out.println("排序後的陣列為:");
        SortTestHelper.printArray(arr);
        System.out.println("-------------------------------------------");

        System.out.println("排序後的陣列是否有序:");
        if (SortTestHelper.isSorted(arr)) {
            System.out.println("陣列有序~");
        } else {
            System.out.println("陣列無序!");
        }
        System.out.println("-------------------------------------------");

        System.out.println("排序演算法的執行時間為" + " : " + (end - start) + "ms");
    }
}

MaxHeap.java

package com.dataStructure.heap;

import java.lang.*;

public class MaxHeap {
    protected Integer[] data;
    protected int count;
    protected int capacity;

    // 建構函式,構造一個空的堆,可以容納 capacity 個元素
    public MaxHeap(int capacity) {
        data = new Integer[capacity + 1];
        count = 0;
        this.capacity = capacity;
    }

    // 建構函式,用 Heapify 的方式使用陣列構建最大堆
    // 根據堆的性質, 只要保證部分有序即可, 即根節點大於左右節點的值.
    // 將陣列抽象為一個完全二叉樹, 所以只要從【最後一個非葉子節點】向前遍歷每一個節點即可.
    // 如果當前節點比左右子樹節點都大, 則已經是一個最大堆, 否則將當前節點與左右節點較大的一個交換。
    public MaxHeap(Integer[] arr){
        int n = arr.length;
        data = new Integer[n+1];
        capacity = n;

        for (int i =0; i<n;i++){
            data[i+1] = arr[i];
        }
        count = n;

        // 從最後一個非葉子節點向前遍歷到根節點,從每一個節點進行 shiftDown 操作
        for (int i = count/2; i >= 1; i--){
            shiftDown(i);
        }

    }

    // 返回堆中元素的個數
    public int size() {
        return count;
    }

    // 返回一個布林值,表示堆是否為空
    public boolean isEmpty() {
        return count == 0;
    }

    // 向最大堆中插入一個新的元素 item
    public void insert(Integer item) {
        if (count + 1 > capacity) {
            System.out.println("元素數量已經達到堆容量的最大值!");
            return;
        }
        // 先將 item 放在堆的末尾
        data[count + 1] = item;
        count++;
        // 上浮 item
        shiftUp(count);
    }

    // 將堆中 k 位置的元素 上浮 到合適的位置
    private void shiftUp(int k) {
        // 如果 當前節點(k指向的位置) 不是根節點,且 父節點 < 當前節點
        // 進行迴圈
        while (k > 1 && data[k / 2].compareTo(data[k]) < 0) {
            swap(k, k / 2);
            k /= 2;
        }
    }

    // 將堆中 i 和 j 位置的元素進行交換
    private void swap(int i, int j) {
        Integer temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

    // 從堆中取出堆頂元素,即堆中所儲存的最大元素
    public Integer extractMax() {
        if (count <= 0 ){
            System.out.println("堆已空!");
            return null;
        }
        Integer ret = data[1];
        swap(1, count);
        count--;
        shiftDown(1);
        return ret;
    }

    // 將堆頂元素 下沉 到合適的位置
    private void shiftDown(int k){
        while (2*k <= count){
            int j = 2*k;

            // 如果 有 右子節點 且 右子節點 > 左子節點
            if (j+1 <= count && data[j+1].compareTo(data[j]) > 0){
                // j 移動到 右子節點 處
                j++;
            }

            // 如果 根節點 > 子節點中較大者
            if (data[k].compareTo(data[j]) >= 0){
                // 跳出迴圈
                break;
            }

            // 交換 根節點 和 子節點中較大者
            swap(k, j);

            // 移動 k
            k = j;
        }
    }

    public static void main(String[] args) {
        MaxHeap maxHeap = new MaxHeap(100);

        int N = 50; // 堆中元素個數
        int M = 100; // 堆中元素取值範圍[0, M)

        for (int i = 0; i < N; i++)
            maxHeap.insert(new Integer((int) (Math.random() * M)));
        System.out.println(maxHeap.size());

//        for (Integer item : maxHeap.data) {
//            System.out.print(item + " ");
//        }

        while (maxHeap.count != 0){
            System.out.print(maxHeap.extractMax() + " ");
        }
    }


}