Quantcast
Channel: CSDN博客推荐文章
Viewing all articles
Browse latest Browse all 35570

数据结构与算法C++描述(12)---堆及最大堆

$
0
0

1、有关堆的相关概念

  1. 最大堆(最小树):最大(最小)的完全二叉树。
  2. 最大树(最小树):每个节点的值都大于(小于)或等于其子节点(如果有的话)值的数。注意,最大(小)数不一定是二叉树。
  3. 完全二叉树:从满二叉树中删除k个元素,其编号为2hi1<=i<=k,所得到的二叉树成为完全二叉树。
    一个最大堆如下图所示:
    这里写图片描述

2、最大堆的相关操作

这里,最大堆的操作包括:堆顶元素的删除、向最大堆中插入一个元素、将一个数组转化为最大堆、最大堆的输出、获取最大堆的堆顶元素等。
下面利用C++语言分别实现之。

2.1 最大堆类的声明与简单函数实现

最大堆类有3个私有成员:heap指针为堆中元素数组、CurrentSize为最大堆当前大小、MaxSize为最大堆的最大容量(通过构造函数赋值)。

template <class T>
class MaxHeap
{
public:
    MaxHeap(int HeapSize=10)
    {
        MaxSize = HeapSize;                          //堆的最大容量                    
        heap = new T[MaxSize + 1];                   //新建堆元素数组
        CurrentSize = 0;                             //设置当前容量为0
    }
    ~MaxHeap() { delete[] heap; }
    //获取当前堆的大小
    int Size() const { return CurrentSize; }
    //获取堆顶元素
    T Max()
    {
        if (CurrrentSize == 0)
            throw OutOfRange();
        else 
            return heap[1];                        //heap中的元素从1开始
    }
    //向堆中插入元素x,并返回操作之后的堆
    MaxHeap<T> &Insert(const T &x);
    //删除堆顶元素,并返回操作后的堆 
    MaxHeap<T> &DeleteMax(T &x);
    //初始化函数,将一个数组构造成最大堆,size为数组中元素个数,ArrayMaxSize为数组最大容量
    MaxHeap<T> &Initialize(T a[], int size, int ArraySize);
    //输出最大堆
    void Output(ostream &out)
    {
        for (int i = 1; i <= CurrentSize; i++)
            out << heap[i] << "  ";
        cout << endl;
    }
private:
    T *heap;                                     //元素数组,索引值从1开始
    int CurrentSize,                             //当前堆大小
        MaxSize;                                 //堆的最大规模
};

2.2 向最大堆中插入一个元素

若插入一个到最大堆中,可以先将元素放入最大堆的末尾,然后在调整插入元素后的完全二叉树,使其成为一个最大堆。调整规则可以根据最大堆的定义来设定。
由于二叉树中最后一个父节点的索引值满足[CurrentSize/2],其中CurrentSize为当前二叉树中元素个数。当插入一个元素后,原二叉树中元素个数加1。此时,可以从最后一个父节点开始,依次访问每个父节点,直到访问完第一个父节点(即根节点)。在访问每个父节点时,分别判断父节点与其左右孩子的大小关系。先判断其左孩子(若存在),若大于父节点,则与父节点交换;再访问其右孩子(若存在),若大于父节点,则交换。

//交换a和b
template <class T>
void Swap(T &a, T &b)
{
    T c;
    c = a; a = b; b = c;
}
//向堆中插入元素x,并返回操作之后的堆
template <class T>
MaxHeap<T> &MaxHeap<T>::Insert(const T &x)
{
    if (CurrentSize == MaxSize)
        throw NoMerm();
    else 
    {
        heap[CurrentSize + 1] = x;                     //取x的值作为堆的最后一个元素
        CurrentSize++;
        int root_i = (CurrentSize + 1) / 2;            //取当前元素的根节点  
        //依次访问各个根节点,调整成为最大堆
        while (root_i > 0)
        {
            //若左孩子(若存在)大于根节点,则交换
            if (2 * root_i <= CurrentSize && heap[root_i] < heap[2 * root_i])
                Swap(heap[root_i], heap[2 * root_i]);
            //若右孩子(若存在)大于根节点,则交换
            if (2 * root_i + 1 <= CurrentSize && heap[root_i] < heap[2 * root_i+1])
                Swap(heap[root_i], heap[2 * root_i + 1]);
            root_i--;                                   //访问下一个根节点
        }
    }       
    return *this;
}

2.3 删除堆顶元素,并赋给x,返回操作后的最大堆

删除堆顶元素后,为了节省重建最大堆的时间,可以将最大堆的最后一个元素移至堆顶。从堆顶开始,依次访问各个父节点,直至最后一个父节点(索引值为[(CurrentSize-1)/2])。在依次访问父节点过程中,将父节点与其左右孩子(若存在)中的最大者交换。

