堆排序是指利用堆這種數據結構所設計的一種排序算法。堆是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點。java
在本文中咱們以大頂堆爲例算法
上浮 : 是指在構建堆時,若節點的值大於父節點則將當前節點與父節點互相交換;直至該節點小於父節點時終止上浮(能夠理解爲一個新入職的員工能力出衆晉升更高的職位); 效果以下圖數組
代碼以下:數據結構
private void siftUp(int k) {
// k == 0 時代表上浮到根節點,結束上浮操做
while (k > 0) {
// 獲取父節點索引
int parent = (k - 1) / 2;
// 小於父節點時退出,結束上浮操做
if (less(parent, k)) {
break;
}
// 與父節點交換數據
swap(parent, k);
// 改變 k 的指向 繼續上浮
k = parent;
}
}
複製代碼
下沉 : 是指構建堆的過程當中,若當前節點值小於子節點則將當前節點與子節點互相交換,直至該節點大於子節點時終止下沉(能夠理解爲一個leader能力平庸的時候被降職的過程,是否是有點很慘); 效果以下圖less
代碼以下:oop
private void siftDown (int k, int length) {
// 獲取左子節點索引
int childIndex = 2 * k + 1;
// 判斷是否存在子節點
while (childIndex < length) {
// 判斷左右子節點 查找最大的子節點
if (childIndex + 1 < length && !less(childIndex, childIndex + 1)) {
childIndex++;
}
// 若當前節點大於子節點 退出循環
if (less(k, childIndex)) {
break;
}
// 判斷當前節點是否小於子節點, 若小於執行交換
swap(k, childIndex);
// 改變 k 指向
k = childIndex;
childIndex = 2 * k + 1;
}
}
複製代碼
那麼如何採用堆的數據結構,對一個無序的數組進行排序呢 ?this
將無序數組構造堆,能夠採用上浮, 也能夠採用下沉的方式處理spa
如上圖所示,爲採用上浮
的方式構建堆,其流程是依次從下標爲 0 開始對數組的每一個元素進行上浮操做,直至最後獲得一個有序的大頂堆。設計
如上圖所示,爲採用下沉
的方式構建堆,其流程是依次從非葉子節點 開始對數組的每一個元素進行下沉操做,直至最後獲得一個有序的大頂堆。code
代碼以下:
for (int i = 0; i < array.length; i++) {
// 上浮方式構建堆
siftUp(i);
}
複製代碼
// 由於堆是徹底二叉樹的特性, 因此下標小於等於 array.length / 2 的節點爲非葉子節點
// 採用下沉的方式 從下往上構建子堆
for (int i = array.length / 2; i >= 0; i--) {
siftDown(i, array.length);
}
複製代碼
完成初始堆構造以後,剩下的工做就是重複進行如下邏輯(這個地方就不畫圖了):
完整代碼以下 :
public class HeapSort {
private int[] array;
public HeapSort(int[] array) {
this.array = array;
}
private boolean less (int i, int j) {
return array[i] > array[j];
}
private void swap (int k, int j) {
int temp = array[k];
array[k] = array[j];
array[j] = temp;
}
/** * 下沉操做 * * @param k */
private void siftDown(int k, int length) {
// loop
// 判斷是否存在子節點
int childIndex = 2 * k + 1;
while (childIndex < length) {
// 查找最大的子節點
if (childIndex + 1 < length && !less(childIndex, childIndex + 1)) {
childIndex++;
}
// 若當前節點大於子節點 退出循環
if (less(k, childIndex)) {
break;
}
// 判斷當前節點是否小於子節點, 若小於執行交換
swap(k, childIndex);
// 改變 k 指向
k = childIndex;
childIndex = 2 * k + 1;
}
}
/** * 上浮操做 * * @param k */
private void siftUp(int k) {
while (k > 0) {
int parent = (k - 1) / 2;
// 小於父節點時退出
if (less(parent, k)) {
break;
}
// 與父節點交換數據
swap(parent, k);
// 改變 k 的指向
k = parent;
}
}
public void sort () {
// 構造堆
for (int i = 0; i < array.length; i++) {
siftUp(i);
}
print();
int n = array.length - 1;
while (n > 0) {
// 由於每次完成堆的構造後, 根節點爲最大(小)值節點
// 將根節點與最後一個節點交換
swap(0, n);
for (int i = 0; i <= n - 1; i++) {
// 排除有序的節點
// 從新構造堆
siftUp(i);
}
print();
n--;
}
}
private void sort1 () {
// 構建堆
// 由於堆是徹底二叉樹的特性, 因此下標小於等於 array.length / 2 的節點爲非葉子節點
// 採用下沉的方式 從下往上構建子堆
for (int i = array.length / 2; i >= 0; i--) {
siftDown(i, array.length);
}
print();
int n = array.length - 1;
while (n > 0) {
// 由於每次完成堆的構造後, 根節點爲最大(小)值節點
// 將根節點與最後一個節點交換
swap(0, n);
for (int i = n / 2; i >= 0; i--) {
// 排除有序的節點
// 從新構造堆
siftDown(i, n);
}
print();
n--;
}
}
private void print () {
for (Integer num : array) {
System.out.print(num);
System.out.print(",");
}
System.out.println("");
}
public static void main(String[] args) {
int[] array = {10, 40, 38, 20, 9, 15, 25, 30, 32};
new HeapSort(array).sort();
System.out.println("");
new HeapSort(array).sort1();
}
}
複製代碼
小結 : 堆排序在哪裏應用到了呢 ? 可參考優先隊列 PriorityQueue 的實現