当前位置: 首页> 游戏> 评测 > 「队列」实现优先队列(priority_queue)的功能 / 手撕数据结构(C++)

「队列」实现优先队列(priority_queue)的功能 / 手撕数据结构(C++)

时间:2025/7/11 3:02:42来源:https://blog.csdn.net/dakingffo/article/details/142304180 浏览次数:0次

目录

前置知识

概述

命名空间

成员变量

创建销毁

数组操作

堆操作

Code


前置知识

在本篇文章之前:你应该先了解堆与堆排序:「数组」堆排序 / 大根堆优化(C++)


概述

优先队列的本质就是堆。

一般的队列执行先入先出逻辑,而优先队列执行最大/小先出逻辑。也就是说,它的出队原则不再是入队时间,而是队内元素的相对大小关系。

堆有两种实现:二叉堆实现和数组实现,其中,数组实现是最常用的,也是效率较高的。C++内置的优先队列就使用了vector动态数组作为底层实现,今天我们也来动手实现一个优先队列。

总的来讲,我们的优先队列就是堆化的动态数组。


命名空间

C++有自己的std命名空间下的priority_queue,为了进行区分,封装一个自己的优先队列命名空间custom_queue。

namespace custom_priority_queue{...
}

成员变量

template <typename T>泛型,作为数据适配器,他的数据单位应该是任意一种类型,此时暂用T表示,至于T为何物将在实例化时以<>告知。

template<typename T, typename CMP = less<T>>则预定义了类型T和比较类对象CMP,CMP默认设为less<T>。(less和greater时C++内置的仿函数类)

实例化CMP时会实现名为compare的对象,内部重载了()运算符作为bool类型函数,当调用compare(a,b)时,会按内部比较规则传回true或false。如我们使用了less<T>作为比较类,那么传入compare会执行{return a<b;}。(同样的,使用greater则执行{return a>b;})

*注意*:堆比较时总执行父子比较,即compare(父,子),我们使用less<T>实例化compare则执行{return 父<子;},返回真值则发生父子交换,则维护了大根堆。

定义class类priority_queue,封装四个成员变量:    

T* val; 数组指针
size_t val_size; 记录数组长度
size_t val_capacity; 记录可用空间
CMP compare; 作为仿函数类,维护大小比较规则。

(size_t 是C/C++标准在stddef.h中定义的(这个头文件通常不需要#include),size_t 类型专门用于表示长度,它是无符号整数。)

另有swap函数实现数据交换,略去不表。

template<typename T,typename CMP = less<T>>
class priority_queue {
private:#define father(x) ((x-1)/2)#define lchild(x) (2*x+1)#define rchild(x) (2*x+2)T* val;size_t val_size;size_t val_capacity;CMP compare;void swap(int& a, int& b) {int temp = b;b = a, a = temp;}...
public:...
};

创建销毁

默认构造:priority_queue(int num = 1);

       接收一个num,num默认是1。为数组开为1个元素的空间。

复制构造:priority_queue(const priority_queue& another);

       按字节复制another。

移动构造:priority_queue(priority_queue&& another);

       移动构造详见:「数组」实现动态数组的功能 / 手撕数据结构(C++)

复制赋值运算符:priority_queue& operator=(const priority_queue& another);

       类似复制构造。

移动赋值运算符:priority_queue& operator=(priority_queue&& another);

       类似移动构造。

析构函数:~priority_queue();

       释放底层数组。

priority_queue(int num = 1) :val_size(0), val_capacity(num) {val = new T[num];
};
priority_queue(const priority_queue& another) : val_size(another.val_size), val_capacity(another.val_capacity) {//拷贝构造val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);
}
priority_queue(priority_queue&& another) noexcept : val_size(another.val_size), val_capacity(another.val_capacity) {//移动构造val = another.val;another.val = nullptr;
}
~priority_queue() {delete[] val;
}
priority_queue& operator=(const priority_queue& another) {delete[]val;val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);val_size = another.val_size;val_capacity = another.val_capacity;return *this;
}
priority_queue& operator=(priority_queue&& another) {if (this == &another)return *this;delete[]val;val = another.val;val_size = another.val_size;val_capacity = another.val_capacity;another.val = nullptr;return *this;
}

数组操作

获取长度:size_t size();

       返回array类型的val内部的val_size。

判断为空:bool empty();

       返回array类型的val内部的val_size ? false : true。

延长空间:void reserve(const int num);

       如果申请的新空间小于val_capacity,无事发生,否则申请更大的空间。

bool empty()const {return val_size ? false : true;
}
size_t size()const {return val_size;
}
void reserve(const int num) {if (num > val_capacity) {T* temp = new T[num];memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_capacity = num;}
}

堆操作

堆操作是优先队列操作的核心。

元素入队(压堆):void push(V&& elem);

       数组容量有限则申请更大的空间,随后元素入堆,再上浮到合适位置。

template<typename V>后接V&&作为万能引用,是为了对传入的参数进行推断,这样这个函数就能同时接受左值引用和右值引用。如果你不想理解它,可以将函数参数直接改为T elem)

