数据结构—堆


 

堆也被称为优先队列,就是一棵完全二叉树。

  队列中允许的操作是先进先出(FIFO),在队尾插入元素,在队头取出元素。而堆也是一样,在堆底插入元素,在堆顶取出元素,但是堆中元素的排列不是按照到来的先后顺序,而是按照一定的优先顺序排列的。这个优先顺序可以是元素的大小或者其他规则。所以堆又分为最大堆与最小堆,或者最大完全二叉树与最小完全二叉树。

  最大堆:堆中每一个节点的值都大于或等于其子节点的值。

  最小堆:堆中每一个节点的值都小于或等于其子节点的值。

  堆可以看成一个完全二叉树,所以可以考虑使用二叉树的表示方法来表示堆。但是因为堆中元素按照一定的优先顺序排列,因此可以使用更简单的方法——动态数组——来表示,这样可以节省子节点指针空间,并且可以快速访问每个节点。

 1 /********************************************
 2     堆结构(最大)完全二叉树类模板
 3     数组实现
 4     2018/3/22
 5 *********************************************/
 6 
 7 #pragma once
 8 #include <iostream>
 9 #include <cmath>
10 
11 template<class T>
12 class MyHeap
13 {
14 public:
15     MyHeap();
16     ~MyHeap();
17     MyHeap(const MyHeap& val);
18     void clear();            //清空堆
19     void _sizeExpand();        //判断堆是否已满,若是,则进行扩容
20     void push(const T& srcData);    //在树尾部插入,再进行交换
21     void initHeap(const T arr[], int len);
22     T deleteHeap();
23 
24     void prePrint(int index = 0)const;
25     size_t size()const;
26     T *getHeapRoot();
27     
28 
29 private:
30     T *pRoot;
31     size_t len;
32     size_t maxSize;
33 
34 };

下面,以最大堆为例,说明堆的初始化、插入、删除等操作。

插入

堆的插入步骤:

  1. 将新元素增加到堆的末尾;
  2. 按照优先顺序,将新元素与其父节点比较,如果新元素小于父节点则将两者交换位置;
  3. 不断进行第2步操作,直到不需要交换新元素和父节点,或者达到堆顶;
  4. 最后通过得到一个最大堆。 

通过将新元素与父节点从下向上调整的操作,叫做上滤。

 

  1)添加66到堆中           1)交换66和5            1)交换66和9           1)交换66和10

初始化

1.首先按照原始数据的位置依次加入到堆中;

2.然后,从最后一个有子节点的位置开始比较,看是否满足最大堆的规则,如果不满足往下交换,直到满足规则达到叶节点或者到达叶节点;

      

删除

堆的删除操作与插入操作相反,插入操作从下往上调整堆,而删除操作则从上往下调整堆。

  1. 删除堆顶元素(通常是将堆顶元素放置在数组的末尾)
  2. 比较左右子节点,将小的元素上调。
  3. 不断进行步骤2,直到不需要调整或者调整到堆底。

上述调整的方法称为下滤(percolate down)。 

       

 堆排序

  堆排序是利用堆这种数据结构而设计的一种排序算法,堆排序是一种选择排序,它的最坏,最好,平均时间复杂度均为O(nlogn),它也是不稳定排序。

所以在已有数据已构成堆得情况下,采用堆排序不失为一种好的选择。

堆排序的基本思想是:将待排序序列构造成一个最大堆,此时,整个序列的最大值就是堆顶的根节点。从堆中删除数据,并以此保存删除的数据(删除函数返回值),就能得到一个有序序列。

 

C++实现:

