堆排序的思想
堆排序的具體算法,思想是每次取出堆的最頂層根節點,即數組下標爲0,然後與最後一個節點即i+1交換。
參考地址
建堆過程:
- 首先將原始隊列構建成完全二叉樹
- 然後從第一個非葉子節點開始,比較當前節點和其孩子節點,將最大的元素放在當前節點,交換當前節點和最大節點元素。
注意:如果根節點是a[0],那麼第一個非葉子節點就是倒數第二層的最後一個根節點,下標爲length/2-1 - 將當前元素前面所有的元素都進行2的過程,這樣就生成了最大堆
/**
* @param data 原始數組序列
* @brief 構建堆
*/
public static void buildHeap(int[] data) {
/**
* 獲取最後一個非葉子節點
*/
int begin = data.length / 2;
for (int i = begin; i >= 0; i--) {
adjustHeap(data, data.length, i);
}
}
/**
* @param data 要調整的數組
* @param heapSize 長度
* @param index 需要調整的節點的下標
* @brief 調整堆
*/
public static void adjustHeap(int[] data, int heapSize, int index) {
/**
* 節點index的左孩子下標
*/
int leftChildSubscript = 2 * index + 1;
/**
*節點index的右孩子下標
*/
int rightChildSubscript = 2 * index + 2;
/**
* 最大元素的初始下標
*/
int largestSubscript = index;
/**
* 找到最大元素
*/
/**
* 如果當前根節點小於左孩子的值,那麼最大元素的下標爲左孩子的下標.
*/
if ((leftChildSubscript < heapSize) && (data[largestSubscript] < data[leftChildSubscript])) {
largestSubscript = leftChildSubscript;
}
/**
*如果當前根節點小於右孩子的值,那麼最大元素的下標爲右孩子的下標.
*/
if ((rightChildSubscript < heapSize) && (data[largestSubscript] < data[rightChildSubscript])) {
largestSubscript = rightChildSubscript;
}
/**
* 將最大元素調整至根節點.
* 根節點不是最大的,那麼就調整.
*/
if (index != largestSubscript) {
/**
* 將根節點的值與子節點中的最大值進行調整.
*/
swapElements(data, index, largestSubscript);
/**
*/
adjustHeap(data, heapSize, largestSubscript);
}
}
public static void swapElements(int[] data, int index1, int index2) {
int temp = data[index1];
data[index1] = data[index2];
data[index2] = temp;
}
堆排序的過程:
首先將原始序列構建爲一個堆。
- 將堆頂元素和最後一個元素交換,列表長度減1。由此無序區減1,有序區加1
- 剩餘元素重新調整建堆
- 繼續1和2,直到所有元素都完成排序
/**
* @param data 原始數組
* @brief 堆排序
*/
public static void heapSort(int[] data) {
int length = data.length;
/**
* 構建堆
*/
buildHeap(data);
while (length >= 1) {
/**
* 將堆的最後一個元素與堆頂元素交換.
*/
swapElements(data, 0, length - 1);
length--;
/**
* 將剩餘元素調整爲堆
*/
adjustHeap(data, length, 0);
}
}
時間複雜度分析
建堆的時間複雜度最差爲
堆排序的時間複雜度是
堆排序爲原地排序,空間複雜度爲