zoukankan      html  css  js  c++  java
  • 数据结构算法Review1_堆

    首先堆的本质是一个棵完全二叉树

    什么是二叉树、满二叉树、完全二叉树?见链接

    树、二叉树(满二叉树、完全二叉树)概念:https://blog.csdn.net/sinat_41144773/article/details/89530403

    最大堆(大顶堆):顶结点最大,父亲结点比孩子结点大;

    最小堆(小顶堆):顶结点最小,父亲结点比孩子结点小;

    堆一般都是vecotor或者数组作为实际存储的结构;

    由于是完全二叉树,所以父亲结点和孩子结点的索引存在一些关系。

    假设我们的堆是用数组存放的;i表示数组索引;

    为了方便计算,i=0下标不存放任何数据。从i=1下标开始存放数据。

    parent(i) = i/2;          //加入孩子结点的索引是i,父亲结点索引是i/2;

    leftchild(i) = i*2;       // i是父亲结点的索引

    rightchild(i)=i*2+1;   //i*2+1<=count; count是总结点数;

    如果是i=0也存放数据的话。处理起来会麻烦一些。

    parent(i) = (i-1)/2;

    leftchild(i) = 2*i+1;

    rightchild(i) = 2*i+2;   //i*2+2<=count-1; count是总结点数;

    上面这些规律完全不用记,需要的时候画一个完全二叉树,简单推算一下即可。

    接下来讲讲核心思路:

    堆有三个核心操作: //方便起见,这里以最大堆为例

    1、堆排序:给定一个数组或vector,如何把这个数组或vector变成一个最大堆?

      这个过程是堆化的过程,或者是建堆的过程;

    2、取出堆顶元素

        堆顶元素取出来后,如何继续保证这个堆是一个最大堆?

        思路就是将堆的最后一个节点的值(即完全二叉树的最后一层,从左往右数最后一个节点)与堆顶节点的值进行交换;

        该节点跑到的堆顶,之后就是不断向下,使用shiftdown操作。将该节点元素不断往下交换,直到寻找到合适的位置,以维持最大堆的定义;

    3、插入一个新元素

        一般插入到队尾,然后不断向上,即使用shiftup操作。往上进行交换操作,也是直到寻找到合适的位置,以维持最大堆的定义;

    实际上核心过程就是shiftdown、shiftup、和建堆;

    建堆(堆排序)时间复杂度,可参考链接:

    建堆的时间复杂度计算:https://blog.csdn.net/wangqing_199054/article/details/20461877

    自下而上建堆的时间复杂度:https://www.cnblogs.com/zuotongbin/p/10702001.html

    【算导】建堆过程时间复杂度为O(n):https://www.jianshu.com/p/f2ab13f951c9

     

      1 #include <iostream>
      2 #include <cassert>
      3 #include <queue>
      4 #include <stack>
      5 
      6 using namespace std;
      7 
      8 template <typename Item>
      9 class MaxHeap{
     10 private:
     11     Item* data;
     12     int count;
     13     int capacity;
     14 
     15     //往堆中插入新元素,一般插入到队尾
     16     //需要shiftUp来维持堆的定义
     17     void shiftUp(int k){
     18         while(k>1 && data[k/2]<data[k]){
     19             swap(data[k/2],data[k]);
     20             k = k/2;
     21         }
     22     }
     23 
     24     //优化后
     25     void shiftUp_Opt(int k){
     26         Item e = data[k];
     27         while(k>1 && data[k/2]<data[k]){
     28             data[k] = data[k/2];
     29             k /= 2;
     30         }
     31         data[k] = e;
     32     }
     33 
     34     //取出最大堆,堆顶元素后,需要shiftdown维持堆的定义。
     35     //将一个元素不断往下,直至找到合适的位置,维持堆的定义。
     36     void shiftDown(int k){
     37         while(2*k<=count){
     38             //j为要与k交换的元素编号
     39             int j = 2*k;
     40             //右孩子存在,且比左孩子大
     41             if(j+1<=count && data[j+1]>data[j])
     42                 j++;
     43             if(data[j]<data[k]) break; //如果交换到头了,就直接break
     44             swap(data[j],data[k]);
     45             k = j; //更新k,继续往下一轮的交换
     46         }
     47     }
     48 
     49     //优化后
     50     void shiftDown_Opt(int k){
     51         Item e = data[k];
     52         while(2*k<=count){
     53             int j = 2*k;
     54             if(j+1<=count && data[j+1]>data[j]) j++;
     55             if( e > data[j]) break;
     56             data[k] = data[j];
     57             k = j;
     58         }
     59         data[k] = e;
     60     }
     61 
     62 
     63 public:
     64     //构造一个空堆
     65     MaxHeap(int capacity){
     66         data = new Item[capacity+1];
     67         count = 0;
     68         this->capacity = capacity;
     69     }
     70 
     71     //构造函数,给定一个数组构造一个最大堆
     72     MaxHeap(Item arr[], int n){
     73         data = new Item[n+1];
     74         capacity = n;
     75 
     76         for(int i = 0; i<n; i++)
     77             data[i+1] = arr[i];
     78         count = n;
     79 
     80         //堆化核心代码
     81         for(int i = count/2; i>=1; i--)
     82             shiftDown(i);
     83 
     84     }
     85 
     86     ~MaxHeap(){
     87         delete[] data;
     88     }
     89 
     90     //往堆中插入元素
     91     void insert(Item item){
     92         assert(count+1<=capacity);
     93         data[count+1] = item;
     94         shiftUp(count+1);
     95         count++;
     96     }
     97 
     98     //取出最大元素
     99     Item extractMax(){
    100         assert(count >0);
    101         Item ret = data[1];
    102         swap(data[1], data[count]);
    103         count --;
    104         shiftDown(1);
    105         return ret;
    106     }
    107 
    108     //堆是否为空
    109     bool isEmpty(){
    110         return count == 0;
    111     }
    112 
    113     //堆中元素个数
    114     int size(){
    115         return count;
    116     }
    117 
    118 
    119 };
    120 
    121 //普通版本的堆排序,将所有元素依次添加到堆中,然后再将所有元素依次从堆中取出,即完成了排序;
    122 //无论是创建堆的过程还是从堆中依次取出元素的过程,时间复杂度均为O(nlogn)
    123 //整个堆排序的整体时间复杂度为(nlogn)
    124 template <typename T>
    125 void heapSort1(T arr[], int n){
    126     MaxHeap<T> maxHeap = MaxHeap<T>(n);
    127     for(int i = 0; i<n; i++)
    128         maxHeap.insert(arr[i]);
    129     for(int i = n-1; i>=0; i--)
    130         arr[i] = maxHeap.extractMax();
    131 }
    132 
    133 //借助heapify进行堆排序
    134 template <typename T>
    135 void heapSort2(T arr[],int n){
    136     MaxHeap<T> maxHeap = MaxHeap<T>(arr,n);
    137     for(int i = n-1; i>=0; i--)
    138         arr[i] = maxHeap.extractMax();
    139 }
  • 相关阅读:
    tyvj 1031 热浪 最短路
    【bzoj2005】 [Noi2010]能量采集 数学结论(gcd)
    hdu 1394 Minimum Inversion Number 逆序数/树状数组
    HDU 1698 just a hook 线段树,区间定值,求和
    ZeptoLab Code Rush 2015 C. Om Nom and Candies 暴力
    ZeptoLab Code Rush 2015 B. Om Nom and Dark Park DFS
    ZeptoLab Code Rush 2015 A. King of Thieves 暴力
    hdoj 5199 Gunner map
    hdoj 5198 Strange Class 水题
    vijos 1659 河蟹王国 线段树区间加、区间查询最大值
  • 原文地址:https://www.cnblogs.com/grooovvve/p/12365752.html
Copyright © 2011-2022 走看看