[MyHeap.h]

 

  1 /****************************************
  2     堆结构(最大)完全二叉树类模板
  3     数组实现
  4     2018/3/22
  5     ****************************************/
  6 
  7 #pragma once
  8 #include <iostream>  9 #include <cmath>
 10 
 11 template<class T>
 12 class MyHeap
 13 {
 14 public:
 15     MyHeap();
 16     ~MyHeap();
 17     MyHeap(const MyHeap& val);
 18     void clear();            //清空堆
 19     void _sizeExpand();        //判断堆是否已满,若是,则进行扩容
 20     void push(const T& srcData);    //在树尾部插入,再进行交换
 21     void initHeap(const T arr[], int len);
 22     T deleteHeap();
 23 
 24     void prePrint(int index = 0)const;
 25     size_t size()const;
 26     T *getHeapRoot();
 27     
 28 
 29 private:
 30     T *pRoot;
 31     size_t len;
 32     size_t maxSize;
 33 
 34 };
 35 
 36 template<class T>
 37 T * MyHeap<T>::getHeapRoot()
 38 {
 39     return pRoot;
 40 }
 41 
 42 template<class T>
 43 size_t MyHeap<T>::size()const
 44 {
 45     return len;
 46 }
 47 
 48 template<class T>
 49 void MyHeap<T>::prePrint(int index /*= 0*/)const
 50 {
 51     if (index < (int)len && index >= 0)
 52     {
 53         cout << pRoot[index] << "  ";
 54         prePrint(2 * index + 1);
 55         prePrint(2 * index + 2);
 56     }
 57 }
 58 
 59 template<class T>
 60 void MyHeap<T>::clear()
 61 {
 62     if (pRoot)
 63         delete[]pRoot;
 64     pRoot = nullptr;    //内存释放后 指针置空
 65     len = maxSize = 0;
 66 }
 67 
 68 
 69 template<class T>
 70 MyHeap<T>::MyHeap(const MyHeap& val)
 71 {
 72     len = val.len;
 73     maxSize = val.maxSize;
 74     pRoot = NULL;
 75     if (len)
 76     {
 77         pRoot = new T[len];
 78         memmove_s(pRoot, len*sizeof(T), val.pRoot, len*sizeof(T));
 79     }
 80 }
 81 
 82 
 83 template<class T>
 84 MyHeap<T>::MyHeap()
 85 {
 86     pRoot = nullptr;
 87     len = maxSize = 0;
 88 }
 89 
 90 template<class T>
 91 MyHeap<T>::~MyHeap()
 92 {
 93     clear();
 94 }
 95 
 96 template<class T>
 97 T MyHeap<T>::deleteHeap()
 98 {
 99     //最大堆删除数据 总是从根节点删除,在把堆中最后一个数拿到根节点,在把根节点往下进行交换
100     if (len==0)
101         throw "there is no data in the heap";
102     T returnVal = pRoot[0];        //删除的元素作为返回值,用于堆排序
103     pRoot[0] = pRoot[len - 1];    //堆中最后一个数放到根节点
104     len--;        //删除一个数
105 
106     int index = 0;
107     while (true)    
108     {
109         int leftChild = 2 * index + 1;
110         int rightChild = 2 * index + 2;
111         if (leftChild >= (int)len)    //没有左子节点
112             break;
113         else if (rightChild >= (int)len)    //有左子节点 没右子节点
114         {
115             if (pRoot[index]<pRoot[leftChild])
116             {
117                 T temp = pRoot[leftChild];
118                 pRoot[leftChild] = pRoot[index];
119                 pRoot[index] = temp;
120                 index = leftChild;
121             }
122             else
123                 break;
124         }
125         else       //有左子节点 也有右子节点
126         {
127             if (pRoot[leftChild] > pRoot[rightChild])
128             {
129                 if (pRoot[index] < pRoot[leftChild])
130                 {
131                     T temp = pRoot[leftChild];
132                     pRoot[leftChild] = pRoot[index];
133                     pRoot[index] = temp;
134                     index = leftChild;
135                 }
136                 else
137                     break;
138             }
139             else
140             {
141                 if (pRoot[index] < pRoot[rightChild])
142                 {
143                     T temp = pRoot[rightChild];
144                     pRoot[rightChild] = pRoot[index];
145                     pRoot[index] = temp;
146                     index = rightChild;
147                 }
148                 else
149                     break;
150             }
151         }    
152     }
153     return returnVal;
154 }
155 
156 template<class T>
157 void MyHeap<T>::initHeap(const T arr[], const int length)
158 {
159     clear();
160     len = maxSize = length;
161     pRoot = new T[maxSize];
162     if (len)
163         memmove_s(pRoot, len*sizeof(T), arr, len*sizeof(T));    //将数组中的元素拷贝到堆中,再进行交换
164     else
165         return;
166     for (int i = (len - 1) >> 1; i >= 0; --i)
167     {
168         int index = i;        //找到最后一个有子节点的下标
169         while (true)    //往下交换 
170         {//1.没有左节点 结束循环  2.只有左子节点 3.有右节点 且左节点大 4.右右节点 且右节点大  
171             int leftChild = 2 * index + 1;
172             int rightChild = 2 * index + 2;
173             if (leftChild >= (int)len) //没有左子节点 结束循环
174                 break;
175             else if (rightChild >= (int)len)    // 有左节点 没有右节点
176             {
177                 if (pRoot[index] < pRoot[leftChild])
178                 {
179                     T temp = pRoot[index];
180                     pRoot[index] = pRoot[leftChild];
181                     pRoot[leftChild] = temp;
182                     index = leftChild;
183                 }
184                 else
185                     break;
186             }
187             else  //有左节点 也有右节点  ,左右节点中较大的 和当前节点比较
188             {
189                 if (pRoot[leftChild] > pRoot[rightChild])    //左节点大
190                 {
191                     if (pRoot[index] < pRoot[leftChild])
192                     {
193                         T temp = pRoot[index];
194                         pRoot[index] = pRoot[leftChild];
195                         pRoot[leftChild] = temp;
196                         index = leftChild;        //当前节点和 左子节点后,还需要比较左子节点及其子节点大小
197                     }
198                     else
199                         break;  //若当前节点大于 左右子节点中最大值
200                 }
201                 else    //右节点大
202                 {
203                     if (pRoot[index] < pRoot[rightChild])
204                     {
205                         T temp = pRoot[index];
206                         pRoot[index] = pRoot[rightChild];
207                         pRoot[rightChild] = temp;
208                         index = rightChild;        //当前节点和 右子节点后,还需要比较右子节点及其子节点大小
209                     }
210                     else
211                         break;  //若当前节点大于 左右子节点中最大值
212                 }
213             }
214         }
215     }
216 }
217 
218 template<class T>
219 void MyHeap<T>::_sizeExpand()
220 {
221     if (len >= maxSize)
222     {
223         maxSize = maxSize + ((maxSize >> 1) > 1 ? (maxSize >> 1) : 1);        //三目运算符的优先级比较低,注意加括号
224         T*temp = new T[maxSize];
225         if (pRoot)
226         {
227             memmove_s(temp, len*sizeof(T), pRoot, len*sizeof(T));
228             delete[]pRoot;
229         }
230         pRoot = temp;
231     }
232 }
233 
234 template<class T>
235 void MyHeap<T>::push(const T& srcData)
236 {
237     _sizeExpand();    //判断容器是否已满
238     pRoot[len++] = srcData;     //堆尾部插入,在进行交换
239     size_t index = len - 1;     //插入数据在堆中的位置
240 
241     while (index >= 1)
242     {
243         if (pRoot[index] > pRoot[(index - 1) >> 1] /*&& index >= 1*/)    //若插入数据大于他的父亲,则需要进行交换
244         {
245             T temp = pRoot[index];
246             pRoot[index] = pRoot[(index - 1) >> 1];
247             pRoot[(index - 1) >> 1] = temp;
248             index = (index - 1)>>1;    //插入数据和父节点交换后,在比较父节点和祖父节点,是否需要交换?
249         }
250         else
251             break;
252     }
253 }

 

