基于链式存储的队列

日常生活中我们吃饭,买票等都是要排队的,这里的排队其实就是对应着数据结构中的队列了。其中排队过程中不允许插队也是一个能反应队列结构性质的表现吧。队列和栈一样,是一个被限制存取的线性结构。普通队列规定只能在队头出,只能在队尾进,这也是为什么不允许插队的原因了。当然这里讨论的是最简单的最普通的队列了,当然还是有一些特殊的队列的,比如双端队列,优先级队列等,后续细说。

基于链式存储的队列结构中,结点类型和之前的单链表是类似的,简直是一样的。

#pragma once
#include<iostream>
using namespace std;
template<class T>
class LinkNode
{
public:
	T data;
	LinkNode<T> *next;//指针域
public:
	LinkNode();
	LinkNode(T &da);
	~LinkNode();
};

template<class T>
LinkNode<T>::LinkNode()//初始化 用于构建一个头结点时使用
{
	next = NULL;//指针域默认为NULL
}
template<class T>
LinkNode<T>::LinkNode(T &da)
{
	data = da;//初始化 用于根据元素值new出新的结点
	next = NULL;
}
template<class T>
LinkNode<T>::~LinkNode()
{
	//可以为空
}

链式队列和单链表比起来是多了一个队尾指针,用来指向最后一个结点,队头指针仍然指向当前队列的第一个结点。进队列的时候要考虑一个问题,那就是这个队列是不是为空的。如果是空队列的话,那需要将队头指针(front)和队尾指针(rear)同时指向新的结点(同时也是第一个结点)。如果队列不为空的话,那就直接将新结点被rear所指即可。所以这里在进队列操作的时候要多一步考虑了。可是我们可以像单链表一样,增加一个头结点,这个头结点什么也不做,只是开辟出来一个结点空间让对头和队尾都指向即可。这时候再进队列的时候就不用考虑队列是否为空了,直接链接在rear的后面OK。

#pragma once
#include"LinkNode.h"
#include<iostream>
using namespace std;
template<class T>
class Queue
{
protected:
	LinkNode<T> *front;//头指针
	LinkNode<T> *rear;//尾指针
public:
	Queue();
	Queue(Queue<T> &Q);//队列 深复制
	~Queue();
	void ClearQueue();//销毁队列

	T De_Queue();//出队列,仅仅在头指针改动,(要做一个 当前出队列指针是否为尾指针的判断)返回元素值,也可返回指针,但后者易造成内存泄露
	void In_Queue(T &elem);//入队列 仅仅在尾指针改动
	LinkNode<T> * GetHead();//返回队列头指针
	LinkNode<T> * GetRear();//返回队列尾指针
	int Length();//返回队列的元素个数
	void output();//按顺序 一次性输出队列
	void operator= (Queue<T> &Q);//复制
};

template<class T>
Queue<T>::Queue()
{
	front = new LinkNode<T>;//调用无参 构造函数,data域未知,指针域为NULL
	rear = front;//初始化 把头指针赋给尾指针,在随后的插入过程中 尾指针后移
}
template<class T>
Queue<T>::Queue(Queue<T> &Q)
{
	front = new LinkNode<T>;//先初始化当前队列
	rear = front;
	//Q为空的时候 直接退出
	if(Q.GetHead() == Q.GetRear())//首尾指针相同 即为空队列
	{
		cout<<"空队列 无须复制构造"<<endl;
		exit(1);
	}
	//下面执行的都是在Q为非空的情况下
	LinkNode<T> *current = Q.GetHead() ->next;//从实际上的队列第一位 开始遍历
	LinkNode<T> *Q_REAR = Q.GetRear();//返回尾指针 作为循环判断的条件
	LinkNode<T> *newNode;
	T elem;
	while(current != Q_REAR)//在当前指针没碰到队尾指针时
	{
		elem = current->data;//提取 值域
		newNode = new LinkNode<T>(elem);//根据元素值 构造一个完全一样的

		rear->next = newNode;//插入
		rear = newNode;

		current = current->next;
	}
	//剩下尾指针的没有复制
	elem = Q_REAR->data;//Q的队尾指针
	newNode = new LinkNode<T>(elem);
	rear->next = newNode;
	rear = newNode;
}
template<class T>
Queue<T>::~Queue()
{
	Queue<T>::ClearQueue();//销毁
}

template<class T>
void Queue<T>::ClearQueue()
{
	if(front == rear)
	{
		cout<<"空队列,无须销毁"<<endl;
		//exit(1);
	}
	//不为空队列
	LinkNode<T> *del = front->next;
	while(del != rear)
	{
		front->next = del->next;//前移
		delete del;
		del = front->next;
	}
	//如果不为空但跳过循环则为队列中一个元素
	delete del;//删除尾指针
	rear = front;//置为空队列
}

template<class T>
T Queue<T>::De_Queue()//出队列 删除队首结点
{
	T elem;
	LinkNode<T> *del = front->next;//把头结点的next赋给del 待删除
	front->next = del->next;//往前移动一位
	if(rear == del)//判断要删除的指针是否为队尾的元素,如果是删除之后就是空队列
		rear = front;//空队列 把首指针赋给尾指针
	elem = del->data;//提取待删除指针的 值域
	delete del;
	return elem;
}

template<class T>
void Queue<T>::In_Queue(T &elem)//入队列,后面修改尾指针即可 不需做判断
{
	LinkNode<T> *newNode = new LinkNode<T>(elem);
	if(newNode == NULL)
	{
		cout<<"内存分配失败"<<endl;
		exit(1);
	}

	rear->next = newNode;//把新结点 接到尾指针的后面
	rear = newNode;//再把尾指针后移,两个顺序不能变
}

template<class T>
LinkNode<T> * Queue<T>::GetHead()
{
	return front;//首指针
}
template<class T>
LinkNode<T> * Queue<T>::GetRear()
{
	return rear;//尾指针
}

template<class T>
int Queue<T>::Length()
{
	int count = 0;//局部变量最好初始化 否则在无意中使用时会出错
	LinkNode<T> *current = front;//把首指针赋给当前指针用于遍历
	while(current != NULL)//如果为空队列 则不执行循环 直接返回count=0
	{
		current =current->next;
		count++;
	}
	return count;
}

template<class T>
void Queue<T>::output()
{
	int count = 0;
	LinkNode<T> *current = front->next;
	while(current != rear)//没碰到尾指针
	{
		cout<<"#"<<count+1<<":"<<current->data<<endl;
		current = current->next;
		count++;
	}
	//输出队尾
	cout<<"#"<<count+1<<":"<<rear->data<<endl;
}
template<class T>
void Queue<T>::operator= (Queue<T> &Q)
{
	front = new LinkNode<T>;//先初始化当前队列
	rear = front;//
	//Q为空的时候 直接退出
	//if(Q.GetHead() == Q.GetRear())//首尾指针相同 即为空队列
	//{
	//	cout<<"空队列 无须复制构造"<<endl;
	//	//exit(1);
	//}

	//Queue<T>::ClearQueue();//在被复制之前 先销毁队列

	//下面执行的都是在Q为非空的情况下
	LinkNode<T> *current = Q.GetHead() ->next;//从实际上的队列第一位 开始遍历
	LinkNode<T> *Q_REAR = Q.GetRear();//返回尾指针 作为循环判断的条件
	LinkNode<T> *newNode;
	T elem;
	while(current != Q_REAR)//在当前指针没碰到队尾指针时
	{
		elem = current->data;//提取 值域
		newNode = new LinkNode<T>(elem);//根据元素值 构造一个完全一样的

		rear->next = newNode;//插入
		rear = newNode;

		current = current->next;
	}
	//剩下尾指针的没有复制
	elem = Q_REAR->data;//Q的队尾指针
	newNode = new LinkNode<T>(elem);
	rear->next = newNode;
	rear = newNode;
}
下面是主函数的测试:

#include"queue.h"
#include<iostream>
using namespace std;
void main()
{
	Queue<int> qu;
	for(int i=0;i<5;i++)
		qu.In_Queue(i);
	qu.output();
	cout<<endl;
	Queue<int> qu1;
	for(int i=0;i<3;i++)
		qu1.In_Queue(i);
	qu1.output();
	cout<<endl;
	qu1 = qu;
	qu1.output();
}

你可能感兴趣的:(数据结构,算法)