您的位置:首页 > 其它

基于链式存储的队列

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

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

#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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: