队列是一种特殊的线性表,它的特殊之处在于队列的插入和删除操作分别在表的两端进行。插入的那一端称为队尾,删除的那一端称为队首。队列的插入操作和删除操作分别简称为进队和出队。
生活中的排队购物等现象就是队列的例子。它的特点是先到先享受购票服务,对于一个队列
k
0
,
k
1
,
k
2
.
.
.
,
k
n
?
1
{k_0,k_1,k_2...,k_{n-1}}
k0?,k1?,k2?...,kn?1?
如果 k 0 k_0 k0?那端是队首, k n ? 1 k_{n-1} kn?1?那端是队尾,则 k 0 k_0 k0?是这些结点中最先插入的结点,若要进行删除操作, k 0 k_0 k0?将首先被删除,所以说,队列有“先进先出”(First In First Out,FIFO)特点的线性结构。
在C语言中,队列的顺序存储可以用一维数组表示。为了标识队首和队尾,需要附设两个指针fronth和rear,front指示的是队列中最前面,即队首结点在数组中元素的下标。rear指示的是队尾结点在数组中元素下标的下一个位置,也就是说rear指示的是即将插入的结点在数组中的下标。
下面所示是队列的几种状态。
①空队列
②连续插入几个结点后的状态
③连续删除几个结点后的状态
④在③状态下再删除一个结点后的状态
⑤连续插入若干个结点后的状态,但数组前部有空位置
文件名seqqueue.h
#define MAXSIZE 100
typedef int datatype;
typedef struct
{
datatype a[MAXSIZE];
int front;
int rear;
}sequence_queue;
void init(sequence_queue *sq)
{
//这里注意rear和front不是指针类型变量
//注意看结构体定义 front 和rear都为int型
//即初始化让让front 和rear都指向数组的第一个元素的位置
sq->front=sq->rear=0;
}
int empty(sequence_queue sq)
{
//判空条件 front==rear
return (sq.front==sq.rear?1:0);
}
void display(sequence_queue sq)
{
int i;
if(empty(sq))
printf("\n顺序队列是空的。");
else
{
for (i=sq.front;i<sq.rear;i++)
{
printf("%5d",sq.a[i]);
}
}
}
datatype get(sequence_queue sq)
{
if(empty(sq))
{
printf("\n顺序队列是空的!无法获得队首结点值!");
exit(1);
}
return sq.a[sq.front];
}
void insert(sequence_queue *sq,datatype x)
{
int i;
if (sq->rear==MAXSIZE)
{
printf("\n顺序队列是满的!");
exit(1);
}
else
sq->a[sq->rear]=x;
sq->rear=sq->rear+1;
}
void del(sequence_queue *sq)
{
if (sq->rear==sq->front)
{
printf("\n顺序队是空的!不能做删除操作!");
exit(1);
}
sq->front++;
}
给定一个大小为MAXSIZE的数组存储一个队列,经过若干次插入和删除操作后,当队尾指针rea==MAXSIZE时,呈现队满的状态,而事实上数组的前部分可能还有空闲的位置。为了有效利用空间,将顺序存储的队列想象成一个环状,把数组中的最前面和最后面的两个元素看作是相邻的,这就是 循环队列。
在循环队列中,如果队列中最后一个结点存放在数组的最后一个位置,而数组前面有空位置的话,则下次再进行插入操作时,将插入数组最前面那个元素的位置。其他情况下的插入操作和一般的队列的插入操作一样。
下面给出循环队列的几种状态。
①空的循环队列
②插入几个元素之后的循环队列
③剩余一个空间的循环队列
④只有一个结点的循环队列
在第三种情况下:
如果再插入一个新的结点,则数组空间被全被占用,队列已满,且rear=front。
在第④种情况下:
若删除一个结点,则队列成为空队列,此时也有rear=front。
也就是说循环队列队满与空的条件都是rear=front。
这个问题如何解决呢?
一种方法是设置一个标志,标识是由于rear增1使得rear=front还是front增1使rear=front,前者是队满而后者是队空。
另一种方法是牺牲一个数组元素的空间,即若数组的大小是MAXSIZE,则该数组所表示的循环队列最后允许存储MAXSIZE-1个结点。这样队满的条件是:
(rear+1)%MAXSIZE=front
而对空的条件是:
rear=front
循环队列的顺序存储与一般队列一致,但是循环队列基本操作的实现和一般队列是有去区别的。主要区别在于判断对空与队满的条件以及相关指针增1的表达上,循环队列中指针增1时要增加一个取模操作,使之成为一个循环队列。
void insert_sequence_cqueue(sequence_queue *sq,datatype x)
{
if((sq->rear+1)%MAXSIZE==sq->front)
{
printf("\n顺序循环队列是满的!无法进行插入操作")
}
sq->a[sq->rear]=x;
sq->rear=(sq->rear+1)%MAXIZE;
}
void dele_sequence_cqueue(sequence_queue *sq)
{
if (sq->front==sq->rear)
{
printf("\n循环队列是空的!无法进行删除操作!");
exit(1);
}
sq->front(sq->front+1)%MAXSIZE;
}