一、队列的定义
队列(简称为队,又称先进先出表)是一种操作受限的线性表,其限制为仅允许在表的一端进行插入,而在表的另一端进行删除。把进行插入的一端称作队尾,进行删除的一端称作队头或队首。向队列中插入新元素称为进队或者入队,新元素进队后就成为新的队尾元素;从列中删除元素称为出队或者离队,元素出队后,其直接后继元素就成为队首元素。
二、队列的存储方式
1、顺序存储结构(采用顺序存储结构的队列称为顺序队)
使用一个数组data(大小为常量MaxSize)顺序存储队列中的所有元素,两个整型变量front和rear分别作为队首指针和队尾指针。
顺序队类SqQueueClass的定义:
class SqQueueClass
{
const int MaxSize=100; //最多元素个数
public string[] data; //存放队中的元素
public int front,rear; //队头和队尾指针
public int count; //队列中的元素个数
public SqQueueClass() //构造函数,用于初始化队列
{
data=new string[MaxSize];
front=rear=x;
count=0; //通常情况下,循环队列x为0,非循环队列x为-1
}
}
1.1在非循环队列中实现队列的基本运算
说明:在顺序队中,队尾指针总是指向当前队列中队尾的元素,而队头指针总是指向当前队列中队头元素的前一个位置。
在非循坏队列中实现队列的基本运算算法如下:
判断队列是否为空QueueEmpty()
若队列满足front==rear,返回true,否则返回false。
public bool QueueEmpty()
{
return(front==rear);
}
进队enQueue(e)
元素进队只能从队尾进,不能从队头或者中间位置进队。在进队运算中,在队列不满的情况下,先将rear循环加一,然后将元素e放在该位置处。
public bool enQueue(string e)
{
If(rear==MaxSize-1) //队满上溢出
return false; //返回false
rear++;
data[rear]=e;
return true;
}
出队deQueue(e)
元素出队只能从队头出,不能从队尾或者中间位置出队。在出队运算中,在队列不为空的情况下,将队首指针front增1,并将该位置的元素值赋给e。
public bool deQueue(ref string e)
{
If(front==rear) //队满下溢出
return false; //返回false
front++;
e=data[front];
return true;
}
1.2在循环队列中实现队列的基本运算
在非循环队列中,元素进队时队尾指针rear增1,元素出队时队头指针front增1,当进队MaxSize个元素后,满足队满的条件,即rear==MaxSize-1成立,此时即使出队若干元素,队满条件仍成立(实际上队列中有空位置),这是一种假溢出。为了能够充分地使用数组中的存储空间,把数组的前端和后端连接起来,形成一个循环的列表,即把存储队列元素的表从逻辑上看成一个环,称为循环队列(也称为环形队列)。
说明:在循环队列中,队首指针front指向队中队头元素的前一个位置,队尾指针rear指向队中的队尾元素,队中的元素个数=(rear-front+MaxSize)%MaxSzie
在循坏队列中实现队列的基本运算算法如下:
判断队列是否为空QueueEmpty(q)
若队列满足front==rear,返回true,否则返回false。
public bool QueueEmpty()
{
return(front==rear);
}
进队enQueue(q,e)
在队列不满的情况下,先将rear循环加一,然后将元素e放在该位置处。
public bool enQueue(string e)
{
If((rear+1)%MaxSize==front) //队满上溢出
return false; //返回false
rear=(rear+1)%MaxSize;
data[rear]=e;
count++;
return true;
}
出队deQueue(q,e)
在队列不为空的条件下,将队首指针front循环增1,并将该位置的元素赋值给e.
public bool deQueue(ref string e)
{
If(front==rear) //队空下溢出
return false; //返回false
front=(front+1)%MaxSzie;
e=data[front];
count--;
return true;
}
求队列中的元素个数GetCount()
public int GetCount()
{
return count;
}
显示队列中的所有元素DispQueue()
public string DispQueue()
{
int i=front+1;
string mystr = "";
while(i!=(rear+1)%MaiSize)
{
mystr+=data[i]+””;
i=(i+1)%MaxSize;
}
return mystr;
}
2、链式存储结构(用于存储队列的单链表简称为队列)
队列的链式存储结构是通过由结点构成的单链表实现的,此时只允许在单链表的表首进行删除操作和在单链表表尾进行插入操作,因此需要使用两个指针:队首指针front和队尾指针rear。用front指向队首结点,用rear指向队尾结点。
链队数据结点类LinkNode的定义:
class LinkNode //链队数据结点类
{
public string data; //结点数据字段
public LinkNode next; //指向下一个结点
}
链队结点类LinkQueue的定义:
class LinkQueue //链队结点类
{
public LinkNode front; //指向队头结点
public LinkNode rear; //指向队尾结点
}
设计链队类LinkNodeClass如下:
class LinkNodeClass
{
LinkQueue Q=new LinkQueue();
public LinkQueueClass()
{
Q.front=null;
Q.rear=null;
}
}
对应队列的基本运算算法如下:
判断队列是否为空QueueEmpty()
若链队结点的rear域值为null,表示队列为空,返回true;否则返回false。
public bool QueueEmpty()
{
return(Q.rear==null);
}
进队enQueue()
创建data域为e的数据结点p。若原队列为空,则将链队结点的两个域均指向p结点,否则,将p结点链到单链表的末尾,并让链队结点的rear域指向它。
Public void enQueue(string e)
{
LinkNode p=new LinkNode();
p.data=e;
p.next=null;
if(Q.rear==null) //若队列为空,则新结点既是队首结点,又是队尾结点
Q.front=Q.rear=p;
else
{
Q.rear.next=p; //将p结点链到队尾,并将rear指向它
Q.rear=p;
}
}
出队deQueue()
若原队列不为空,则将第一个数据结点的data域值赋给e,并删除它。若出队之前队列中只有一个结点,则需将链队结点的两个域均置为null,表示队列已为空。
public bool deQueue(ref string e)
{
LinkNode p;
If(Q.rear==null) //队列为空
return false;
p=Q.front; //p指向第一个数据结点
if(Q.front==Q.rear) //队列中只有一个结点时
Q.front=Q.rear=null;
else //队列中有多个结点时
Q.front=Q.front.next;
e=p.data;
p=null;
return true;
}
求链队中的元素个数GetCount()
public int GetCount()
{
LinkNode p;
if(Q.rear==null)
return 0;
p=Q.front;
int n=0;
while(p!=null)
{
n++;
p=p.next;
}
return n;
}
显示队列中的所有元素DispQueue()
public string DispQueue()
{
LinkNode p;
string mystr;
if(Q.rear==null)
mystr+=”队列为空”;
p=Q.front;
while(p!=null)
{
mystr+=p.data+” ”;
p=p.next;
}
return mystr;
}
三、不同存储方式的比较
1、链表存储结构的内存地址不一定是连续的,但顺序存储结构的内存地址一定是连续的
2、链式存储适用于较频繁地插入、删除、更新元素时使用,不能随机地存取元素;而顺序存储结构适用于频繁查询时使用,且顺序表具有按元素序号随机访问的特点。
3、链式结构每一个结点都有一个指针存储域,因此,顺序存储结构比链式存储结构节省空间。