今天我们来聊聊队列这种数据结构。
队列是一种只允许在一端插入数据,在另一端删除数据的线性表,插入数据的一端称作队尾,删除数据的一端称作队头。队列是一种先进先出的数据结构,也就是FIFO结构。关于队列,生活中有很多例子可以加深我们对它的理解。比如我们排队买饭,排在前面的人会先买到饭,然后离开队伍,自己找座位坐下吃饭。后面来到的人只能排在队伍的后面等待着买饭。每当排在最前面的人打好了饭,大家就都会向前移动一个位置。
既然我们已经有了普通的线性表,干嘛又搞出个队列这种结构呢?其实主要是因为在某些场景使用队列会更加符合我们的需求。比如网上购票,先提交订单的用户应该优先获得购票权,后面下单的用户就得先排队等着。这种情形,就适合用队列这种结构来存储数据,先来的先处理。
线性表有顺序结构和链式结构之分,队列作为特殊的线性表,也有这两种存储方式,我们先来看看队列的顺序存储结构。
队列的顺序存储结构和线性表的表示法一致,只不过在对顺序队列操作时,删除元素只能从队头删除,添加元素只能从队尾添加。我们在删除元素时,可以将后面的元素都依次往前移动一个位置,但是这样子在性能上会有些低。其实我们如果能够确定哪些空间是闲置的,那就没有必要每次删除元素都把元素往前移动了。基于此想法,就有了循环队列(这里是指顺序存储的循环队列,链式结构中也可以有循环队列)。
我们来定义一下循环队列的顺序存储结构:
typedefstructSqQueue{
intdata[SIZE];
intfront;//用于表示队头所在的位置
intrear;//用于表示队尾所在的位置
}SqQueue;
在使用某种数据结构时,我们一定要先对数据结构内的元素进行初始化操作,然后才能进行一些增删改查的操作。当我们创建队列时,默认使队头和队尾都指向数组第一个位置,也就是下标为0的地方。
StatusinitQueue(SqQueue*q){
q-front=0;
q-rear=0;
returnOK;
}
元素入队的代码如下:
StatusenQueue(SqQueue*q,inte){
//队列满了
if((q-rear+1)%SIZE==q-front){
returnERROR;
}
q-data[q-rear]=e;
q-rear=(q-rear+1)%SIZE;
returnOK;
}
元素出队的代码如下:
StatusdeQueue(SqQueue*q,int*e){
//队列为空
if(q-front==q-rear){
returnERROR;
}
e=q-data[q-front];
q-front=(q-front+1)%SIZE;
returnOK;
}
另外,在我们计算循环队列的长度时,我们可以使用以下公式:
(q-rear-q-front+SIZE)%SIZE
循环队列相对于普通的顺序队列,避免了元素出队时移动元素的麻烦,但是顺序存储结构都可能会面临存储空间不够的问题,这种情况下,我们就要考虑使用链式存储结构了,我们再对此进行讲解。