数据结构与算法学习笔记(四)堆栈和队列

        堆栈和队列,是两种相当典型的抽象数据类型,也是一种有特定进出规则的线性表;主要特性:根据自身的规则按位置插入和删除数据;本文主要,通过数组和单链表来实现堆栈(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;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白要努力sgy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值