堆排序
1.前提知識
堆:(英語:heap)是計算機科學中一類特殊的數據結構的統稱。堆通常是一個可以被看做一棵樹的數組對象。
堆中某個節點的值總是不大于或不小于其父節點的值;
堆總是一棵完全二叉樹。
將根節點最大的堆叫做最大堆或大根堆,根節點最小的堆叫做最小堆或小根堆。
優先隊列(priority queue)
普通的隊列是一種先進先出的數據結構,元素在隊列尾追加,而從隊列頭刪除。在優先隊列中,元素被賦予優先級。當訪問元素時,具有最高優先級的元素最先刪除。優先隊列具有最高級先出 (first in, largest out)的行為特征。通常采用堆數據結構來實現。
a.完全二叉樹
若設二叉樹的深度為h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層所有的結點都連續集中在最左邊,這就是完全二叉樹。
完全二叉樹是由滿二叉樹(一個二叉樹,如果每一個層的結點數都達到最大值,則這個二叉樹就是滿二叉樹。也就是說,如果一個二叉樹的層數為K,且結點總數是(2^k) -1 ,則它就是滿二叉樹。)而引出來的。對于深度為K的,有n個結點的二叉樹,當且僅當其每一個結點都與深度為K的滿二叉樹中編號從1至n的結點一一對應時稱之為完全二叉樹。
(1)所有的葉結點都出現在第k層或k-l層(層次最大的兩層)
(2)對任一結點,如果其右子樹的最大層次為L,則其左子樹的最大層次為L或L+l。
一棵二叉樹至多只有最下面的兩層上的結點的度數可以小于2,并且最下層上的結點都集中在該層最左邊的若干位置上,則此二叉樹成為完全二叉樹,并且最下層上的結點都集中在該層最左邊的若干位置上,而在最后一層上,右邊的若干結點缺失的二叉樹,則此二叉樹成為完全二叉樹。
b.小根堆和大根堆
假設有一棵完全二叉樹,在滿足作為完全二叉樹的基礎上,對于任意一個擁有父節點的子節點,其數值均不小于父節點的值;這樣層層遞推,就是根節點的值最小,這樣的樹,稱為小根堆。
同理,又有一棵完全二叉樹,對于任意一個子節點來說,均不大于其父節點的值,如此遞推,就是根節點的值是最大的,這樣的數,稱為大根堆。
2.一些規律
最后一個非葉節點的位置為nums.length-1,所有擁有左右子節點的節點與子節點的位置關系為,假設子節點的編號為n,那么左子節點的編號為2n+1右節點的編號為2(n+1)
然后具體的做法為1.先建立大(小)頂堆,2.交換堆頂的位置和調整整個堆,每調整一次堆的長度就減少一,直到堆的大小為0或1結束
3.先實現大頂堆
public static void main(String[] args) {
int[] array = new int[]{1,3,4,2,5};
heapsort(array);
其結構就相當于
然后我們先實現大頂堆
private static int[] heapsort(int[] array) {
buildBigheap(array);
}
從最后一個非葉節點的節點開始調整,一直調整到根節點,這樣大頂堆就建立好了
private static void buildBigheap(int[] array) {
for(int i=array.length/2-1;i>-1;i--){ //從最后一個非葉節點的節點開始調整,一直調整到根節點
adjustheap(array,i,array.length);
}
然后是具體的調整過程:
private static void adjustheap(int[] array, int i,int end) {
//如果沒有左右子樹或者左右子樹現在已經是拍好序的順序
if(2*i+1 >=end){
return;
}else if(2*(i+1)>=end){
if(array[2*i+1]>=array[i]){
swap(array,2*i+1,i);
}
}else{
//根已為最大
if(array[i]>=array[2*i+1]&&array[i]>=array[2*(i+1)]){
return;
//左子樹最大,交換后去排列左邊
}else if(array[2*i+1]>=array[i]&&array[2*i+1]>=array[2*(i+1)]){
swap(array,2*i+1,i);
adjustheap(array,2*i+1,end);
//右子樹最大,交換后去排列右邊
}else{
swap(array,2*(i+1),i);
adjustheap(array,2*(i+1),end);
}
}
}
**具體就是先判斷現在調整的位置,
A狀況說明(分成兩種情況:1.該節點是葉節點,2,該節點往后的子節點已經被拍好序了)
B狀況說明(因為當一個節點只含有左子節點可以用的時候,那么他的右節點一定是空或者已經排好序了)
C狀況說明(如果根最大什么都不用做,直接返回,如果左子節點最大,那么與根交換,并對左子節點的剩余節點進行調整,同理可得右子節點)
OK
現在做完這一步我們可以看一下數組結構
這個時候的根是最大的也就是已經實現了大頂堆
4.交換堆頂元素與最后一個元素,實現排序
其實這一過程就是交換調整,每一次交換都會使堆的需排序的大小減一,那么很容易就想到當堆大小為1時,調整完畢,所以用一個for循環for i array.length ->1
for(int i =array.length-1;i>0;i--){
swap(array,i,0); //交換棧頂元素與堆的最后一個數據(每次最后一個數據都會減一)
adjustheap(array,0,i); //調整堆為大頂堆
}
例如第一次交換后的數據為
然后調整完的數據為:
這樣做完后所有的序都已經排好了
完整代碼
public static void main(String[] args) {
int[] array = new int[]{1,3,4,2,5};
heapsort(array);
for(int i=0;i<array.length;i++){
System.out.printf(array[i]+" ");
}
}
private static int[] heapsort(int[] array) {
buildBigheap(array);
return array;
}
private static void buildBigheap(int[] array) {
for(int i=array.length/2-1;i>-1;i--){
adjustheap(array,i,array.length);
}
for(int i =array.length-1;i>0;i--){
swap(array,i,0);
adjustheap(array,0,i);
}
}
private static void adjustheap(int[] array, int i,int end) {
//如果沒有左右子樹或者左右子樹現在已經是拍好序的順序
if(2*i+1 >=end){
return;
}else if(2*(i+1)>=end){
if(array[2*i+1]>=array[i]){
swap(array,2*i+1,i);
}
}else{
//根已為最大
if(array[i]>=array[2*i+1]&&array[i]>=array[2*(i+1)]){
return;
//左子樹最大,交換后去排列左邊
}else if(array[2*i+1]>=array[i]&&array[2*i+1]>=array[2*(i+1)]){
swap(array,2*i+1,i);
adjustheap(array,2*i+1,end);
//右子樹最大,交換后去排列右邊
}else{
swap(array,2*(i+1),i);
adjustheap(array,2*(i+1),end);
}
}
}
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] =array[j];
array[j] = temp;
}