• 堆排序


    堆排序算法原理    

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

    根据以上性质,因为要结点是整个树中最大(小)的元素,那么如果给我一棵二叉树,就可以很得到它的最大(小)值啰。
    想想,如果我们每次建立一个堆后,将它的根拿出来,然后用剩下的元素再建一棵二叉树,那是不是就可以排序了呢?
    因为第一次建立一棵二叉树,然后拿到它的根,然后将剩下的元素再建一棵二叉树,那么现在的根就是上一次剩下的元素中间
    最大(小)的元素了呢。嗯。这是肯定的。依次下去,拿出的根结点元素就己经排好序了啊。
    那废话不多说,如何实现呢?看代码得了:
      主代码:
    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. }
    就是每次把最大(小)的元素拿出来啦,然后缩短堆大小,再建堆。。。。。。。。


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

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





        




  • 相关阅读:
    构建一个应用,并在docker compose里运行起来
    docker engine docker-compose部署
    django 返回数据的几种常用姿势
    fiddler+httprunner 零编码实现接口自动化DEMO
    选择排序
    曾经学的那些表示时间复杂度的公式怎么来的?
    python+Airtest+android使用AirtestIDE编写一个DEMO
    怎么计算时间复杂度?
    算法_冒泡排序python+java实现
    2020年1月16日(多进程)
  • 原文地址:https://www.cnblogs.com/yml435/p/4655561.html
Copyright © 2020-2023  润新知