堆排序原理以及實現
堆性質的簡介
堆的創建
public void maxHeap(int[] A,int i,int length){
int left = i*2 + 1; // 根結點的左孩子
int right = i*2 + 2; // 根結點的右孩子
int largest = i;
// 如果存在左孩子,且左孩子大於插入結點的值
if(left < length && A[i] < A[left]){
largest = left;
}
// 如果存在右孩子,且右孩子大於插入結點的值
if(right < length && A[largest] < A[right]){
largest = right;
}
// 如果最大值不是當前插入的結點的值,交換兩個結點之間的值
if(largest != i){
exchange(A, i, largest);
maxHeap(A,largest,length);
}
}
private void exchange(int[] A, int i, int largest) {
A[i] = A[i] ^ A[largest];
A[largest] = A[i] ^ A[largest];
A[i] = A[i] ^ A[largest];
}
從程序中我們可以看出,對於新插入的結點在數組的第一個位置,首先比較其對應的左孩子與右孩子的值,將三個結點中值最大的設置爲根結點,將要插入的結點與其交換位置。利用下標 largest,i 判斷是否發生過交換,如果發生過交換,交換對應兩個結點的值,利用遞歸的形式重複上面的步驟,未發生交換則退出。下圖爲交換的過程。
public void buildMaxHeap(int[] A){
for(int i = A.length >> 1; i >= 0 ; i--){
maxHeap(A,i,A.length);
}
}
通過上面的幾個程序,我們就可以建立一個堆了。利用創建好的堆進行堆排序
// 堆排序
public void heapSort(){
// 考慮到堆是完全二叉樹,所以可以考慮使用連續的數組存儲二叉樹的結點。
int a[] = {4,1,3,2,16,9,10,14,8,7};
// 建立最大堆
buildMaxHeap(a);
System.out.println(Arrays.toString(a));
// 堆排序
int length = a.length;
for(int i = 0; i+1 < length;){
exchange(a,i,--length);
maxHeap(a,i,length);
}
System.out.println(Arrays.toString(a));
}
利用堆構造優先隊列
// 把元素x插入到集合A中
public void insert(int[] A,int x){
int i;
// 遍歷一遍數組,查找已存在數組的末尾下標
for(i = 0; i < A.length; i++){
if(A[i] == -1){
break;
}
}
A[i] = Integer.MIN_VALUE;
increaseKey(A,i,x);
}
public int maximum(int[] A){
return A[0];
}
extactMax(S) : 去掉並返回S中具有最大關鍵字的元素,我們結合之前的maxHeap()方法,很容易想到如何實現這個方法。 // 返回最大優先隊列中的最大元素並刪除
public int extractMax(int[] A){
int max;
int length = A.length-1;
// 判斷A數組中元素的個數
if(A.length == 0){
return Integer.MIN_VALUE;
}
max = A[0];
// 交換兩個數的值
exchange(A,0,length);
// 重新維護堆的結構
maxHeap(A,0,length);
return max;
}
increaseKey(S,x,k) : 將元素x的關鍵字值增加到k,這裏假設k的值不小於x的原關鍵字的值。 public void increaseKey(int[] A,int i,int key){
if(key < A[i-1]){
System.out.println("new key is smaller than key");
}
A[i-1] = key;
while(i > 0 && A[i-1] > A[i>>1]){
exchange(A,i-1,i/2);
i = i>>1;
}
}