一、堆的概念
一个堆必定是一个完全二叉树,
这又说到了什么是二叉树,它分为 完全二叉树和不完全二叉树两种。
- 大根堆:任何一个节点店,都是以他为父节点的树的最大值
- 小根堆:任何一个节点店,都是以他为父节点的树的最小值
- 规律: 某个下标是 i 的节点,它的
左孩子:2*i+1
右孩子:2*i+2
父节点:(i-1)/2 向下取整
;
二、堆的 heapInsert 过程
-----------------heapInsert往上的过程-----------------
- 新进加来的数,现在停在了index位置,请依次往上移动
- 移动到0位置,或者干不掉自己的父亲了,停
int heapInsert(int arr[],int index){
// 进来的 [index] 他父亲[(index-1)/2]
// 但是当index==0 的时候,这个条件就不会成立了,因为你永远不会比你你自己大 0 的父节点 0
// 当 index位小于父亲的时候,条件也不成立,使以说这个条件是关键。
while(arr[index]>arr[(index-1)/2]){
swap(arr[index],arr[(index-1)/2]);
index= (index-1)/2;
}
};
三、堆的 heapify 过程
-----------------heapify 往下-----------------
- 从index位置,往下看,不断的下沉
- 停:较大的那个孩子都没有index位置的数大;或者是index已经没有孩子了
int heapify(int arr[],int index,int heapSize){
int left= index*2+1;
//heapSize 表示队列的长度大小
while(left <= heapSize){ // 如果有左孩子,此时可能有右孩子,也可能没有
// 把较大孩子的下表,给largest
int largest = left+1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if(largest == index){
break;
}
swap(arr[largest],arr[index]);
index= largest;
left= index* 2+ 1;
}
};
四、现在我有一个堆整挺好的,突然某个index的数变了,不知道它是变大了还是变小了。
** 这时候怎么复原呢???**
执行一次 heapInsert()
,再执行一次heapify()
,顺序调用就完了,其实他们只发生一个!
** 通过这个方式堆一定能重新整好 ** ** 接下来可以讲堆排序了 **
五、堆排序
堆排思路分析
- 无序的数组进来,共N个元素
- 每进来一个数,执行一次 heapInsert 上浮,变成大根堆,此时heapSize = N-1
- 0位上的数 和 N-1 上的数 交换,然和 heapSize-1
- 此时再执行一次heapify 下沉过程,又变成了大根堆,
- 然后不断去重复 3 4过程,直到heapSize>0。
C++具体代码:
#include<bits/stdc++.h>
using namespace std;
// -----------------heapInsert 往上——————————
// 新进加来的数,现在停在了index位置,请依次往上移动
// 移动到0位置,或者干不掉自己的父亲了,停
int heapInsert(int arr[],int index){
// 进来的 [index] 他父亲[(index-1)/2]
// 但是当index==0 的时候,这个条件就不会成立了,因为你永远不会比你你自己大 0 的父节点 0
// 当 index位小于父亲的时候,条件也不成立,使以说这个条件是关键。
while(arr[index]>arr[(index-1)/2]){
swap(arr[index],arr[(index-1)/2]);
index= (index-1)/2;
}
};
// -----------------heapify 往下——————————
// 从index位置,往下看,不断的下沉
// 停:较大的那个孩子都没有index位置的数大;或者是index已经没有孩子了
int heapify(int arr[],int index,int heapSize){
int left= index*2+1;
//heapSize 表示队列的长度大小
while(left <= heapSize){ // 如果有左孩子,此时可能有右孩子,也可能没有
// 把较大孩子的下表,给largest
int largest = left+1 < heapSize && arr[left+1] > arr[left] ? left+1 : left;
largest = arr[largest] > arr[index] ? largest : index;
if(largest == index){
break;
}
swap(arr[largest],arr[index]);
index= largest;
left= index* 2+ 1;
}
};
// -----------------堆排——————————
int heapSort(int arr[],int length){
//1.给我一个数组,建立大根堆
if(length< 2){ //为啥 arr[]== null || sizeof(arr)< 2 报错
return 0;
}
for(int i=0; i<length;i++){ //O(N)
heapInsert(arr,i); //O(logN)
}
//2.heapSize一定是数组大小
int heapSize= length-1;
//3.0位置和heapSize(堆上最后一个数)交换,然后 heapSize-1
swap(arr[0],arr[heapSize]);
heapSize--; //全局最大值换到了N-1位,而且我还把它断开了
//复杂度 O(N*logN)
while(heapSize>0 ){ //O(N)
heapify(arr,0,heapSize); //O(logN)
swap(arr[0],arr[heapSize]); //O(1)
heapSize--;
}
};
int main()
{
int arr[6]={8,25,43,65,3,7};
heapSort(arr,6);
for(int m=0; m<=5;m++){
cout<<arr[m]<<endl;
}
cout<<"hhhhh"<<endl;
return 0;
}