数据结构算法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 }
原文地址:https://www.cnblogs.com/grooovvve/p/12365752.html