堆排序算法原理
堆排序算法,就是利用二叉树的原理,我们知道,对于二叉树而言,具有一定的排序性质:
如 左节点是小于根节点的值,右节点的值肯定是大于根节点的值的,因此, 我们算是快能找到某一个元素
因为如果当前元素比要找的元素要大,那么就往右走,如果当前元素比要找的元素要小,那么往右找,呵呵,相
信如何树上存在这号元素的话,应该很快就能找到。
引申到堆排序
那么堆就是一棵完全“二叉树”,只是形似而己了。
根据以上性质,因为要结点是整个树中最大(小)的元素,那么如果给我一棵二叉树,就可以很得到它的最大(小)值啰。
想想,如果我们每次建立一个堆后,将它的根拿出来,然后用剩下的元素再建一棵二叉树,那是不是就可以排序了呢?
因为第一次建立一棵二叉树,然后拿到它的根,然后将剩下的元素再建一棵二叉树,那么现在的根就是上一次剩下的元素中间
最大(小)的元素了呢。嗯。这是肯定的。依次下去,拿出的根结点元素就己经排好序了啊。
那废话不多说,如何实现呢?看代码得了:
主代码:
#ifndef HEAP_SORT_H
#define HEAP_SORT_H
#include<iostream>
#include<string>
template<class Type>
class HeapSort{
private:
Type *ptr;
int Length;
int HeapSize;
public:
int rightHeap(int i){
return 2*i+1;
}
int leftHeap(int i){
return 2*i+2;
}
HeapSort(Type *p,int Len){
this->ptr=p;
this->Length=Len;
this->HeapSize=Len;
}
void maxHeapy(int idx){
int right=rightHeap(idx);
int left=leftHeap(idx);
int maxIdx=idx;
if(right<HeapSize&&ptr[right]>ptr[idx]){
maxIdx=right;
}
if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
maxIdx=left;
}
if(maxIdx!=idx){
Type temp=ptr[idx];
ptr[idx]=ptr[maxIdx];
ptr[maxIdx]=temp;
maxHeapy(maxIdx);
}
}
void buildMaxHeapy(){
for(int i=HeapSize/2+2; i>=0;i--){
maxHeapy(i);
}
}
void heapSort(){
for(int i=Length-1;i>0;i--){
buildMaxHeapy(); //建立堆
Type temp=ptr[i];
ptr[i]=ptr[0];
ptr[0]=temp;
--HeapSize;
}
}
};
#endif
代码部分解释:
int rightHeap(int i){
return 2*i+1;
}
int leftHeap(int i){
return 2*i+2;
}
上面的代码的主要作用是求左右孩子了啊,那么为什么是2*i+1和2*i+2而不是2*i和2*i+1,
问得好,我也是曾经在这上面栽过跟头的啊,首先我们的下标是从0开始啦 ,那么如果下标i取0的时候,那是不是2*i=0,也就是孩子和根的下标相等了,那是
不行的。代码就有BUG了。但是如果对一些下标从1开始的数组,那么2*i和2*i+1也是可以的。
void maxHeapy(int idx){
int right=rightHeap(idx);
int left=leftHeap(idx);
int maxIdx=idx;
if(right<HeapSize&&ptr[right]>ptr[idx]){
maxIdx=right;
}
if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
maxIdx=left;
}
if(maxIdx!=idx){
Type temp=ptr[idx];
ptr[idx]=ptr[maxIdx];
ptr[maxIdx]=temp;
maxHeapy(maxIdx);
}
}
上面代码的主要作用就是对某一个堆的下票idx节点,我们要求它下面的节点维护“堆”的性质,如何维护呢?无非就是使根、右,左孩子
三个中,使得根节点取得它们中的最大值啰。哈哈。。好简单。,嗯。。还是有些小问题。可能是我太笨。
if(right<HeapSize&&ptr[right]>ptr[idx]){
maxIdx=right;
}
if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
maxIdx=left;
}
不是
if(right<HeapSize&&ptr[right]>ptr[idx]){
maxIdx=right;
}
if(left<HeapSize&&ptr[left]>ptr[idx]){
maxIdx=left;
}
好好想想两者的差别。这里搞不对,问题就大。当然
if(maxIdx!=idx){
Type temp=ptr[idx];
ptr[idx]=ptr[maxIdx];
ptr[maxIdx]=temp;
maxHeapy(maxIdx);
}
这里的是因为你调换了其中的两个元素,可能以前就只有这两个元素破坏了堆的性质,其它的都 可以,但下因为你的调换使得调换后下面的堆
性质又破坏了,因此,还是得下维护其性质啊。
在上面的代码中,还存在一非常关键的问题:
if(right<HeapSize&&ptr[right]>ptr[idx]){
maxIdx=right;
}
if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
maxIdx=left;
}
这是正好限制了下标的取值范围。
void buildMaxHeapy(){
for(int i=HeapSize/2; i>=0;i--){
maxHeapy(i);
}
}
为什么是for(int i=HeapSize/2; i>=0;i--),而不是for(int i=HeapSize; i>=0;i--)呢?
因为
也就是能省就省吧。反正你浪费也没办法。
void heapSort(){
for(int i=Length-1;i>0;i--){
buildMaxHeapy(); //建立堆
Type temp=ptr[i];
ptr[i]=ptr[0];
ptr[0]=temp;
--HeapSize;
}
就是每次把最大(小)的元素拿出来啦,然后缩短堆大小,再建堆。。。。。。。。