堆栈和队列,是两种相当典型的抽象数据类型,也是一种有特定进出规则的线性表;主要特性:根据自身的规则按位置插入和删除数据;本文主要,通过数组和单链表来实现堆栈(FILO)和队列(FIFO);同时,使用循环数组和循环链表实现了环形队列的线性结构。
一、堆栈stack
堆栈(stack):last in first out(LIFO)先进后出,只能在一个端口进行操作,比如:入栈(压入push)、出栈(弹出pop);
应用范围:递归调用和返回(每次递归之前,须先将下一个指令的地址和变量的值保存到堆栈中,;当递归返回时,则按序从堆栈顶端取出这些相关值,回到原来执行递归前的状态,再继续执行),算数表达式的转换和求值(如中序法变成后序法),二叉树和森林的遍历操作(前序遍历,中序遍历),调用子程序和从子程序中返回(在执行调用子程序前,必须将返回地址(即即下一条指令的地址)压入堆栈中,然后才开始执行调用子程序的操作,等到子程序执行完后,再从堆栈中弹出返回地址),CPU的中断处理(interrupt handling)中重要数据的保护作用(可以对重要的寄存器或变量进行入栈和出栈操作,达到保护数据的目的)、编译错误处理(当编译程序出现错误或警告信息时,会将所在的地址压入堆栈中,之后才会显示错误相关的信息对照表);
堆栈的基本操作:创建空堆栈、将数据入栈并返回新的堆栈、从栈顶弹出数据并返回新的堆栈、判断堆栈是否是空堆栈、判断堆栈是否已满;
c++中,堆栈的逻辑结构是线性表,存储结构:顺序栈结构和链表;
1.顺序栈结构(数组)实现堆栈
代码分析:采用数组的存储结构,并使用模板类来实现栈内数据的压入和弹出,用capacity、size来记录栈的最大容量和元素个数;在main执行完成后,创建的栈去的内存空间被系统自动回收;
stack_arr.hpp
#pragma once
#include<iostream>
using namespace std;
template<class T>
class MyArrStack
{
public:
MyArrStack(int capacity); // 构造函数:初始化栈空间的数组的容量和元素个数,以及栈空间的内存结构
~MyArrStack(); // 析构函数:释放掉在堆区申请的用来存放栈结构的内存空间
// 获取栈空间的数组的容量和元素个数
int GetSize() { return m_size; }
int GetCapacity() { return m_capacity; }
// 判断栈空间中,元素的个数:空/满
bool IsFull();
bool IsEmpty();
void push(const T& data);
void pop(T& data);
void Print(); // 按照出栈的顺序打印元素
private:
int m_size;
int m_capacity;
T* stack_top; // 栈空间的栈顶指针
};
template<typename T>
MyArrStack<T>::MyArrStack(int capacity)
{
m_size = 0;
m_capacity = capacity;
stack_top = new T[m_capacity];
cout << "调用了有参构造函数MyArrStack<T>::MyArrStack(int capacity) " << endl;
}
template<typename T>
MyArrStack<T>::~MyArrStack()
{
delete[] stack_top;
cout << "调用了析构函数MyArrStack<T>::~MyArrStack() " << endl;
}
template<typename T>
bool MyArrStack<T>::IsFull()
{
if (m_size == m_capacity)
{
return true;
}
else
{
return false;
}
}
template<typename T>
bool MyArrStack<T>::IsEmpty()
{
if (m_size == 0)
{
return true;
}
else
{
return false;
}
}
template<typename T>
void MyArrStack<T>::push(const T& data)
{
if (IsFull())
{
cout << "stack overflow" << endl;
return;
}
else
{
stack_top[m_size] = data;
m_size++;
}
}
template<typename T>
void MyArrStack<T>::pop(T& data)
{
if (IsEmpty())
{
cout << "empty stack" << endl;
return;
}
else
{
data = stack_top[size];
m_size--;
}
}
template<typename T>
void MyArrStack<T>::Print()
{
if (IsEmpty())
{
cout << "empty stack" << endl;
return;
}
for (int i = m_size - 1; i >= 0; i--)
{
cout << stack_top[i] << " ";
}
cout << endl;
}
main.cpp
#include "array_stack.hpp"
int main()
{
MyArrStack<int> stack_arr(10);
for (int i = 0; i < 10; i++)
{
stack_arr.push(i);
}
stack_arr.Print();
if (!stack_arr.IsEmpty())
{
cout << "size = " << stack_arr.GetSize() << "\tcapacity = " << stack_arr.GetCapacity() << endl;
}
if (stack_arr.IsFull())
{
cout << "stack full" << endl;
}
stack_arr.push(10); // 栈溢出
return 0;
}
2.单链表实现堆栈
代码分析:采用链表的存储结构,并使用模板类LinkNode来创建结点、用模板类LinkStack来实现栈内数据的压入和弹出,用size来记录栈的最大容量,可以随意不断地增加元素,不会有溢出的风险;重载<<,用来打印栈中的元素;main执行完成后,创建的栈的内存空间会被系统自动回收;
stack_link.hpp
#pragma once
#include <iostream>
using namespace std;
// 结点模板结构体
template <class T>
struct LinkNode
{
explicit LinkNode(LinkNode<T>* node = NULL) : next(node) {}
explicit LinkNode(const T& data, LinkNode<T>* node = NULL) : data(data), next(node) {}
T data; // 数据项
LinkNode<T>* next; // 下一结点
};
// 链式栈模板类
template <class T>
class LinkStack
{
public:
LinkStack() : top_(NULL), size(0) {} // 构造函数(相当于创建了一个空的尾指针,作为 遍历/判断是否为空 的终止条件)
~LinkStack(); // 析构函数
LinkNode<T>* GetTopPtr() { return top_; }; // 获取栈顶结点指针
int GetSize() { return size; } // 获取栈中元素的个数
bool top(T& data) const; // 获取栈顶数据 data数据(保存数据项)
bool IsEmpty() const; // 判断栈是否为空
void push(const T& data); // 入栈
bool pop(); // 出栈
// 重载<<:用来打印栈中的元素
// 调用模板函数的代码必须拥有匹配的模板函数声明;声明必须包括与定义相同的模板参数;
template <class T> // 必须存在,否则会报错
friend ostream& operator<<(ostream& os, LinkStack<T>& stack);
private:
int size; // 保存栈的元素个数,即栈的大小
LinkNode<T>* top_; // 栈顶结点指针
};
// 析构函数 显式销毁时调用
template<class T>
LinkStack<T>::~LinkStack<T>()
{
// 清空栈:需要释放栈中每个元素
while (this->top_ != NULL)
{
LinkNode<T>* cur = this->top_;
top_ = top_->next;
delete cur;
cur = nullptr;
}
size = 0;
}
// 仅仅获取栈顶元素,不需要将栈顶元素删除
template <class T>
bool LinkStack<T>::top(T& data) const
{
if (IsEmpty())
{
return false;
}
else
{
data = this->top_->data;
return true;
}
}
// 判断栈是否为空
template<class T>
bool LinkStack<T>::IsEmpty() const
{
if (this->top_ == NULL)
{
return true;
}
else
{
return false;
}
}
// 入栈
template <class T>
void LinkStack<T>::push(const T& data)
{
LinkNode<T>* node = new LinkNode<T>(data);
node->next = this->top_;
this->top_ = node;
size++;
}
// 出栈
template <class T>
bool LinkStack<T>::pop()
{
if (IsEmpty())
{
return false;
}
else
{
LinkNode<T>* temp = this->top_;
this->top_ = this->top_->next;
delete temp;
temp = nullptr;
size--;
return true;
}
}
// 重载<<(打印栈) os:输出流 link_stack:栈
template<class T>
ostream& operator<<(ostream& os, LinkStack<T>& stack)
{
if (stack.IsEmpty())
{
cout << "empty stack" << endl;
}
else
{
LinkNode<T>* cur = stack.GetTopPtr();
os << "pop sequence : data" << endl;
for (int i = 1; cur != NULL; i++)
{
os << i << ":" << cur->data << endl;
cur = cur->next;
}
}
return os;
}
main.cpp
#include "stack_link.hpp"
int main()
{
LinkStack<int> test_stack;
test_stack.push(1);
test_stack.push(2);
test_stack.push(3);
test_stack.push(4);
int top_data;
if (test_stack.top(top_data))
{
cout << "the data of top is: " << top_data << endl;
}
if (test_stack.IsEmpty())
{
cout << "the number of elements inside the stack is : " << test_stack.GetSize() << endl;
}
LinkNode<int> *top_ptr1 = test_stack.GetTopPtr();
// test operator<<
cout << test_stack << endl;
test_stack.pop();
test_stack.pop();
test_stack.pop();
cout << test_stack << endl;
test_stack.pop();
LinkNode<int> *top_ptr2 = test_stack.GetTopPtr();
cout << test_stack << endl;
return 0;
}
二、队列
队列(queue):first in first out(FIFO),在队列的两端都能够进行操作,通过两个指针记录队列中状态front、rear(分别指向队列的前端和末端), front 会随着数据输出而改变,而 rear 则是随着数据输入而改变;当队列时空的时候,front == rear;元素入队时,rear向后移动;元素出队时,front向后移动;
队列的应用:CPU的调度Job Scheduling:利用队列完成作业的先到先执行的要求;外围设备联机并发处理系统spooling;图遍历的广度优先搜索BFS;
c++中,堆栈的逻辑结构是线性表,存储结构:顺序栈结构和链表;
1.顺序栈结构(数组)实现队列
代码分析:
1)用front、rear两个索引,进行队列中元素的定位;开始时,队列为空,front==rear==-1;
2)首次给元素enqueue入队时,front、rear都要加一,表示都指向了第一个元素;
3)随着元素继续enqueue入队,rear每次加一,表示指向最后一个元素;
4)在dequeue时,front才会再次变化,每dequeue一次减一,表示指向的队列中的首个元素;
5)当不断dequeue直到,front==rear && m_size=1时,再次dequeue的话,表示队列置空并将front和rear置为-1;
array_queue.hpp
#pragma once
#include<iostream>
using namespace std;
template<class T>
class MyArrQueue
{
public:
MyArrQueue(int capacity); // 构造函数:初始化栈空间的数组的容量和元素个数,以及栈空间的内存结构
~MyArrQueue(); // 析构函数:释放掉在堆区申请的用来存放栈结构的内存空间
// 获取栈空间的数组的容量和元素个数
int GetSize() { return m_size; }
int GetCapacity() { return m_capacity; }
int GetFrontIndex() { return front; }
int GetRearIndex() { return rear; }
// 判断栈空间中,元素的个数:空/满
bool IsFull() { return rear == m_capacity - 1; }
bool IsEmpty() { return m_size==0; }
void enqueue(const T& data); // 从rear的端口处,入队
void dequeue(T& data); // 从front的端口处,出队
void Print(); // 按照出栈的顺序打印元素
private:
int m_capacity;
int m_size;
T* arr_queue;
int front; // 队列中第一个元素的前一个位置的索引
int rear; // 队列中最后一个元素的索引
};
template<typename T>
MyArrQueue<T>::MyArrQueue(int capacity)
{
m_capacity = capacity;
front = -1; // 空队列中,front==rear==-1,都指向队列的第一个元素的前一个位置
rear = -1;
arr_queue = new T[m_capacity];
cout << "调用了有参构造函数MyArrQueue<T>::MyArrQueue(int capacity) " << endl;
}
template<typename T>
MyArrQueue<T>::~MyArrQueue()
{
delete[] arr_queue;
cout << "调用了析构函数MyArrQueue<T>::~MyArrQueue() " << endl;
}
template<typename T>
void MyArrQueue<T>::enqueue(const T& data) // 从rear的端口处,入队
{
if (IsFull())
{
cout << "queue overflow" << endl;
return;
}
// 添加一个元素,rear加一,rear是队列中的最后一个元素的索引
if (front == -1) // front==-1,表示是首次添加元素
{
front++;
}
rear++;
arr_queue[rear] = data;
m_size++;
}
template<typename T>
void MyArrQueue<T>::dequeue(T& data) // 从front的端口处,出队
{
if (IsEmpty())
{
cout << "empty queue" << endl;
return;
}
// 删除一个元素,front加一,即front是队列中第一个元素的索引
data = arr_queue[front];
if (front == rear && m_size == 1) // 当队列中只剩一个元素,经过此次出队后,front==rear==-1表示队列清零
{
front = -1;
rear = -1;
}
else
{
front++;
}
m_size--;
}
template<typename T>
void MyArrQueue<T>::Print()
{
if (IsEmpty())
{
cout << "empty stack" << endl;
return;
}
for (int i = front; i <= rear; i++)
{
cout << arr_queue[i] << " ";
}
cout << endl;
}
main.cpp
#include "array_queue.hpp"
int main()
{
MyArrQueue<int> queue_arr(10);
for (int i = 0; i < 10; i++)
{
queue_arr.enqueue(i);
}
queue_arr.Print();
if (!queue_arr.IsEmpty())
{
cout << "size = " << queue_arr.GetSize() << "\tcapacity = " << queue_arr.GetCapacity() << endl;
}
if (queue_arr.IsFull())
{
cout << "queue full" << endl;
}
queue_arr.enqueue(10); // 栈溢出
cout << endl;
int dequeue_data1, dequeue_data2;
queue_arr.dequeue(dequeue_data1);
queue_arr.dequeue(dequeue_data2);
cout << "dequeue data is " << dequeue_data1 << "、" << dequeue_data2 << endl;
queue_arr.Print();
cout << "the index of dequeue array's first element is " << queue_arr.GetFrontIndex() << ", the last is " << queue_arr.GetRearIndex() << endl;
cout << "size = " << queue_arr.GetSize() << "\tcapacity = " << queue_arr.GetCapacity() << endl;
cout << endl;
queue_arr.dequeue(dequeue_data2);
queue_arr.dequeue(dequeue_data2);
queue_arr.dequeue(dequeue_data2);
queue_arr.dequeue(dequeue_data2);
queue_arr.dequeue(dequeue_data2);
queue_arr.dequeue(dequeue_data2);
queue_arr.dequeue(dequeue_data2);
queue_arr.Print();
cout << "the index of dequeue array's first element is " << queue_arr.GetFrontIndex() << ", the last is " << queue_arr.GetRearIndex() << endl;
cout << endl;
// clear queue's all elements, check the value of front、rear、size、capacity
queue_arr.dequeue(dequeue_data2);
cout << "size = " << queue_arr.GetSize() << "\tcapacity = " << queue_arr.GetCapacity() << endl;
cout << "the index of dequeue array's first element is " << queue_arr.GetFrontIndex() << ", the last is " << queue_arr.GetRearIndex() << endl;
return 0;
}
2.单链表实现队列
代码分析:通过模板类LinkNode、LinkQueue实现队列的相关操作,包括元素的入队、出队、获取front指针/rear指针、重载<<来打印输出队列中的元素;最终通过析构函数释放掉new的队列空间;
link_queue.hpp
#pragma once
#include <iostream>
using namespace std;
// 链表队列结点模板结构体
template <class T>
class LinkNode
{
public:
LinkNode(LinkNode<T>* node = NULL) : next(node) {} // 构造函数(下一结点指针)
LinkNode(const T& data, LinkNode<T>* node = NULL) : data(data), next(node) {} // 构造函数(数据项&下一结点指针)
T data; // 结点数据项
LinkNode<T>* next; // 下一结点
};
// 链表队列模板类
template <class T>
class LinkQueue
{
public:
LinkQueue() : front_(NULL), rear_(NULL), m_size(0) {} // 构造函数
~LinkQueue(); // 析构函数
int GetSize() const { return m_size; } // 获取队列长度
LinkNode<T>* GetFrontPtr() { return this->front_; } // 获取队头结点指针
LinkNode<T>* GetRearPtr() { return this->rear_; } // 获取队尾结点指针
bool IsEmpty() const; // 判断队列是否为空
void enqueue(const T& data); // 入队
void dequeue(); // 出队(不保存数据)
// 重载<<(打印队列)
template<typename T>
friend ostream& operator<<(ostream& os, LinkQueue<T>& link_queue);
private:
int m_size;
LinkNode<T>* front_; // 队头指针
LinkNode<T>* rear_; // 队尾指针
};
template<class T>
LinkQueue<T>::~LinkQueue()
{
LinkNode<T>* temp = this->front_;
while (temp != NULL)
{
LinkNode<T>* temp2 = temp->next;
delete temp;
temp = temp2;
}
m_size = 0;
cout << "调用了模板类的析构函数LinkQueue<T>::~LinkQueue()" << endl;
}
template<class T>
bool LinkQueue<T>::IsEmpty() const // 判断队列是否为空
{
if (this->front_ == NULL || m_size == 0)
{
return true;
}
else
{
return false;
}
}
template<class T>
void LinkQueue<T>::enqueue(const T& data) // 入队
{
LinkNode<T>* new_node = new LinkNode<T>(data);
if (new_node == NULL)
{
return;
}
if (IsEmpty())
{
// 空队列或者队列中只有一个元素时,front==rear
this->front_ = new_node;
this->rear_ = new_node;
}
else
{
this->rear_->next = new_node;
this->rear_ = new_node;
}
m_size++;
}
template<class T>
void LinkQueue<T>::dequeue() // 出队(不保存数据)
{
if (IsEmpty())
{
return;
}
LinkNode<T>* cur = this->front_;
this->front_ = this->front_->next;
delete cur;
cur = nullptr;
m_size--;
}
template<class T>
ostream& operator<<(ostream& os, LinkQueue<T>& link_queue) // 重载<<(打印队列)
{
LinkNode<T>* temp = link_queue.GetFrontPtr();
for (int i = 1; temp != NULL; i++)
{
os << temp->data << " ";
temp = temp->next;
}
os << "\n";
return os;
}
main.cpp
#include "link_queue.hpp"
int main()
{
// 元素入队
LinkQueue<int> int_queue;
int_queue.enqueue(1);
int_queue.enqueue(2);
int_queue.enqueue(3);
int_queue.enqueue(4);
// 用重载的<<,打印队列中的元素
cout << int_queue;
// 元素出队
int_queue.dequeue();
int_queue.dequeue();
if (!int_queue.IsEmpty())
{
cout << "int_queue's size: " << int_queue.GetSize() << endl;
}
else
{
cout << "LinkinPark_song_queue is empty." << endl;
}
// 测试队列获取队头/获取队尾指针/元素
int front_data = int_queue.GetFrontPtr()->data;
int rear_data = int_queue.GetRearPtr()->data;
cout << "The front of the int_queue: " << front_data << endl;
cout << "The rear of the int_queue: " << rear_data << endl;
return 0;
}
三、环形队列
1、顺序结构(数组)实现环形链表
普通队列有着先入先出的特性,但是普通队列如果删除队头,所删除的空间将不会被释放,又由于队列只能由队尾插入,这就导致被删除部分的空间被浪费。循环队列就是用于解决普通队列所存在的内存浪费的问题,即将队列串起来形成一个类似于环的结构,出队后留下的空间,任然能够通过rear来给其中,添加元素;
循环队列相对于普通队列的操作不同点:1)判断循环队列满的条件:不再是rear == m_capacity-1,而是改成(rear - front + m_capacity) % m_capacity;2)入队操作时,rear = (rear + 1) % m_capacity;3)出队操作时,front = (front + 1) % m_capacity;
注意:为了不让rear和front的值超过m_capacity的大小,故在rear和front自增时候模m_capacity;空队时,rear == front;满队时必须空一个位置;size只是表示队列中元素的个数;
代码分析:通过模板类来实现队列的出队,入队、得到元素个数和最容量(动态变化的,当存满后,自动的将容量扩大二倍,故在调用时不用担心capacity容量不够的问题)、通过重载[]运算符,实现队列元素按照索引进行访问和修改;
circular_queue.hpp
#include <iostream>
#include <cstdlib>
#include <memory.h>
using namespace std;
template<class T>
class CicularQueue
{
private:
int m_capacity; // 队列最大容量
int front; // 队列头结点的索引
int rear; // 队列尾结点的索引
void resize(int oldCapacity, int newCapacity);
T *m_CircularQueueArr; // 队列数组
// front和rear之间必须有空余的位置,当只有一个空余的位置时,认为环形队列为空;
bool IsFull() { return (rear + 1) % m_capacity == front; } // 检查队列是否已满
public:
// 只有空队列时,front == rear才能成立;
bool IsEmpty() { return rear == front; } // 检查队列是否为空
CicularQueue(int capacity); // 构造函数
~CicularQueue(); // 析构函数
int GetFrontIndex() { return front; } // 队列中第一个元素的索引
int GetRearIndex() { return rear; } // 队列中最后一个元素的后一个位置的索引
// 重载[]运算符,实现队列中元素的读取和修改
T operator[](int index) const { return m_CircularQueueArr[index]; } // 获取元素(读取)
// 必须引用的返回形式int &,因为等号左边必须是左值
T& operator[](int index) { return m_CircularQueueArr[index]; } // 修改元素
// (rear - front + m_capacity) % m_capacity中,加m_capacity时为了防止出现rear-front为负值的问题
int size() { return (rear - front + m_capacity) % m_capacity; } // 当前队列中元素的个数
// 申请的m_capacity个类T的内存空间,但在环形队列中只有m_capacity-1个空间能够使用
int capacity() { return m_capacity - 1; }
void enqueue(const T& data); // 入队
T dequeue(); // 出队
void Print(); // 显示队列
};
template<class T>
CicularQueue<T>::CicularQueue(int capacity)
{
if (capacity == -1)
{
cout << "Create queue error!" << endl;
return;
}
m_capacity = capacity;
front = 0;
rear = 0;
m_CircularQueueArr = new T[m_capacity];
// memset():作用是在一段内存块中填充某个给定的值;用memset初始化完后,后面程序中再向该内存空间中存放需要的数据;
memset(m_CircularQueueArr, 0, sizeof(T) * m_capacity);
}
template<class T>
CicularQueue<T>::~CicularQueue() //析构函数
{
delete[] m_CircularQueueArr;
m_CircularQueueArr = nullptr;
}
template<class T>
void CicularQueue<T>::resize(int oldCapacity, int newCapacity)
{
// 按照新的容量申请新的内存空间,并将原内存空间中的元素拷贝到新的内存空间
m_capacity = newCapacity;
T* newCircularQueueArr = new T[m_capacity];
int i = 0;
int temp_front = front;
while (temp_front != rear)
{
newCircularQueueArr[i++] = m_CircularQueueArr[temp_front];
temp_front++;
temp_front = temp_front % oldCapacity;
}
// 先释放掉原内存空间,然后让arr指向新的内存空间的首地址
delete[] m_CircularQueueArr;
m_CircularQueueArr = newCircularQueueArr;
front = 0;
rear = i;
}
template<class T>
void CicularQueue<T>::enqueue(const T& data) //入队
{
if (IsFull())
{
cout << "Queue Overflow and Capacity Expansion" << endl;
resize(m_capacity, 2 * m_capacity);
return;
}
m_CircularQueueArr[rear] = data;
rear = (rear + 1) % m_capacity;
}
template<class T>
T CicularQueue<T>::dequeue() //出队
{
if (IsEmpty())
{
cout << "empty Queue!" << endl;
return (T)-1;
}
int result = m_CircularQueueArr[front];
m_CircularQueueArr[front] = 0; // 表示清空该位置的元素
front = (front + 1) % m_capacity;
return result;
}
template<class T>
void CicularQueue<T>::Print() // 显示队列
{
if (IsEmpty())
{
cout << "empty Queue!" << endl;
return;
}
int temp_front = front;
while (temp_front != rear)
{
cout << m_CircularQueueArr[temp_front] << " ";
temp_front++;
temp_front = temp_front % m_capacity;
}
cout << endl;
}
Queue_circular.cpp
#include "circular_Queue.hpp";
int main()
{
// 申请的m_capacity个类T的内存空间,但在环形队列中只有m_capacity-1个空间能够使用
CicularQueue<int> *test_CircularQueue = new CicularQueue<int>(5);
// 此段代码为了验证,rear>front且front=0,没达到队满的状况
for (int i = 1; i <= 5; i++)
{
test_CircularQueue->enqueue(i);
}
test_CircularQueue->Print();
cout << "the size of the Circular Queue is " << test_CircularQueue->size() << ", max capacity is " << test_CircularQueue->capacity() << endl;
cout << "队列中第一个元素的索引:" << test_CircularQueue->GetFrontIndex()
<< "\t队列中最后一个元素的后一个位置的索引:" << test_CircularQueue->GetRearIndex()
<< "\t队列中最后一个元素:" << (*test_CircularQueue)[test_CircularQueue->GetRearIndex() - 1] << endl; // 重载[]运算符,实现队列中元素的读取
(*test_CircularQueue)[test_CircularQueue->GetRearIndex() - 1] = 6; // 重载[]运算符,修改队列中最后一个元素为6
cout << "重载[]运算符,实现队列中元素的修改:" << endl;
test_CircularQueue->Print();
cout << endl;
// 此段代码为了验证,rear<front且没达到队满的状况
test_CircularQueue->dequeue();
test_CircularQueue->dequeue();
test_CircularQueue->dequeue();
for (int i = 5; i <= 12; i++)
{
test_CircularQueue->enqueue(i);
}
test_CircularQueue->Print();
cout << "队列中第一个元素的索引:" << test_CircularQueue->GetFrontIndex()
<< "\t队列中最后一个元素的后一个位置的索引:" << test_CircularQueue->GetRearIndex()
<< "\t队列中最后一个元素:" << (*test_CircularQueue)[test_CircularQueue->GetRearIndex() - 1] << endl;
cout << endl;
// 此段代码为了验证,队满之后,重新申请2倍的容量空间并将元素拷贝到新的内存空间(此时front = 0;rear = size()元素个数),同时释放旧的内存空间
for (int i = 5; i <= 14; i++)
{
test_CircularQueue->enqueue(i);
}
test_CircularQueue->Print();
cout << "the size of the Circular Queue is " << test_CircularQueue->size() << ", max capacity is " << test_CircularQueue->capacity() << endl;
cout << "队列中第一个元素的索引:" << test_CircularQueue->GetFrontIndex()
<< "\t队列中最后一个元素的后一个位置的索引:" << test_CircularQueue->GetRearIndex()
<< "\t队列中最后一个元素:" << (*test_CircularQueue)[test_CircularQueue->GetRearIndex() - 1] << endl;
// 释放整个动态的数组,构成的环形队列的内存空间
delete test_CircularQueue;
test_CircularQueue = nullptr;
return 0;
}
2、单链表实现环形队列
循环链表实现环形队列,不用考虑队满的情况;实现时,只采用一个队尾指针指向的队尾节点,因为队尾指针的下一个节点就是队首节点;队空条件:队尾节点的内容为空;进队操作:将新的节点插入到队尾,称为队尾节点,然后队尾指针指向这个新的节点;出队操作:将队尾节点的下一个节点数据取出,并绕过该节点;
代码分析:通过模板类实现链表环形队列,进行入队和出队操作,并计算队列中数据元素的个数、以及判断队列为空;通过函数来获取,指定的队头数据结点指针和队尾数据结点指针;通过重载<<运算符,来打印队列中的数据元素(按照对头到队尾的顺序)
link_circular_queue.hpp
#pragma once
#include<iostream>
using namespace std;
template<class T>
class LinkCircularQueueNode
{
public:
LinkCircularQueueNode(LinkCircularQueueNode<T>* node = NULL) : next(node) {} // 构造函数(下一结点指针)
LinkCircularQueueNode(const T& data, LinkCircularQueueNode<T>* node = NULL) : data(data), next(node) {} // 构造函数(数据项&下一结点指针)
T data;
LinkCircularQueueNode<T>* next;
};
template <class T>
class LinkCircularQueue
{
public:
LinkCircularQueue() :m_size(0), front(NULL), rear(NULL) {}
~LinkCircularQueue();
int size() { return m_size; } // 获取队列长度
LinkCircularQueueNode<T>* GetFrontPtr() { return this->front; } // 获取队头结点指针
LinkCircularQueueNode<T>* GetRearPtr() { return this->rear; } // 获取队尾结点指针
bool empty() const { return front == NULL; }
void enqueue(T data);
T dequeue();
template<class T>
friend ostream& operator<<(ostream& os, LinkCircularQueue<T>& link_CircularQueue);
private:
int m_size;
LinkCircularQueueNode<T>* front;
LinkCircularQueueNode<T>* rear;
};
template<class T>
void LinkCircularQueue<T>::enqueue(T data)
{
LinkCircularQueueNode<T>* node = new LinkCircularQueueNode<T>(data);
if (m_size == 0)
{
this->front = node;
this->front->next = node; // 尾结点的下一个结点指针指向头结点,构成一个环形链表队列
m_size++;
return;
}
if (m_size == 1)
{
this->rear = node;
this->front->next = this->rear;
this->rear->next = this->front;
m_size++;
return;
}
else
{
this->rear->next = node;
this->rear = node;
this->rear->next = this->front;
m_size++;
return;
}
}
template<class T>
T LinkCircularQueue<T>::dequeue()
{
if (m_size == 0)
{
cout << "empty queue" << endl;
return (T)-1;
}
if (m_size == 1) // 此时,头指针front和尾指针rear,指向同一块内存,释放其中的一个另一个指针就无法访问到该内存
{
T temp_data = this->front->data;
delete this->front;
this->front = nullptr;
this->rear = nullptr;
m_size == 0;
return temp_data;
}
else
{
LinkCircularQueueNode<T>* temp = this->front->next;
T temp_data = temp->data;
delete this->front;
this->front = temp;
this->rear->next = this->front;
m_size--;
return temp_data;
}
}
template<class T>
LinkCircularQueue<T>::~LinkCircularQueue()
{
cout << "call the function, delete the circular link queue" << endl;
while (this->front == this->rear)
{
LinkCircularQueueNode<T>* temp = this->front;
this->front = this->front->next;
delete temp;
temp = nullptr;
}
delete this->front;
this->front = nullptr;
this->rear = nullptr;
m_size = 0;
}
template<class T>
ostream& operator<<(ostream& os, LinkCircularQueue<T>& link_CircularQueue)
{
LinkCircularQueueNode<T>* temp = link_CircularQueue.front;
cout << temp->data << " ";
temp = temp->next;
while (temp != link_CircularQueue.front)
{
cout << temp->data << " ";
temp = temp->next;
}
cout << endl;
return os;
}
main.cpp
#include"link_circular_queue.hpp"
int main()
{
LinkCircularQueue<int> *int_link_CircularQueue = new LinkCircularQueue<int>();
if (int_link_CircularQueue->empty())
{
cout << "empty circular link queue" << endl;
}
// 入队
for (int i = 0; i < 10; i++)
{
int_link_CircularQueue->enqueue(i);
}
cout << (*int_link_CircularQueue) << endl; // 重载operator<<(ostream& os, LinkCircularQueue<T>& link_CircularQueue);打印队列
cout << int_link_CircularQueue->size() << endl;
cout << "the front pointer's data is " << int_link_CircularQueue->GetFrontPtr()->data << endl;
cout << "the rear pointer's data is " << int_link_CircularQueue->GetRearPtr()->data << endl;
for (int i = 0; i < 5; i++)
{
int_link_CircularQueue->dequeue();
}
cout << (*int_link_CircularQueue) << endl;
cout << int_link_CircularQueue->size() << endl;
cout << "the front pointer's data is " << int_link_CircularQueue->GetFrontPtr()->data << endl;
cout << "the rear pointer's data is " << int_link_CircularQueue->GetRearPtr()->data << endl;
delete int_link_CircularQueue;
int_link_CircularQueue = nullptr;
return 0;
}