資料結構與演算法分析(Java語言描述)(12)—— 堆排序與陣列建堆
阿新 • • 發佈:2019-01-24
基本的堆排序
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() + " ");
}
}
}