最近看树比较多,总结一下堆的算法吧,
堆得定义:(个人觉得和二叉排序树很像,只是堆可以看做是一颗完全二叉树)
n个关键字序列Kl,K2,…,Kn称为(Heap),当且仅当该序列满足如下性质:
ki<=k(2i)且ki<=k(2i+1)(1≤i≤ n/2),当然,这是小根堆,大根堆则换成>=号
树中任一非叶子结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。
![](https://i-blog.csdnimg.cn/blog_migrate/d865386ccba937bd515d2d1a7b07c46a.jpeg)
既然是堆呢,那么首先考虑的肯定如何构建堆(以大根堆为列):
根据完全二叉树的性质我们可以知道对于一个有n个节点的树,有[n/2](取上整数)个节点是叶子节点,所以构建时字需要对前n/2个节点进行递归维护就是了,
维护就是让这颗树保持堆的性质,其步奏为:
- 比较当前节点与其左子树节点的大小,如果小于则记录,
- 比较当前节点和右子树的大小,如果小于则记录,
- 如果前根节点需要交换,则交换节点值,并对交换的子树进行同样的维护操作;
根据这个思路代码也就不难了:
void HeapAdjust(int *A, int i, int HeapSize){
int lchild= i*2;
int rchild= i*2+1;
int maxi= i;
if(lchild<=HeapSize && A[i] < A[lchild])
maxi= lchild;
if(rchild<=HeapSize&& A[maxi] < A[rchild] )
maxi = rchild;
if(maxi != i ){
int tmp = A[i];
A[i]= A[maxi];
A[maxi]= tmp;
HeapAdjust(A, maxi, HeapSize);
}
}
于是呢构建堆得代码就有很好写了:
void bulidmaxheap(int *A, int n){
for(int i=n/2;i>=1;i--)
HeapAdjust(A, i, n);
}
堆排序呢也就更简单了,应为我们已经把大根堆构建出来了,所以第一个节点就是最大元素咯, 所以排序的过程就是: 将第一个节点的值和最后一个节点的值交换 维护现有堆 进行第一步一直到堆得size为0; 根据这样的代码就有了下面的代码: void HeapSort(int *A, int n){
bulidmaxheap(A, n);
for(int i=n;i>=1;i--){
int tmp= A[i];
A[i]= A[1];
A[1]= tmp;
HeapAdjust(A,1,i-1);
}
}
为了方便树对孩子节点的计算,我们在数组中的元素,下标都是从1开始;
下面是测试:
<pre name="code" class="cpp">int main()
{
int A[10]={0,1,8,5,6,9,7,2,3,4};
cout<< "原数组:"<<endl;
for(int i=0;i<10;i++)
cout<< A[i] << " ";
cout<<endl;
bulidmaxheap(A,9);
cout<< "大根堆数组:"<<endl;
for(int i=0;i<10;i++)
cout<< A[i] << " ";
cout<<endl;
HeapSort(A, 9);
cout<< "堆排序后的数组:"<<endl;
for(int i=0;i<10;i++)
cout<< A[i] << " ";
cout<<endl;
return 0;
}
应为第一个元素没有用到,随便赋了个0值,无影响,测试结果为:
在顺便说一下堆得应用,就是游侠你队列,priority_queue
在头文件queue里,直接定义一个优先级关系就可以调用了,感觉特别方便;