9、【堆】二叉堆

一、堆和二叉堆的介绍

堆的定义

堆(heap),这里所说的堆是数据结构中的堆,而不是内存模型中的堆。堆通常是一个可以被看做一棵树,它满足下列性质:
  [性质一] 堆中任意节点的值总是不大于(不小于)其子节点的值;
  [性质二] 堆总是一棵完全树。
将任意节点不大于其子节点的堆叫做最小堆或小根堆,而将任意节点不小于其子节点的堆叫做最大堆或大根堆。常见的堆有二叉堆、左倾堆、斜堆、二项堆、斐波那契堆等等。

二叉堆的定义

二叉堆是完全二元树或者是近似完全二元树,它分为两种:最大堆和最小堆。
最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

二叉堆一般都通过"数组"来实现。数组实现的二叉堆,父节点和子节点的位置存在一定的关系。有时候,我们将"二叉堆的第一个元素"放在数组索引0的位置,有时候放在1的位置。当然,它们的本质一样(都是二叉堆),只是实现上稍微有一丁点区别。
假设"第一个元素"在数组中的索引为 0 的话,则父节点和子节点的位置关系如下:
  (1) 索引为i的左孩子的索引是 (2*i+1);
  (2) 索引为i的左孩子的索引是 (2*i+2);
  (3) 索引为i的父结点的索引是 floor((i-1)/2);

假设"第一个元素"在数组中的索引为 1 的话,则父节点和子节点的位置关系如下:
  (1) 索引为i的左孩子的索引是 (2*i);
  (2) 索引为i的左孩子的索引是 (2*i+1);
  (3) 索引为i的父结点的索引是 floor(i/2);

二、二叉堆的解析

以"最大堆"来进行介绍的。

1. 基本定义

 1 template <class T>
 2 class MaxHeap{
 3     private:
 4         T *mHeap;        // 数据
 5         int mCapacity;    // 总的容量
 6         int mSize;        // 实际容量
 7 
 8     private:
 9         // 最大堆的向下调整算法
10         void filterdown(int start, int end);
11         // 最大堆的向上调整算法(从start开始向上直到0,调整堆)
12         void filterup(int start);
13     public:
14         MaxHeap();
15         MaxHeap(int capacity);
16         ~MaxHeap();
17 
18         // 返回data在二叉堆中的索引
19         int getIndex(T data);
20         // 删除最大堆中的data
21         int remove(T data);
22         // 将data插入到二叉堆中
23         int insert(T data);
24         // 打印二叉堆
25         void print();
26 };

2. 添加

 1 /*
 2  * 最大堆的向上调整算法(从start开始向上直到0,调整堆)
 3  *
 4  * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
 5  *
 6  * 参数说明:
 7  *     start -- 被上调节点的起始位置(一般为数组中最后一个元素的索引)
 8  */
 9 template <class T>
10 void MaxHeap<T>::filterup(int start)
11 {
12     int c = start;            // 当前节点(current)的位置
13     int p = (c-1)/2;        // 父(parent)结点的位置 
14     T tmp = mHeap[c];        // 当前节点(current)的大小
15 
16     while(c > 0)
17     {
18         if(mHeap[p] >= tmp)
19             break;
20         else
21         {
22             mHeap[c] = mHeap[p];
23             c = p;
24             p = (p-1)/2;   
25         }       
26     }
27     mHeap[c] = tmp;
28 }
29   
30 /* 
31  * 将data插入到二叉堆中
32  *
33  * 返回值:
34  *     0,表示成功
35  *    -1,表示失败
36  */
37 template <class T>
38 int MaxHeap<T>::insert(T data)
39 {
40     // 如果"堆"已满,则返回
41     if(mSize == mCapacity)
42         return -1;
43  
44     mHeap[mSize] = data;        // 将"数组"插在表尾
45     filterup(mSize);    // 向上调整堆
46     mSize++;                    // 堆的实际容量+1
47 
48     return 0;
49 }

insert(data)的作用:将数据data添加到最大堆中。当堆已满的时候,添加失败;否则data添加到最大堆的末尾。然后通过上调算法重新调整数组,使之重新成为最大堆。

3. 删除

 1 /* 
 2  * 最大堆的向下调整算法
 3  *
 4  * 注:数组实现的堆中,第N个节点的左孩子的索引值是(2N+1),右孩子的索引是(2N+2)。
 5  *
 6  * 参数说明:
 7  *     start -- 被下调节点的起始位置(一般为0,表示从第1个开始)
 8  *     end   -- 截至范围(一般为数组中最后一个元素的索引)
 9  */
10 template <class T>
11 void MaxHeap<T>::filterdown(int start, int end)
12 {
13     int c = start;          // 当前(current)节点的位置
14     int l = 2*c + 1;     // 左(left)孩子的位置
15     T tmp = mHeap[c];    // 当前(current)节点的大小
16 
17     while(l <= end)
18     {
19         // "l"是左孩子,"l+1"是右孩子
20         if(l < end && mHeap[l] < mHeap[l+1])
21             l++;        // 左右两孩子中选择较大者,即mHeap[l+1]
22         if(tmp >= mHeap[l])
23             break;        //调整结束
24         else
25         {
26             mHeap[c] = mHeap[l];
27             c = l;
28             l = 2*l + 1;   
29         }       
30     }   
31     mHeap[c] = tmp;
32 }
33 
34 /*
35  * 删除最大堆中的data
36  *
37  * 返回值:
38  *      0,成功
39  *     -1,失败
40  */
41 template <class T>
42 int MaxHeap<T>::remove(T data)
43 {
44     int index;
45     // 如果"堆"已空,则返回-1
46     if(mSize == 0)
47         return -1;
48 
49     // 获取data在数组中的索引
50     index = getIndex(data); 
51     if (index==-1)
52         return -1;
53 
54     mHeap[index] = mHeap[--mSize];    // 用最后元素填补
55     filterdown(index, mSize-1);        // 从index位置开始自上向下调整为最大堆
56 
57     return 0;
58 }

猜你喜欢

转载自www.cnblogs.com/Long-w/p/9786146.html