//删除堆顶元素,赋给x,并返回操作后的堆 
template <class T>
MaxHeap<T> &MaxHeap<T>::DeleteMax(T &x)
{
    if (CurrentSize == 0)
        throw OutOfRange();
    else
    {
        x = heap[1];                                   //将堆顶元素给z
        heap[1] = heap[CurrentSize];                   //将堆的最后一个元素移到堆顶
        CurrentSize--;                                 //当前堆大小-1
        int Max_root_i = CurrentSize / 2;              //当前最后一个根节点的索引值
        int i = 1;
        while (i <= Max_root_i &&(2*i<=CurrentSize || 2*i+1<=CurrentSize))
        {
            //不满足最大堆的定义
            if (heap[i] < heap[2 * i] || heap[i] < heap[2 * i + 1])  
            {
                int max_child_index;
                //寻找左右孩子最大者的索引值
                if (2 * i < CurrentSize)               //根节点左右孩子都存在
                    //最大者索引值为左右孩子较大者的索引值
                    max_child_index = (heap[2 * i] >= heap[2 * i + 1]) ? 2 * i : 2 * i + 1;
                else if (2 * i <= CurrentSize)         //仅存在左孩子,最大值索引值为左孩子
                    max_child_index = 2 * i;
                //根节点和左右孩子的较大者交换
                Swap(heap[i], heap[max_child_index]);
            }
            i++;                                      //访问下一个根节点
        }       
    }
    return *this;
}

2.4 将数组转化成最大堆

对于一个给定数组,首先删除原来heap数组中的数据,然后为heap分配与给定数组大小相同的空间,并将数组元素赋值给heap数组。接着,采用了正反向两次访问父节点的策略,将heap中元素调整成为最大堆。

  1. 首先从最后一个父节点开始(索引值为[CurrentSize/2]),依次访问父节点,直到第一个父节点(即根节点)。在访问每个父节点时,分别判断父节点与其左右孩子的大小关系。先判断其左孩子(若存在),若大于父节点,则与父节点交换;再访问其右孩子(若存在),若大于父节点,则交换。
  2. 接着,再从第一个父节点开始,直至访问到最后一个父节点。期间,元素调整策略相同。
//将一个数组转化为最大堆,size为数组中元素个数,ArrayMaxSize为数组最大容量
template <class T>
MaxHeap<T> &MaxHeap<T>::Initialize(T a[], int size, int ArrayMaxSize)
{
    delete[] heap;                                 //删除heap中的所有元素
    MaxSize = ArrayMaxSize;                        //最大容量
    heap = new T[size];
    CurrentSize = size-1;                          //当前容量为数组中元素个数
    for (int i = 1; i < size; i++)                 //将数组a的值赋给数组heap
        heap[i] = a[i];
    int root_i = CurrentSize / 2;                  //最后一个根节点索引值
    while (root_i>0)                               //从后向前依次访问每个根节点
    {   
        //若根节点小于其左孩子,则交换
        if (2 * root_i <= CurrentSize && heap[root_i] < heap[2 * root_i])
            Swap(heap[root_i], heap[2 * root_i]);
        //若根节点小于其右孩子,则交换
        if (2 * root_i + 1 <= CurrentSize && heap[root_i] < heap[2 * root_i + 1])
            Swap(heap[root_i], heap[2 * root_i + 1]);
        //访问上一个根节点
        root_i--;
    }
    int i = 1;
    while (i <= int(CurrentSize / 2))              //从前向后依次访问每个根节点
    {
        //若根节点小于其左孩子,则交换
        if (2 * i <= CurrentSize && heap[i] < heap[2 * i])
            Swap(heap[i], heap[2 * i]);
        //若根节点小于其右孩子,则交换
        if (2 * i + 1 <= CurrentSize && heap[i] < heap[2 * i + 1])
            Swap(heap[i], heap[2 * i + 1]);
        //访问下一个根节点
        i++;
    }
    return *this;
}

2.5 重载操作符“<<”

//重载输出操作符"<<"
template <class T> 
ostream &operator<<(ostream &out, MaxHeap<T> &MH)
{
    MH.Output(out);
    return out;
}

2.6 测试

#include "MaxHeap.h"

int main()
{
    try
    {
        int a[] = { 12,32,43,56,74,13,9 };
        MaxHeap<int> MH;
        //测试Insert函数
        MH.Insert(3).Insert(4).Insert(10).Insert(6);
        cout <<"当前最大堆为:      " << MH<<endl;
        //测试DeleteMax函数
        int x;
        MH.DeleteMax(x);
        cout << "删除元素为:        " << x << endl << endl;
        cout <<"当前最大堆为:      " << MH << endl;
        //测试Initialize函数
        MH.Initialize(a, 7, 10);
        cout << "输入的数组为:     ";
        for (int i = 1; i < 7; i++)
            cout << a[i] << "  ";
        cout<< endl << endl;
        cout <<"转化为最大堆为:   "<< MH << endl;      
    }
    catch (OutOfRange)
    {
        cout << "越界!!!" << endl;
    }
    catch (NoMerm)
    {
        cout << "内存不足!!!" << endl;
    }
}

测试结果:
这里写图片描述

作者:weixin_38215395 发表于2017/10/30 19:22:51 原文链接
阅读:57 评论:0 查看评论

Viewing all articles
Browse latest Browse all 35570

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>