实现IQueue(队列+优先队列)
功能需求
这里我们用纯虚函数来当作接口实现IQeue,实现如下的功能:
/*************************************************************************
> File Name: 14.cpp
> Author:Xiao Yuheng
> Mail:3312638794@qq.com
> Created Time: Wed Nov 8 15:53:09 2023
************************************************************************/
#include <iostream>
#include <functional>
#include <cstring>
using namespace std;
namespace xyh {
bool cmp(int a, int b) {
return a < b;
}
bool cmp1(int a, int b) {
return a > b;
}
class IQueue {
public:
virtual void push(int x) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual int front() const = 0;
virtual bool empty() const = 0;
virtual int size() const = 0;
virtual ~IQueue() {}
};
class queue : public IQueue {
public:
};
class priority_queue : public IQueue {
public:
};
}
int main() {
using namespace xyh;
queue que;
for (int i = 0; i < 100; i++) {
que.push(i + 1);
}
while (!que.empty()) {
cout << que.top() << endl;
que.pop();
}
cout << "***************" << endl;
priority_queue prque;
for (int i = 0; i < 100; i++) {
prque.push(i + 1);
}
while (!prque.empty()) {
cout << prque.top() << endl;
prque.pop();
}
cout << "*******************" << endl;
priority_queue fprque(cmp1);
for (int i = 0; i < 100; i++) {
fprque.push(i + 1);
}
while (!fprque.empty()) {
cout << fprque.top() << endl;
fprque.pop();
}
return 0;
}
简单解释一下这段代码需要我们干什么:
命名空间和比较函数
namespace xyh {
bool cmp(int a, int b) {
return a < b;
}
bool cmp1(int a, int b) {
return a > b;
}
}
- 在
xyh
命名空间中定义了两个比较函数cmp
和cmp1
,分别用于升序和降序排列。
抽象队列接口类
class IQueue {
public:
virtual void push(int x) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual int front() const = 0;
virtual bool empty() const = 0;
virtual int size() const = 0;
virtual ~IQueue() {}
};
IQueue
是一个抽象类,定义了队列的基本接口,包括push
、pop
、top
、front
、empty
和size
函数。这是一个纯虚类,没有具体的实现。
queue 类
cppCopy codeclass queue : public IQueue {
public:
// 这里应该添加具体的实现
};
queue
类继承自IQueue
接口,但在代码中没有给出具体的实现。在程序中并没有定义这个类的实际功能。
priority_queue 类
cppCopy codeclass priority_queue : public IQueue {
public:
// 这里应该添加具体的实现
};
priority_queue
类同样继承自IQueue
接口,但在代码中也没有给出具体的实现。同样,需要在程序中定义这个类的实际功能。
主函数 main
cppCopy codeint main() {
using namespace xyh;
queue que;
// ... (对队列 que 进行操作)
priority_queue prque;
// ... (对优先队列 prque 进行操作)
priority_queue fprque(cmp1);
// ... (对具有自定义比较函数的优先队列 fprque 进行操作)
return 0;
}
main
函数中创建了一个queue
对象和两个priority_queue
对象,分别进行了操作。具体的操作没有在代码中给出,但是通过push
、top
和pop
函数来模拟对队列和优先队列的基本操作。在第三个priority_queue
对象中使用了自定义的比较函数cmp1
。
队列实现
这是一个C++队列的实现,扩展了IQueue
接口。
int *copy_int(int *now_begin, int *old_begin, int *old_end) {
memmove(now_begin, old_begin, sizeof(int) * (old_end - old_begin));
return now_begin + (old_end - old_begin);
}
class queue : public IQueue {
public:
// 构造函数
queue(int n = 0) : first(new int[n]), now(first), last(first + n) {}
// 将新元素推入队列
void push(int x) override {
if (now == last) {
Expansion();
}
*(now++) = x;
}
// 从队列中弹出前面的元素
void pop() override {
if (empty()) return;
*(first++);
}
// 获取前面元素的值
int top() const override { return *first; }
// 获取前面元素的值(与`top`相同)
int front() const override { return *first; }
// 检查队列是否为空
bool empty() const override { return first == now; }
// 获取队列的大小
int size() const override { return now - first; }
private:
// 辅助函数,在需要时扩展队列
void Expansion() {
if (now != last) return;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_first = new int[new_size];
int *new_now = new_first;
new_now = copy_int(new_first, first, last);
delete[] first;
first = new_first;
now = new_now;
last = new_first + new_size;
}
// 成员变量
int *first;
int *now;
int *last;
};
解释:
- 构造函数:使用给定的大小或默认大小为0初始化队列。
- push(int x):将新元素
x
添加到队列中。如果队列已满,则调用Expansion
函数增加其大小。 - pop():从队列中删除前面的元素。如果队列为空,则不执行任何操作。
- top() 和 front():返回前面元素的值,而不移除它。
- empty():检查队列是否为空。
- size():返回队列的大小。
这里我们简单实现了一下队列的一个接口,这里我们的代码还有很大的优化空间,上述代码中如果队列中的元素pop
出栈之后,这个空间将不在使用,浪费内存的使用,我们来实现一个循环队列进行存储:
循环队列实现
class queue : public IQueue {
public:
// 构造函数
queue(int n = 10) : data(new int[n]), head(0), tail(-1), count(0), total(n) {}
// 将新元素推入队列
void push(int x) override {
if (full()) {
Expansion();
}
count += 1, tail = (tail + 1) % total;
data[tail] = x;
}
// 从队列中弹出前面的元素
void pop() override {
if (empty()) return;
count -= 1, head = (head + 1) % total;
}
// 获取前面元素的值
int top() const override { return data[head]; }
// 获取前面元素的值(与`top`相同)
int front() const override { return data[head]; }
// 检查队列是否为空
bool empty() const override { return count == 0; }
// 获取队列的大小
int size() const override { return count; }
// 检查队列是否已满
int full() const { return count == total; }
private:
// 辅助函数,在需要时扩展队列
void Expansion() {
if (!full()) return;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_data = new int[new_size];
int *now_data = new_data;
int cnt = count;
now_data = copy_int(now_data, data + head, data + total);
now_data = copy_int(now_data, data, data + tail);
delete[] data;
data = new_data;
head = 0, tail = count - 1;
total = new_size;
cout << total << endl;
}
// 成员变量
int *data;
int head, tail, count, total;
};
解释:
- 构造函数:使用给定的大小或默认大小为10初始化队列。
- push(int x):将新元素
x
添加到队列中。如果队列已满,则调用Expansion
函数增加其大小。 - pop():从队列中删除前面的元素。如果队列为空,则不执行任何操作。
- top() 和 front():返回前面元素的值,而不移除它。
- empty():检查队列是否为空。
- size():返回队列的大小。
- full():检查队列是否已满。
- Expansion():辅助函数,在需要时扩展队列。扩展后会将原有数据复制到新的存储空间中。
我们这里的扩展队列函数太长了,我们来进行优化一下:
class queue : public IQueue {
public:
queue(int n = 10) : data(new int[n]), head(0), tail(-1), count(0), total(n) {}
void push(int x) override {
if (full()) {
Expansion();
}
count += 1, tail = (tail + 1) % total;
data[tail] = x;
}
void pop() override {
if (empty()) return ;
count -= 1, head = (head + 1) % total;
}
int top() const override { return data[head]; }
int front() const override { return data[head]; }
bool empty() const override { return count == 0; }
int size() const override { return count; }
int full() const { return count == total; }
~queue() { delete[] data; }
void swap(queue &q) {
std::swap(this->data, q.data);
std::swap(this->head, q.head);
std::swap(this->tail, q.tail);
std::swap(this->count, q.count);
std::swap(this->total, q.total);
}
private:
void Expansion() {
int old_size = size();
int len = old_size != 0 ? old_size * 2 : 1;
queue q(len);
while (!empty()) {
q.push(top());
pop();
}
swap(q);
}
int *data;
int head, tail, count, total;
};
我们可以直接开辟一个原对象两倍大小的对象,然后把原对象的元素插入到新对象中去,最后交换两个对象的所有成员属性,最后q
对象会调用析构函数,自动释放掉内存。到此,我们的队列就实现完成了。
优先队列
class priority_queue : public IQueue {
public:
priority_queue(function<bool(int, int)> cmp = less<int>(), int n = 10)
: first(new int[n]), now(first),
last(first + n), cmp(cmp) {}
void push(int x) override {
if (now == last) {
Expansion();
}
*(now++) = x;
int temp = now - first;
while (temp >> 1 && cmp(first[temp / 2 - 1], first[temp - 1])) {
swap(first[temp - 1], first[temp / 2 - 1]);
temp >>= 1;
}
}
void pop() override {
if (empty()) return ;
*first = *(--now);
int temp = 1, cnt = now - first;
while (temp << 1 <= cnt) {
int ind = temp, l = temp * 2, r = temp * 2 + 1;
if (cmp(first[temp - 1], first[l - 1])) temp = l;
if (r <= cnt && cmp(first[temp - 1], first[r - 1])) temp = r;
if (temp == ind) break;
swap(first[ind - 1], first[temp - 1]);
}
}
int top() const override { return *first; }
int front() const override { return *first; }
bool empty() const override { return first == now; }
int size() const override { return now - first; }
private:
void Expansion() {
if (now != last) return ;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_first = new int[new_size];
int *new_now = new_first;
new_now = copy_int(new_first, first, last);
delete[] first;
first = new_first;
now = new_now;
last = new_first + new_size;
}
int *first;
int *now;
int *last;
function<bool(int, int)> cmp;
};
构造函数
cppCopy codepriority_queue(function<bool(int, int)> cmp = less<int>(), int n = 10)
: first(new int[n]), now(first),
last(first + n), cmp(cmp) {}
- 构造函数接受两个参数,一个是比较函数
cmp
,默认为less<int>()
,另一个是队列的初始大小n
,默认为 10。 - 使用初始化列表初始化了类的成员变量。
first
指向队列的起始位置,now
指向当前元素的位置,last
指向队列的末尾位置,cmp
是用于比较元素大小的函数。
push 函数
cppCopy codevoid push(int x) override {
if (now == last) {
Expansion();
}
*(now++) = x;
int temp = now - first;
while (temp >> 1 && cmp(first[temp / 2 - 1], first[temp - 1])) {
swap(first[temp - 1], first[temp / 2 - 1]);
temp >>= 1;
}
}
push
函数用于将元素推入优先队列。- 如果队列已满,调用
Expansion
函数扩展队列。 - 将元素
x
放入队列末尾,然后通过循环将其调整到合适的位置,确保父节点的值大于或等于子节点的值。
pop 函数
cppCopy codevoid pop() override {
if (empty()) return ;
*first = *(--now);
int temp = 1, cnt = now - first;
while (temp << 1 <= cnt) {
int ind = temp, l = temp * 2, r = temp * 2 + 1;
if (cmp(first[temp - 1], first[l - 1])) temp = l;
if (r <= cnt && cmp(first[temp - 1], first[r - 1])) temp = r;
if (temp == ind) break;
swap(first[ind - 1], first[temp - 1]);
}
}
pop
函数用于从优先队列中移除最大元素。- 如果队列为空,函数直接返回。
- 将队列的首元素替换为队列末尾的元素,并通过循环将其调整到合适的位置,确保父节点的值大于或等于子节点的值。
top 和 front 函数
cppCopy codeint top() const override { return *first; }
int front() const override { return *first; }
top
和front
函数返回队列的首元素值。
empty 和 size 函数
cppCopy codebool empty() const override { return first == now; }
int size() const override { return now - first; }
empty
函数检查队列是否为空。size
函数返回队列中元素的数量。
Expansion 函数
cppCopy codeprivate:
void Expansion() {
if (now != last) return ;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_first = new int[new_size];
int *new_now = new_first;
new_now = copy_int(new_first, first, last);
delete[] first;
first = new_first;
now = new_now;
last = new_first + new_size;
}
Expansion
函数用于在队列满时扩展队列的大小。- 如果队列未满,函数直接返回。
- 计算新队列的大小,创建新的数组,并将原数组的元素复制到新数组。
- 最后更新队列的指针和大小。
完整代码:
/*************************************************************************
> File Name: 14.cpp
> Author:Xiao Yuheng
> Mail:3312638794@qq.com
> Created Time: Wed Nov 8 15:53:09 2023
************************************************************************/
#include <iostream>
#include <functional>
#include <cstring>
using namespace std;
namespace xyh {
bool cmp(int a, int b) {
return a < b;
}
bool cmp1(int a, int b) {
return a > b;
}
int *copy_int(int *now_begin, int *old_begin, int *old_end) {
memmove(now_begin, old_begin, sizeof(int) * (old_end - old_begin));
return now_begin + (old_end - old_begin);
}
class IQueue {
public:
virtual void push(int x) = 0;
virtual void pop() = 0;
virtual int top() const = 0;
virtual int front() const = 0;
virtual bool empty() const = 0;
virtual int size() const = 0;
virtual ~IQueue() {}
};
/*
class queue : public IQueue {
public:
queue(int n = 0) : first(new int[n]), now(first), last(first + n) {}
void push(int x) override {
if (now == last) {
Expansion();
}
*(now++) = x;
}
void pop() override {
if (empty()) return ;
*(first++);
}
int top() const override { return *first; }
int front() const override { return *first; }
bool empty() const override { return first == now; }
int size() const override { return now - first; }
private:
void Expansion() {
if (now != last) return ;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_first = new int[new_size];
int *new_now = new_first;
new_now = copy_int(new_first, first, last);
delete[] first;
first = new_first;
now = new_now;
last = new_first + new_size;
}
int *first;
int *now;
int *last;
};
*/
class queue : public IQueue {
public:
queue(int n = 10) : data(new int[n]), head(0), tail(-1), count(0), total(n) {}
void push(int x) override {
if (full()) {
Expansion();
}
count += 1, tail = (tail + 1) % total;
data[tail] = x;
}
void pop() override {
if (empty()) return ;
count -= 1, head = (head + 1) % total;
}
int top() const override { return data[head]; }
int front() const override { return data[head]; }
bool empty() const override { return count == 0; }
int size() const override { return count; }
int full() const { return count == total; }
~queue() { delete[] data; }
void swap(queue &q) {
std::swap(this->data, q.data);
std::swap(this->head, q.head);
std::swap(this->tail, q.tail);
std::swap(this->count, q.count);
std::swap(this->total, q.total);
}
private:
/*
void Expansion() {
if (!full()) return ;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_data = new int[new_size];
int *now_data = new_data;
int cnt = count;
now_data = copy_int(now_data, data + head, data + total);
now_data = copy_int(now_data, data, data + tail);
delete[] data;
data = new_data;
head = 0, tail = count - 1;
total = new_size;
cout << total << endl;
}
*/
void Expansion() {
int old_size = size();
int len = old_size != 0 ? old_size * 2 : 1;
queue q(len);
while (!empty()) {
q.push(top());
pop();
}
swap(q);
}
int *data;
int head, tail, count, total;
};
class priority_queue : public IQueue {
public:
priority_queue(function<bool(int, int)> cmp = less<int>(), int n = 10)
: first(new int[n]), now(first),
last(first + n), cmp(cmp) {}
void push(int x) override {
if (now == last) {
Expansion();
}
*(now++) = x;
int temp = now - first;
while (temp >> 1 && cmp(first[temp / 2 - 1], first[temp - 1])) {
swap(first[temp - 1], first[temp / 2 - 1]);
temp >>= 1;
}
}
void pop() override {
if (empty()) return ;
*first = *(--now);
int temp = 1, cnt = now - first;
while (temp << 1 <= cnt) {
int ind = temp, l = temp * 2, r = temp * 2 + 1;
if (cmp(first[temp - 1], first[l - 1])) temp = l;
if (r <= cnt && cmp(first[temp - 1], first[r - 1])) temp = r;
if (temp == ind) break;
swap(first[ind - 1], first[temp - 1]);
}
}
int top() const override { return *first; }
int front() const override { return *first; }
bool empty() const override { return first == now; }
int size() const override { return now - first; }
private:
void Expansion() {
if (now != last) return ;
int old_size = size();
int new_size = old_size != 0 ? old_size * 2 : 1;
int *new_first = new int[new_size];
int *new_now = new_first;
new_now = copy_int(new_first, first, last);
delete[] first;
first = new_first;
now = new_now;
last = new_first + new_size;
}
int *first;
int *now;
int *last;
function<bool(int, int)> cmp;
};
}
int main() {
using namespace xyh;
queue que;
for (int i = 0; i < 100; i++) {
que.push(i + 1);
}
while (!que.empty()) {
cout << que.top() << endl;
que.pop();
}
cout << "***************" << endl;
priority_queue prque;
for (int i = 0; i < 100; i++) {
prque.push(i + 1);
}
while (!prque.empty()) {
cout << prque.top() << endl;
prque.pop();
}
cout << "*******************" << endl;
priority_queue fprque(cmp1);
for (int i = 0; i < 100; i++) {
fprque.push(i + 1);
}
while (!fprque.empty()) {
cout << fprque.top() << endl;
fprque.pop();
}
return 0;
}