堆排序:不穩定排序
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分爲大根堆和小根堆,是完全二叉樹。
思想:初始時把要排序的數的序列看作是一棵順序存儲的二叉樹,調整它們的存儲序,使之成爲一個 堆,這時堆的根節點的數最大。然後將根節點與堆的最後一個節點交換。然後對前面(n-1)個數重新調整使之成爲堆。依此類推,直到只有兩個節點的堆,並對
它們作交換,最後得到有n個節點的有序序列。從算法描述來看,堆排序需要兩個過程,一是建立堆,二是堆頂與堆的最後一個元素交換位置。所以堆排序有兩個函數組成。一是建堆的滲透函數,二是反覆調用滲透函數實現排序的函數。
由於建初始堆所需的比較次數較多,所以堆排序不適宜於記錄數較少的文件。
時間複雜度:
最差:O(nlog2n)
平均:O(nlog2n)
最好:O(nlog2n)
空間複雜度: O(1)
package test;
import java.util.Arrays;
/*
* 堆排序
* 大頂堆的特點:父節點比左右節點的值都大的完全二叉樹
* 數組中可以按下標來確定在二叉樹的位置,例如,位置爲i的父節點,其子節點位置是2*i 和 2*i+1
* 所以可以直接將數組當成二叉樹來處理,不用另外建樹。注意,數組的第一個元素A[0] 不在二叉樹的範圍內
* 二叉樹應滿足 父節點 大於子節點,所以從最後一個非葉節點開始往前遍歷,來調整堆
* (注意:當交換節點位置時,需要重新調整子節點樹,因爲經過交換後,其子樹可能不符合堆的要求)
*
* 當二叉樹滿足大頂堆時,堆頂的元素就是最大的元素。從而可以利用這個來排序
* 將堆頂元素與對的最後一個元素交換,然後對前面(n-1)個元素重新調整,就可以變成新的堆。重複交換即可排序
*/
public class HeapSort {
//調整堆的函數,i爲需要調整的子樹的父節點
static void heapAdjust(int a[], int i, int size) {
//左右節點的下標
int l = 2* i;
int r = 2*i + 1;
//尋找父節點,左節點,右節點中最大的元素的下標,注意左右節點的下標不能越界
int max = i;
if(l <= size && a[l] > a[max]) {
max = l;
}
if(r <= size && a[r] > a[max]) {
max = r;
}
//max != i 表明父節點不是最大的,需要交換該子節點A[max] 和父節點,並且重新調整該子節點的子樹
if(max != i) {
int temp = a[i];
a[i] = a[max];
a[max] = temp;
//調整子節點的子樹
heapAdjust(a,max,size);
}
}
//建堆函數:初始化數組時,從最後一個非葉子節點開始往前掃描,逐一調整堆,直到A[1],表明建堆完畢
static void heapBuild(int a[], int size) {
for(int i = size/2; i >= 1; i--) {
heapAdjust(a,i,size);
}
}
//堆排序函數
static void heapSort(int a[], int size) {
//首先建堆
heapBuild(a,size);
//從最後一個元素開始,與堆頂元素交換,得到最大的數。前面n-1個數重新調整爲堆,再繼續交換
for(int i = size; i >= 1; i--) {
int temp = a[1];
a[1] = a[i];
a[i] = temp;
heapAdjust(a,1,i-1);
}
}
public static void main(String args[]) {
int a[] = new int[]{0,16,7,3,20,17,8};
heapSort(a,6);
System.out.println(Arrays.toString(a));
}
}