代码测试:

 

 1 // 堆.cpp : 定义控制台应用程序的入口点。
 2 //
 3 
 4 #include "stdafx.h"
 5 #include "MyHeap.h"
 6 #include <time.h>
 7 using namespace std;
 8 
 9 int _tmain(int argc, _TCHAR* argv[])
10 {
11     MyHeap<int> heap;
12     
13     int arr[12];
14     srand((unsigned)time(NULL));
15 
16     for (int i = 0; i < 10; ++i)
17         arr[i] = rand() % 100;
18     cout << "堆初始化:" << endl;
19     heap.initHeap(arr, 10);    
20     heap.prePrint(); cout << endl;
21     cout << "------------------------------------" << endl;
22 
23     cout << "向堆中插入数据:" << endl;
24     heap.push(88); heap.prePrint(); cout << endl;
25     heap.push(666); heap.prePrint(); cout << endl;
26     arr[10] = 88; arr[11] = 666;
27     cout << "------------------------------------" << endl;
28 
29     cout << "从堆中删除数据:" << endl;
30     int *deleteArr = new int[heap.size()];
31     MyHeap<int> temp(heap);
32     int len = (int)heap.size();
33     for (int i = 0; i < len; ++i)
34     {
35         heap.prePrint(); cout << endl;
36         deleteArr[i]=heap.deleteHeap();
37     }
38 
39     cout << "------------------------------------" << endl;
40     cout << "对堆中的数据排序:" << endl;
41     cout << "排序前:" << endl;
42     for (int i = 0; i < 12; ++i)
43         cout << arr[i] << "  ";
44     cout << endl;
45 
46     cout << "排序后:" << endl;
47     for (int i = 0; i < len; ++i)
48         cout << deleteArr[i] << "  ";
49     cout << endl;
50 
51     delete[]deleteArr;
52     //heap.prePrint();
53     //heap.prePrint();
54     cin.get();
55     return 0;
56 }

 

运行结果:

 

posted @ 2018-03-24 17:14  daniumeng  阅读(423)  评论(0编辑  收藏  举报