手撕C++代码,利用二叉堆实现优先队列

最近准备面试,疯狂补数据结构和算法。本文手撕C++代码,使用二叉堆实现优先队列。

普通的队列是一种先进先出的数据结构,元素在队列尾追加,而从队列头删除。在优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。优先队列具有最高级先出 (first in, largest out)的行为特征。通常采用堆数据结构来实现(百度百科)。

优先队列的实现中,我们一般选择堆数据结构,最大优先队列可以选用大堆,最小优先队列可以选用小堆来实现。二叉堆的底层存储方式一般使用简单的数组就可以了,它本质上是一种完全二叉树。所谓最大堆,通俗来讲,就是堆中父节点的值都大于或者等于它左右孩子节点的值,而最小堆则是其父节点的值都小于或者等于它左右孩子节点的值。堆的操作主要是插入和删除,堆的插入需要进行“上浮”操作,而删除根节点则需要进行“下沉”操作。二叉堆的实现原理可以参考这篇图文教程

通过数组实现的二叉堆,其根节点位于下标0处。已知父节点下标parentIdx,则该节点的左孩子节点下标为2*parentIdx + 1,右孩子节点下标为2*parentIdx + 2。已知孩子节点下标childIdx,则其父节点小标为(childIdx - 1)/2。

下面使用C++模板类来实现基于二叉堆的最大优先队列(注释超详细)。

#include <iostream>

using namespace std;

template<typename T>
class myPriQue
{
        public:
        myPriQue(int cap) {capacity = cap; size = 0; arr = new T[cap];}
        ~myPriQue() {delete [] arr; cout<<"arr [] deleted."<<endl;}//析构函数释放堆空间,防止内存泄漏
        bool enQue(T num);//入队
        T deQue();//出队
        void printQue();//打印调试
        private:
        void upAdjust();//堆的上浮操作
        void downAdjust();//下沉操作
        int size, capacity;//size记录实际数组大小,capacity记录数组容量在需要的时候进行扩容
        T * arr;
};

template<typename T>
bool myPriQue<T>::enQue(T num){//当数组容量不够时需要进行扩容操作
    if(++size >= capacity){
        T * oldArr = arr;
        arr = new T[capacity << 1];
        if(arr = nullptr) throw "没有足够的内存空间!!!";
        for(int i=0; i< capacity; i++)
            arr[i] = oldArr[i];
        delete [] oldArr;
    }
    arr[size-1] = num;//将新数据放入数组末尾
    upAdjust();//每插入一个数据需要进行上浮操作,保持堆的特性
    capacity <<=1;
    return true;
}

template<typename T>
T myPriQue<T>::deQue(){
    if(size <= 0) throw "队列为空!!!";//抛出异常,类型为const char *
    T temp = arr[0];//堆的根节点就是下标为0的数据,保存的是最大堆中值最大的数据
    arr[0] = arr[size - 1];
    size--;
    if(size > 1)
        downAdjust();//下沉操作,找出剩余数据中的最大值放于根节点上
    return temp;
}

template<typename T>
void myPriQue<T>::upAdjust(){
    int childIdx = size - 1;
    int parentIdx = (childIdx - 1)/2;
    if(size == 1) return;
    T temp = arr[childIdx];
    //最大堆arr[parentIdx]不会越界,因为 -1 / 2 (int)= 0
    //直观上使用parentIdx>0作为循环终止条件比较合适,但是由于上面的原因必须使用childIdx > 0
    //否则会造成死循环
    while(childIdx > 0 && temp > arr[parentIdx]){//只要新插入的数据比其父节点大就继续上浮
        arr[childIdx] = arr[parentIdx];
        childIdx = parentIdx;
        parentIdx = (childIdx-1)/2;
        //cout<<"parentIdx:"<<parentIdx<<endl;
    }
    arr[childIdx] = temp;
}

template<typename T>
void myPriQue<T>::downAdjust(){
    int parentIdx = 0;
    int childIdx = 2*parentIdx + 1;
    T temp = arr[parentIdx];
    while(childIdx < size){
        //先确定左右子节点中最大值的下标
        if(childIdx < size - 1 && arr[childIdx+1] > arr[childIdx]) childIdx++;
        if(temp >= arr[childIdx]) break;//找到合适的位置推出循环
        arr[parentIdx] = arr[childIdx];
        parentIdx = childIdx;
        childIdx = 2*parentIdx + 1;
    }
    arr[parentIdx] = temp;
}

template<typename T>
void myPriQue<T>::printQue(){
    for(int i=0; i<size; i++)
        cout << arr[i] << " ";//自定义的类需要定义友元函数实现打印功能
    cout << endl;
}

class Student
{
    public:
    Student() {cout <<"默认Constructor"<< endl;}
    Student(int score) {this->score = score;}
    Student(const Student &a) {this->score = a.score;}
    int getScore(){return this->score;}
    void setScore(int score){this->score = score;}
    bool operator<(const Student &a){return this->score < a.score;}
    bool operator>(const Student &a){return this->score > a.score;}
    bool operator>=(const Student &a){return !(*this<a);}//由于优先级,注意要加括号
    void operator=(const Student &a){this->score = a.score;}
    friend ostream & operator<<(ostream & os, const Student &a);//注意要使用friend友元
    private:
    int score;
};

ostream & operator<<(ostream & os, const Student &a){
    os<<a.score; return os;
}

int main(){
    myPriQue<Student> priQ = myPriQue<Student>(10);
    priQ.enQue(Student(20));
    priQ.enQue(Student(10));
    priQ.printQue();
    cout<< priQ.deQue() << " deQue result." << endl;
    priQ.enQue(Student(200));
    priQ.printQue();
    priQ.enQue(Student(50));
    priQ.enQue(Student(80));
    priQ.printQue();
    priQ.deQue();
    priQ.printQue();
    /*cout << Student(100) << endl;
    Student*  s = new Student[9];//在已经定义非默认构造函数的情况下这里要求显示定义默认构造函数否则报错
    s[0].setScore(90);
    cout << s[0] << "s0 score: " << endl;
    */
        /*cout<<"-1 / 2 (int): " << -1/2 <<endl;
    myPriQue<int> priQ =  myPriQue<int>(10);
    priQ.enQue(20);
    priQ.enQue(10);
    priQ.enQue(200);
    priQ.printQue();
    priQ.enQue(50);
    priQ.printQue();
    priQ.enQue(80);
    priQ.printQue();
    priQ.deQue();
    priQ.printQue();
    priQ.deQue();
    priQ.printQue();
    try{
    priQ.deQue();
    priQ.deQue();
    priQ.deQue();
    priQ.deQue();
    }
    catch(char const* e){
        cout << e;
    }*/
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值