数据结构之堆(优先队列)c++

数据结构之堆(优先队列)c++

什么是堆?

堆(优先队列)是“特殊”的队列,取出元素的顺序是按照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
堆可以看成是一颗完全二叉树。既可以使用链表实现也可以使用数组实现。但是使用数组实现比较方便。其使用数组实现的一个堆如下图所示。
在这里插入图片描述
对应数组(在用数组为存储方式的结构中,数组下标为0的地方是不存放任何数据的在这里插入图片描述
堆可以分为2种,一个是最大堆,另一个是最小堆
最大堆:父节点的值要比其两个子节点(如果存在)大。
最小堆:父节点的值要比其两个子节点(如果存在)小。

下面我们给出堆的结构描述以及堆的操作:

为了应对不同数据类型,在这里我们使用模板类,

template <class T>
class Heap
{

public:
//方便用户选择创建大堆还是小堆
    enum Way
    {
        GREATER,//大
        LESS//小
    };

private:
    T *array_heap;       //堆
    size_t current_size; //当前容量
    size_t max_size;     //最大容量
    Way flag;            //堆的标志(大堆/小堆)

public:
    explicit Heap(const int &Size);
    ~Heap();
    //输出堆内数据
    void print();
    //堆是否为空
    bool empty() const;
    //堆是否已满
    bool full() const;
    //出堆
    T delHeap();
    //插入数据
    void insert(const T &Data, Way way);
    //构造堆
    void createHeap(const T *Data_Array, const size_t &Size, Way way = Heap<T>::GREATER);

private:
    //小于
    bool less(const T &Left, const T &Right);
    //大于
    bool greater(const T &Left, const T &Right);
    //比较
    bool compare(const T &Left, const T &Right, Way way);
    //调整堆
    void adjust();
    //调整堆
    void adjust(Way way);
public:
    T &operator[](const int &location);
};

下面我们给出部分函数的定义(类外实现)


template <typename T>
Heap<T>::Heap(const int &Size) : max_size(Size), current_size(0), flag(Heap<T>::GREATER)
{
    //申请内存 0号位置不使用
    this->array_heap = new T[Size + 1];
    //内存是否申请成功
    assert(this->array_heap);
    //初始化数据
    for (int i = 0; i < Size; ++i)
    {
        this->array_heap[i] = 0;
    }
};

template <typename T>
Heap<T>::~Heap()
{
    if (this->array_heap)
    {
        delete[] this->array_heap;
    }
    this->array_heap = nullptr;
    this->current_size = this->max_size = 0;
}

//堆是否为空
template <typename T>
bool Heap<T>::empty() const
{
    return (this->current_size == 0);
}
//堆是否已满
template <typename T>
bool Heap<T>::full() const
{
    return (this->current_size == this->max_size + 1);
}

template <typename T>
T &Heap<T>::operator[](const int &Location)
{
    return (this->array_heap[Location]);
}

//小于
template <typename T>
bool Heap<T>::less(const T &Left, const T &Right)
{
    return (Left < Right);
}
//大于
template <typename T>
bool Heap<T>::greater(const T &Left, const T &Right)
{
    return (Left > Right);
}

//比较
template <typename T>
bool Heap<T>::compare(const T &Left, const T &Right, Heap<T>::Way way)
{
    if (way == this->GREATER)
    {
        return this->greater(Left, Right);
    }
    else
    {
        return (this->less(Left, Right));
    }
}
//输出堆内数据
template <typename T>
void Heap<T>::print()
{
    for (auto i = 1; i <= this->current_size; ++i)
    {
        std::cout << this->array_heap[i] << ' ';
    }
    std::cout << std::endl;
}

堆的构造

我们使用数组来构造一个堆,为了处理多个数据,我们的构造选择接受一个数组元素来初始化我们的堆(此函数为类内实现)

//构造堆
    void createHeap(const T *Data_Array, const size_t &Size, Way way = Heap<T>::GREATER)
    {
        this->flag = way; //标识堆
        if (Size > this->max_size)
        {
            return;
        }
        for (auto i = 0; i < Size; ++i)
        {
            this->insert(Data_Array[i], way);
        }
    }

堆的插入

大家应该已经注意到了,在我们构造堆的函数中,核心的操作其实就是插入,
***注意事项:***将元素插入到堆中的时候,不能改变堆的性质。
插入操作
插入元素到堆中,首先我们将元素插入到数组最后一个元素的下一个位置(假设下标为i),接下来我们要对该堆进行一个调整。

我们以大堆为例,假设我们要插入是数据是10,如图所示:

在这里插入图片描述
我们把10放在数组的最后面,我们可以看到此时10>9,这肯定已经违反了大堆的性质,所以我们要对其进行调整,调整的方式为,将10与他的父节点(parent)进行比较,如果值大于父节点,我们就把该节点与父节点进行交换,然后继续和其父节点比较,直到有序为止(注意:在调整过程中父节点下标不能到零,即>1)。(父节点下标的计算公式为:父节点下标=当前下标 / 2)
在这里插入图片描述
此时我们发现10<16(父节点),堆已经有序,所以不需要调整了

下面我们来看插入操作的代码实现:

 //插入数据
    void insert(const T &Data, Way way)
    {
        //堆是否已满
        if (this->full())
        {
            return;
        }
        //把数据插入到末尾
        this->array_heap[++this->current_size] = Data;
        //调整堆
        this->adjust(way);
    }

下面是插入操作的核心,即调整堆,让堆变得有序,

//调整堆
    void adjust(Way way)
    {
        auto curPos = this->current_size;    // curPos指向堆中最后一个节点位置
        auto max = this->array_heap[curPos]; //保存当前节点数据
          // curPos > 1 防止访问到0号位置
        while (curPos > 1 && this->compare(max, this->array_heap[curPos / 2], way))
        {
            this->array_heap[curPos] = this->array_heap[curPos / 2]; //向下过滤节点
            curPos = curPos / 2;
        }
        //将原数据插入
        this->array_heap[curPos] = max;
    }

在此,为了提高效率,我们并不使用交换两个数据元素的方式

这种调整堆的方式又称为向下过滤节点

堆的删除

删除操作针对与对顶元素也就是根节点,我们采用的删除方式是,拿掉根节点,把末尾节点放到原根节上面,如图所示:
在这里插入图片描述
此时我们发现,根节点的值明显小于其左右子节点的值(1<16,1<12),已经不满足大堆的性质,在此时,我们需要对该堆的进行调整,让他变得有序。
此时我们所用的调整方式与插入时不一样
这次我们要将根节点的值与其子节点进行比较,对其进行向下调整,即
将根节点与左右子节点进行比较,把大的值往上移动,直到堆有序为止(子节点下标=父节点下标 * 2)
如图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
此时堆已经调整完毕

下面我们来看代码实现


//出堆
template <typename T>
T Heap<T>::delHeap()
{
    //判断堆是否为空
    if(this->empty())
    {
        return false;
    }
    //保存根节点元素
    auto item = this->array_heap[1];
    //将末尾元素放到根节点上面,堆的大小减一
    this->array_heap[1] = this->array_heap[this->current_size--] ;
    //调整堆
    this->adjust();
    //返回根节点元素
    return item;
}

下面是调整堆的代码实现


//调整堆
template <typename T>
void Heap<T>::adjust()
{
    //记录父节点和子节点下标位置
    size_t parent, child;
//保存根节点元素
    auto item=this->array_heap[1];
    
   for(parent=1;parent*2<this->current_size;parent=child)
   {
       child=parent*2;
       //比较左节点和右节点大小
       if(child<this->current_size&&this->compare(this->array_heap[child+1],this->array_heap[child],this->flag))
       {
           ++child;
       }
       //向下过滤节点
        if(this->compare(this->array_heap[child],item,this->flag))
        {
            this->array_heap[parent]=this->array_heap[child];
        }
   }
   //插入数据
   this->array_heap[parent]=item;
    

}

下面我们来进行测试:


int main()
{

    int arr[10] = {1,3,2,5,4,6,7,9,8,10};
    Heap<int> heap1(10);
    std::cout<<"最大堆"<<std::endl;
    //最大堆
    heap1.createHeap(arr,10,Heap<int>::GREATER);
    std::cout<<"堆内元素"<<std::endl;
    heap1.print();
    //删除
    heap1.delHeap();
    std::cout<<"删除后堆内元素"<<std::endl;
    heap1.print();
    //插入
    heap1.insert(20,Heap<int>::GREATER);
     std::cout<<"插入后堆内元素"<<std::endl;
    heap1.print();

    Heap<int>heap2(10);
    std::cout<<"最小堆"<<std::endl;
    //最小堆
    heap2.createHeap(arr,10,Heap<int>::LESS);
     std::cout<<"堆内元素"<<std::endl;
    heap2.print();
    //删除
    heap2.delHeap();
    std::cout<<"删除后堆内元素"<<std::endl;
    heap2.print();
    //插入
    heap2.insert(20,Heap<int>::LESS);
     std::cout<<"插入后堆内元素"<<std::endl;
    heap2.print();

    return 0;
}

这是输出结果
在这里插入图片描述
最后这是完整的代码(分文件)
头文件

#ifndef HEAP_H_
#define HEAP_H_
#include <assert.h>
#include <iostream>

template <class T>
class Heap
{

public:
    enum Way
    {
        GREATER,
        LESS
    };

private:
    T *array_heap;       //堆
    size_t current_size; //当前容量
    size_t max_size;     //最大容量
    Way flag;            //堆的表示(大堆/小堆)

public:
    explicit Heap(const int &Size);
    ~Heap();
    //输出堆内数据
    void print();
    //堆是否为空
    bool empty() const;
    //堆是否已满
    bool full() const;
    //出堆
    T delHeap();

    //插入数据
    void insert(const T &Data, Way way)
    {
        if (this->full())
        {
            return;
        }
        //把数据插入到末尾
        this->array_heap[++this->current_size] = Data;
        //调整堆
        this->adjust(way);
    }

    //构造堆
    void createHeap(const T *Data_Array, const size_t &Size, Way way = Heap<T>::GREATER)
    {
        this->flag = way; //标识堆
        if (Size > this->max_size)
        {
            return;
        }
        for (auto i = 0; i < Size; ++i)
        {
            this->insert(Data_Array[i], way);
        }
    }

private:
    //小于
    bool less(const T &Left, const T &Right);
    //大于
    bool greater(const T &Left, const T &Right);
    //比较
    bool compare(const T &Left, const T &Right, Way way);
    //调整堆
    void adjust();
    //调整堆
    void adjust(Way way)
    {
        auto curPos = this->current_size;    // curPos指向堆中最后一个节点位置
        auto max = this->array_heap[curPos]; //保存当前节点数据
                                             // curPos > 1 防止访问到0号位置
        while (curPos > 1 && this->compare(max, this->array_heap[curPos / 2], way))
        {
            this->array_heap[curPos] = this->array_heap[curPos / 2]; //向下过滤节点
            curPos = curPos / 2;
        }
        //将原数据插入
        this->array_heap[curPos] = max;
    }

public:
    T &operator[](const int &location);
};

template <typename T>
Heap<T>::Heap(const int &Size) : max_size(Size), current_size(0), flag(Heap<T>::GREATER)
{
    //申请内存 0号位置不使用
    this->array_heap = new T[Size + 1];
    //内存是否申请成功
    assert(this->array_heap);
    //初始化数据
    for (int i = 0; i < Size; ++i)
    {
        this->array_heap[i] = 0;
    }
};

template <typename T>
Heap<T>::~Heap()
{
    if (this->array_heap)
    {
        delete[] this->array_heap;
    }
    this->array_heap = nullptr;
    this->current_size = this->max_size = 0;
}

//堆是否为空
template <typename T>
bool Heap<T>::empty() const
{
    return (this->current_size == 0);
}
//堆是否已满
template <typename T>
bool Heap<T>::full() const
{
    return (this->current_size == this->max_size + 1);
}

template <typename T>
T &Heap<T>::operator[](const int &Location)
{
    return (this->array_heap[Location]);
}

//小于
template <typename T>
bool Heap<T>::less(const T &Left, const T &Right)
{
    return (Left < Right);
}
//大于
template <typename T>
bool Heap<T>::greater(const T &Left, const T &Right)
{
    return (Left > Right);
}

//比较
template <typename T>
bool Heap<T>::compare(const T &Left, const T &Right, Heap<T>::Way way)
{
    if (way == this->GREATER)
    {
        return this->greater(Left, Right);
    }
    else
    {
        return (this->less(Left, Right));
    }
}

//出堆
template <typename T>
T Heap<T>::delHeap()
{
    //判断堆是否为空
    if(this->empty())
    {
        return false;
    }
    //保存根节点元素
    auto item = this->array_heap[1];
    //将末尾元素放到根节点上面,堆的大小减一
    this->array_heap[1] = this->array_heap[this->current_size--] ;
    //调整堆
    this->adjust();
    //返回根节点元素
    return item;
}

//调整堆
template <typename T>
void Heap<T>::adjust()
{
    //记录父节点和子节点下标位置
    size_t parent, child;
//保存根节点元素
    auto item=this->array_heap[1];
    
   for(parent=1;parent*2<this->current_size;parent=child)
   {
       child=parent*2;
       //比较左节点和右节点大小
       if(child<this->current_size&&this->compare(this->array_heap[child+1],this->array_heap[child],this->flag))
       {
           ++child;
       }
       //向下过滤节点
        if(this->compare(this->array_heap[child],item,this->flag))
        {
            this->array_heap[parent]=this->array_heap[child];
        }
   }
   //插入数据
   this->array_heap[parent]=item;
    

}

//输出堆内数据
template <typename T>
void Heap<T>::print()
{
    for (auto i = 1; i <= this->current_size; ++i)
    {
        std::cout << this->array_heap[i] << ' ';
    }
    std::cout << std::endl;
}

#endif

main文件

#include<iostream>
#include "heap.h"

int main()
{

    int arr[10] = {1,3,2,5,4,6,7,9,8,10};
    Heap<int> heap1(10);
    std::cout<<"最大堆"<<std::endl;
    //最大堆
    heap1.createHeap(arr,10,Heap<int>::GREATER);
    std::cout<<"堆内元素"<<std::endl;
    heap1.print();
    //删除
    heap1.delHeap();
    std::cout<<"删除后堆内元素"<<std::endl;
    heap1.print();
    //插入
    heap1.insert(20,Heap<int>::GREATER);
     std::cout<<"插入后堆内元素"<<std::endl;
    heap1.print();

    Heap<int>heap2(10);
    std::cout<<"最小堆"<<std::endl;
    //最小堆
    heap2.createHeap(arr,10,Heap<int>::LESS);
     std::cout<<"堆内元素"<<std::endl;
    heap2.print();
    //删除
    heap2.delHeap();
    std::cout<<"删除后堆内元素"<<std::endl;
    heap2.print();
    //插入
    heap2.insert(20,Heap<int>::LESS);
     std::cout<<"插入后堆内元素"<<std::endl;
    heap2.print();

    return 0;
}

好了这就是以上数据结构之堆的所有内容,希望大家喜欢。
喜欢的话麻烦大家点个赞~在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值