IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 手撕C++代码,利用二叉堆实现优先队列 -> 正文阅读

[C++知识库]手撕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;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-04 21:12:22  更:2021-07-04 21:13:23 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 0:56:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码