美文网首页
堆与堆排序

堆与堆排序

作者: 小幸运Q | 来源:发表于2018-06-21 16:11 被阅读1次

    鸣谢:
    https://www.cnblogs.com/chengxiao/p/6129630.html
    https://www.jianshu.com/p/21bef3fc3030

    堆的定义:

    具有以下性质的完全二叉树(结构很紧凑适合数组):每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆。


    image.png

    从大小层面看:

    大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
    小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]

    从相对位置来说:

    (i+1)/ 2 - 1=其顶部节点的位置


    image.png

    第一个非叶子结点下标为 len/2-1.


    堆不能随意删除元素,只能删除根节点的元素。


    堆的删除:(自顶向下)

    删除堆顶元素然后替换为堆底元素:


    image.png

    length--然后进行下沉操作:

    image.png

    删除完成

    // 删除操作,摘掉顶点元素,然后用a[length-1]替换,再边比较边下沉。
    int del(struct Heap & heap){
      if(heap.length==0){
        return -1;
      }
      int pop=heap.heap[0];
      swap(heap.heap[0],heap.heap[heap.length-1]);
      heap.length--;
    
      if(heap.length==1){
          return pop;
      }
      else{
          int next=0;
          int root=0;
          // 让root遍历倒数第二层的节点,因为是完全二叉树,所以只有左右完整还有只有左子树两种情况。
          while(root<=(heap.length)/2-1){
            if(root*2+2<heap.length){
                if(heap.heap[root*2+1]>heap.heap[root*2+2]){
                  next=root*2+1;
                  if(next<heap.length){
                    // 右子树可能为null!!!!需要检查再替换
                    swap(heap.heap[next],heap.heap[root]);
                  }
                }
                else{
                  next=root*2+2;
                  if(next<heap.length){
                    // 右子树可能为null!!!!需要检查再替换
                    swap(heap.heap[next],heap.heap[root]);
                  }
                }
            }
            else if(heap.heap[root]>heap.heap[root*2+1]){
                next=root*2+1;
                swap(heap.heap[next],heap.heap[root]);
            }
            root=next;
          }
      }
      return pop;
    }
    
    

    堆的插入:(自底向上)

    image.png
    // 堆只需要满足a[i]>=a[i*2+1]&&a[i]>=a[i*2+2]
    void build_heap(struct Heap &heap,int num){
      // 不加 & 会无法修改
      int foot,next;
      heap.heap[heap.length++]=num;
      foot=heap.length-1;
      //cout<<"----------------";
      while(foot>0){
        next=(foot+1)/2-1;
        // 大顶堆
        if(heap.heap[next]<heap.heap[foot]){
          swap(heap.heap[next],heap.heap[foot]);
        }
        else{
          break;
        }
        foot=next;
      }
    };
    

    排序的思路:

    1. 无需序列构建成一个堆,将根据升序降序需求选择大顶堆或小顶堆
    2. 将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端
    3. 重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

    代码:

    1. 构建一个堆:
    // 从a[0]到a[length-1]逐个插入。
    void build_heap(struct Heap &heap,int num){
      int root,next;
      heap.heap[heap.length++]=num;
      root=heap.length-1;
      //cout<<"----------------";
      while(root>0){
        next=(root+1)/2-1;
        // 大顶堆
        if(heap.heap[next]<heap.heap[root]){
          swap(heap.heap[next],heap.heap[root]);
        }
        root=next;
      }
    }
    

    相关文章

      网友评论

          本文标题:堆与堆排序

          本文链接:https://www.haomeiwen.com/subject/vwkqyftx.html