1.队列的结构
1.队列的概念
队列是只允许在一端进行插入删除的操作,在另一端进行删除的线性表,队列中的元素遵循先进先出即FIFO的特性。
元素入队:从队尾一端进行插入。
元素出队:从队头一端进行删除。
队列的操作即如上图所示,由FIFO的特性可以知道,元素进队顺序一旦固定,出队顺序唯一。
2.队列数据结构
那么,根据这种数据结构,我们使用顺序表形式对其进行定义。
首先,定义如下图所示的结点结构,由于要对两侧进行操作,队列里需要两个方向,即两种运作方式的结点,接着定义队列结构如下图所示。
typedef int QDataType;
typedef struct QNode{
struct QueueNode* next;
QDataType val;
}QNode;
typedef struct Queue{
QNode* phead;
QNode* ptail;
int size;
}
这种结构即可双向操作完成FIFO的功能。
3.队列的初始化
下面完成对队列的初始化部分。
void QueueInit(Queue* pq) {
assert(pq);
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
4.队列的销毁
队列的销毁与栈的结构略有不同,队列可以进行顺序遍历,使用cur指针一个节点一个节点地进行释放。其余重点跟栈的要求一致。
void QueueDestroy(Queue* pq) {
assert(pq);
Qnode* cur = pq->phead;
while (cur) {
Qnode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
pq->size = 0;
}
2.队列的插入和删除
1.插入
队列的插入操作同顺序表,大体思路是需要生成节点然后进行尾插。首先的操作还是判满然后对队列进行扩容,其次把需要进行操作的元素值赋给新节点,此时检测ptail是否为空,若为空直接把新节点复制进去即可,少了这一步的话next为空将会触发断言。接着就是经典的尾插语句把新节点尾插进去,接着size++,让出下一次插入的空间。
void QueuePush(Queue* pq, QDataType x) {
assert(pq);
Qnode* newnode = (Qnode*)malloc(sizeof(Qnode));
if (newnode == NULL) {
perror("malloc fail!");
return;
}
newnode->val = x;
newnode->next = NULL;
if (pq->ptail == NULL) {
pq->phead = pq->ptail = newnode;
}
else {
pq->ptail->next = newnode;
pq->ptail = newnode;
}
pq->size++;
}
2.删除
对于队列进行元素删除,还是要判空,再进行操作,以及要加上头结点不能空。删除就是简单的一个头删操作,如果头被删了,把尾也置空,防止对空队列进行后续操作,然后把size--即可。
void QueuePop(Queue* pq) {
assert(pq);
assert(pq->phead);
assert(pq->size > 0);
Qnode* prev = pq->phead;
pq->phead = pq->phead->next;
free(prev);
prev = NULL;
if (pq->phead == NULL) {
pq->ptail = NULL;
}
pq->size--;
}
3.队列的其余操作
其余操作并非不重要,反而这才是队列的精华。
比如说如何实现FIFO?
这时,我们选择的头结点和尾结点就能体现出他们的作用。
1.取队头元素
QDataType QueueFront(Queue* pq) {
assert(pq);
assert(pq->phead);
return pq->phead->val;
}
2.取队尾元素
QDataType QueueBack(Queue* pq) {
assert(pq);
assert(pq->ptail);
return pq->ptail->val;
}
上面两个代码虽然简单,对于完成FIFO功能,只需取队头元素即可。
3.队列判空以及计算大小
bool QueueEmpty(Queue* pq) {
assert(pq);
return pq->phead == NULL;
}
int QueueSize(Queue* pq) {
assert(pq);
return pq->size;
}
这两份代码与栈类似,不多赘述。