堆排序

堆排序算法原理    

    堆排序算法,就是利用二叉树的原理,我们知道,对于二叉树而言,具有一定的排序性质:
        如   左节点是小于根节点的值,右节点的值肯定是大于根节点的值的,因此, 我们算是快能找到某一个元素
        因为如果当前元素比要找的元素要大,那么就往右走,如果当前元素比要找的元素要小,那么往右找,呵呵,相
        信如何树上存在这号元素的话,应该很快就能找到。
    
     引申到堆排序
            那么堆就是一棵完全“二叉树”,只是形似而己了。
            
                

根据以上性质,因为要结点是整个树中最大(小)的元素,那么如果给我一棵二叉树,就可以很得到它的最大(小)值啰。
想想,如果我们每次建立一个堆后,将它的根拿出来,然后用剩下的元素再建一棵二叉树,那是不是就可以排序了呢?
因为第一次建立一棵二叉树,然后拿到它的根,然后将剩下的元素再建一棵二叉树,那么现在的根就是上一次剩下的元素中间
最大(小)的元素了呢。嗯。这是肯定的。依次下去,拿出的根结点元素就己经排好序了啊。
那废话不多说,如何实现呢?看代码得了:
  主代码:
  1. #ifndef HEAP_SORT_H
  2. #define HEAP_SORT_H
  3. #include<iostream>
  4. #include<string>
  5. template<class Type>
  6. class HeapSort{
  7. private:
  8. Type *ptr;
  9. int Length;
  10. int HeapSize;
  11. public:
  12. int rightHeap(int i){
  13. return 2*i+1;
  14. }
  15. int leftHeap(int i){
  16. return 2*i+2;
  17. }
  18. HeapSort(Type *p,int Len){
  19. this->ptr=p;
  20. this->Length=Len;
  21. this->HeapSize=Len;
  22. }
  23. void maxHeapy(int idx){
  24. int right=rightHeap(idx);
  25. int left=leftHeap(idx);
  26. int maxIdx=idx;
  27. if(right<HeapSize&&ptr[right]>ptr[idx]){
  28. maxIdx=right;
  29. }
  30. if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
  31. maxIdx=left;
  32. }
  33. if(maxIdx!=idx){
  34. Type temp=ptr[idx];
  35. ptr[idx]=ptr[maxIdx];
  36. ptr[maxIdx]=temp;
  37. maxHeapy(maxIdx);
  38. }
  39. }
  40. void buildMaxHeapy(){
  41. for(int i=HeapSize/2+2; i>=0;i--){
  42. maxHeapy(i);
  43. }
  44. }
  45. void heapSort(){
  46. for(int i=Length-1;i>0;i--){
  47. buildMaxHeapy(); //建立堆
  48. Type temp=ptr[i];
  49. ptr[i]=ptr[0];
  50. ptr[0]=temp;
  51. --HeapSize;
  52. }
  53. }
  54. };
  55. #endif
  代码部分解释:
    
  1. int rightHeap(int i){
  2. return 2*i+1;
  3. }
  4. int leftHeap(int i){
  5. return 2*i+2;
  6. }
上面的代码的主要作用是求左右孩子了啊,那么为什么是2*i+1和2*i+2而不是2*i和2*i+1,  
问得好,我也是曾经在这上面栽过跟头的啊,首先我们的下标是从0开始啦 ,那么如果下标i取0的时候,那是不是2*i=0,也就是孩子和根的下标相等了,那是
不行的。代码就有BUG了。但是如果对一些下标从1开始的数组,那么2*i和2*i+1也是可以的。

  1. void maxHeapy(int idx){
  2. int right=rightHeap(idx);
  3. int left=leftHeap(idx);
  4. int maxIdx=idx;
  5. if(right<HeapSize&&ptr[right]>ptr[idx]){
  6. maxIdx=right;
  7. }
  8. if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
  9. maxIdx=left;
  10. }
  11. if(maxIdx!=idx){
  12. Type temp=ptr[idx];
  13. ptr[idx]=ptr[maxIdx];
  14. ptr[maxIdx]=temp;
  15. maxHeapy(maxIdx);
  16. }
  17. }
上面代码的主要作用就是对某一个堆的下票idx节点,我们要求它下面的节点维护“堆”的性质,如何维护呢?无非就是使根、右,左孩子
三个中,使得根节点取得它们中的最大值啰。哈哈。。好简单。,嗯。。还是有些小问题。可能是我太笨。
  
  1. if(right<HeapSize&&ptr[right]>ptr[idx]){
  2. maxIdx=right;
  3. }
  4. if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
  5. maxIdx=left;
  6. }
不是
  1. if(right<HeapSize&&ptr[right]>ptr[idx]){
  2. maxIdx=right;
  3. }
  4. if(left<HeapSize&&ptr[left]>ptr[idx]){
  5. maxIdx=left;
  6. }
好好想想两者的差别。这里搞不对,问题就大。当然
  1. if(maxIdx!=idx){
  2. Type temp=ptr[idx];
  3. ptr[idx]=ptr[maxIdx];
  4. ptr[maxIdx]=temp;
  5. maxHeapy(maxIdx);
  6. }
这里的是因为你调换了其中的两个元素,可能以前就只有这两个元素破坏了堆的性质,其它的都 可以,但下因为你的调换使得调换后下面的堆
性质又破坏了,因此,还是得下维护其性质啊。
在上面的代码中,还存在一非常关键的问题:
  1. if(right<HeapSize&&ptr[right]>ptr[idx]){
  2. maxIdx=right;
  3. }
  4. if(left<HeapSize&&ptr[left]>ptr[maxIdx]){
  5. maxIdx=left;
  6. }
这是正好限制了下标的取值范围。

 
  1. void buildMaxHeapy(){
  2. for(int i=HeapSize/2; i>=0;i--){
  3. maxHeapy(i);
  4. }
  5. }
为什么是for(int i=HeapSize/2; i>=0;i--),而不是for(int i=HeapSize; i>=0;i--)呢?
   因为

也就是能省就省吧。反正你浪费也没办法。

  1. void heapSort(){
  2. for(int i=Length-1;i>0;i--){
  3. buildMaxHeapy(); //建立堆
  4. Type temp=ptr[i];
  5. ptr[i]=ptr[0];
  6. ptr[0]=temp;
  7. --HeapSize;
  8. }
就是每次把最大(小)的元素拿出来啦,然后缩短堆大小,再建堆。。。。。。。。


所以堆排序算法的主要步步骤:

左右孩子问题->  维护最大堆性质  ->建堆->堆排序





    




原文地址:https://www.cnblogs.com/yml435/p/4655561.html