元素出队(出堆):void pop();

       断言val_size>1之后移除堆顶,之后令堆的最后一个元素上升到堆顶,再逐层沉底。

访问队头(堆顶):const T& top();

       返回堆顶的常量引用。

template<typename V>
void push(V&& elem) {if (val_capacity - val_size <= 1)reserve(val_capacity * 2);val[val_size] = elem;up(val_size);val_size++;
}
void pop() {assert(val_size > 0);val[0]=val[--val_size];down(0);
}
const T& top()const {assert(val_size > 0);return val[0];
}

交换:void swap(int& a, int& b);

       执行元素交换。

上浮与下沉:void up(int idx);void down(int idx);

       详见:「数组」堆排序 / 大根堆优化(C++)

 *注意*:他们应均声明为私有成员。 

void swap(int& a, int& b) {int temp = b;b = a, a = temp;
}
void up(int idx) {if (idx && compare(val[father(idx)],val[idx])) {swap(val[father(idx)], val[idx]);up(father(idx));}
}
void down(int idx) {int pos;if (rchild(idx) < val_size)pos = compare(val[lchild(idx)],val[rchild(idx)]) ? rchild(idx) : lchild(idx);else if (lchild(idx) < val_size)pos = lchild(idx);else return;if (compare(val[idx],val[pos])) {swap(val[idx], val[pos]);down(pos);}
}

Code

#include <cassert>
#ifndef CUSTOM_PRIORITY_QUEUE
#define CUSTOM_PRIORITY_QUEUE
namespace custom_priority_queue {template<typename T,typename CMP = less<T>>class priority_queue {private:#define father(x) ((x-1)/2)#define lchild(x) (2*x+1)#define rchild(x) (2*x+2)T* val;size_t val_size;size_t val_capacity;CMP compare;void swap(int& a, int& b) {int temp = b;b = a, a = temp;}void up(int idx) {if (idx && compare(val[father(idx)],val[idx])) {swap(val[father(idx)], val[idx]);up(father(idx));}}void down(int idx) {int pos;if (rchild(idx) < val_size)pos = compare(val[lchild(idx)],val[rchild(idx)]) ? rchild(idx) : lchild(idx);else if (lchild(idx) < val_size)pos = lchild(idx);else return;if (compare(val[idx],val[pos])) {swap(val[idx], val[pos]);down(pos);}}public:priority_queue(int num = 1) :val_size(0), val_capacity(num) {val = new T[num];};priority_queue(const priority_queue& another) : val_size(another.val_size), val_capacity(another.val_capacity) {//拷贝构造val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);}priority_queue(priority_queue&& another) noexcept : val_size(another.val_size), val_capacity(another.val_capacity) {//移动构造val = another.val;another.val = nullptr;}priority_queue& operator=(const priority_queue& another) {delete[]val;val = new T[another.val_capacity];memcpy(val, another.val, sizeof(T) * another.val_size);val_size = another.val_size;val_capacity = another.val_capacity;return *this;}priority_queue& operator=(priority_queue&& another) {if (this == &another)return *this;delete[]val;val = another.val;val_size = another.val_size;val_capacity = another.val_capacity;another.val = nullptr;return *this;}~priority_queue() {delete[] val;}bool empty()const {return val_size ? false : true;}size_t size()const {return val_size;}void reserve(const int num) {if (num > val_capacity) {T* temp = new T[num];memcpy(temp, val, sizeof(T) * val_size);delete[]val;val = temp;val_capacity = num;}}template<typename V>void push(V&& elem) {if (val_capacity - val_size <= 1)reserve(val_capacity * 2);val[val_size] = elem;up(val_size);val_size++;}void pop() {assert(val_size > 0);val[0]=val[--val_size];down(0);}const T& top()const {assert(val_size > 0);return val[0];}};
}
#endif
关键字:「队列」实现优先队列(priority_queue)的功能 / 手撕数据结构(C++)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: