堆排序問題

堆排序

堆排序是在二叉樹的概念上創建起來的一種排序方式,利用的是二叉樹中的徹底二叉樹;完美二叉樹兩種樹形結構來完成堆排序c++

關於徹底二叉樹的概念能夠去查閱一些,這裏不做詳細說明,完美二叉樹依據從上至下,從左至右的方式能夠轉換成一種數組存儲結構,每個節點都是對應數組索引數組

基本概念:

  0.堆排序分爲大頂堆,小頂堆,每個堆都是從大到小和從小到大(堆頂 > 左右 | 堆頂 < 左右)優化

  1.按照0索引開始從上至下,N個節點的徹底二叉樹,最後一個非葉子節點N / 2 - 1,非葉子節點的範圍 = [0,N / 2 -1]spa

  2.非葉子節(N)點和本身的左(L)右(R)節點的關係:L = N * 2 + 1   R = N * 2 + 2排序

  3.堆排序方式:咱們從下至上查找,每次完成一個非葉子節點的堆排序,使根節點的值最大,交換根節點和最後一個節點的值索引

對於從最後一個非葉子節點像上進行推導:重構

/**
* 咱們從最下最左的非葉子節點開始構建堆,葉子節點自己就是堆了。
* 咱們須要構建最後一個非葉子節點的堆
* */
public void heap(int[] arr){
int a = arr.length/2-1; //找到最後一個非葉子節點索引
int l = a * 2 + 1; //左子節點索引
int r = a * 2 + 2; //右子節點索引
int i = 0;
if(arr[a] < arr[l] || arr[a] < arr[r]){ //咱們斷定只有當它確實比其中一個小的時候咱們才進來判斷
if(arr[l] > r){
i = arr[a];
arr[a] = arr[l];
arr[l] = i;
}else if(arr[l] < arr[r]){
i = arr[a];
arr[a] = arr[r];
arr[r] = i;
}
}
}
/**
* 倒數第二個非葉子節點
* */
public void heap_1(int[] arr){
int a = arr.length/2-2; //找到倒數第二個非葉子節點索引
int l = a * 2 + 1; //左子節點索引
int r = a * 2 + 2; //右子節點索引
int i = 0;
if(arr[a] < arr[l] || arr[a] < arr[r]){ //咱們斷定只有當它確實比其中一個小的時候咱們才進來判斷
if(arr[l] > r){
i = arr[a];
arr[a] = arr[l];
arr[l] = i;
}else if(arr[l] < arr[r]){
i = arr[a];
arr[a] = arr[r];
arr[r] = i;
}
}
}
問題:當咱們改變第二個非葉子節點時,若是該節點和以前的非葉子節點有依賴關係怎麼處理?
   若是有依賴關係說明非左即右,咱們只須要對這個非葉子節點堆發生了交換的節點進行判斷是否該節點是非葉子節點,由於徹底二叉樹的特性,若是是非葉子節點,那麼必然是有左節點的。
  /**
* 對上一個代碼進行完善,heap_2就是一次完整的構建大頂堆的過程
* 解決問題:若是產生依賴關係如何處理 / 控制全部非葉子節點的遍歷 / 該代碼能夠做爲構建堆頂的使用,可是heap_2的優化不足
* */
public void heap_2(){
int i = 0;
for(int a=arr.length/2-1;a>=0;a--){ //控制非葉子節點的遍歷
int c = a;
while(c * 2 + 1 < arr.length){ //c就是咱們本次進行堆頂節點,若是發生了交換,咱們會對交換的節點和堆頂節點進行替換
int l = c * 2 + 1;
int r = c * 2 + 2;
if(arr[c] < arr[l] || arr[c] < arr[r]){
if(arr[l] > arr[r]){
i = arr[c];
arr[c] = arr[l];
arr[l] = i;
c = l; //若是是l和c進行了交換
}else if(l < r){
i = arr[c];
arr[c] = arr[r];
arr[r] = i;
c = r;        //若是是r和c進行了交換
}
}
}
}
}
/**
* 優化代碼
* */
public void heap_3(){
int i = 0;
for(int a=arr.length/2-1;a>=0;a--){
int c = a;
int b = a;
while((c = c * 2 + 1) < arr.length){
if(arr[c] < arr[c+1]){
c++;
}
if(arr[b] < arr[c]){
i = arr[b];
arr[b] = arr[c];
arr[c] = i;
b = c;
}
}
}
}
當咱們從下至上把大/小頂堆構建完成後,須要的就是不停的把堆頂和末尾進行交換,並在中間穿插對依賴的堆重構大/小堆
/**
* 一下爲對堆排序的完整版本
* */
public void heap_4(int[] arr,int c,int length){
int i = arr[c];
for(int b=c*2+1;b<length;b=c*2+1){
b = b+1<length?arr[b] < arr[b+1]? b+1 : b : b;
if(arr[b] > i){
arr[c] = arr[b];
c = b;
}
}
arr[c] = i;
}
public void heap_5(){
int temp = 0;
//大頂堆的整理
for(int a=arr.length/2-1;a>=0;a--){
heap_4(arr,a,arr.length);
}
//進行交換
for(int b=arr.length;b>0;b--){
temp = arr[0];
arr[0] = arr[b-1];
arr[b-1] = temp;
  //從上至下進行整理,由於已是大頂堆,如今只須要進行對改變的依賴節點進行大頂堆處理

heap_4(arr,0,b-1);
}
}