系列文章目录
前言
队列:只允许在一端进行插入数据,在另一端进行删除数据操作的特殊线性表。
一、队列
队列就像我们生活中的排队,只允许在队头离开,在队尾加入。进行插入的一端为队尾,进行删除的一端为队头。所以队列具有先进先出的特点。
二、队列的实现
typedef int QDataType;
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QNode;
typedef struct Queue
{
QNode* head;
QNode* tail;
int size;
}Queue;
在上述的定义中,我们先定义了队列中的元素,是用链表的形式来实现队列结构的。其实原因很简单,队列涉及头删的问题,而顺序表中头删的时间复杂度是O(N)。很明显,顺序表实现队列的效率是非常低的。所以,我们采用链表的形式。
我们发现,每次尾插的时候都需要寻找尾部节点。该过程的是将复杂度是O(N)。其效率是很低的。因此,我们可以实现定义一个尾指针来记录尾部节点。因此我们就有了另外一个关于队列的结构体,这个结构体中定义了两个变量,一个是头指针,一个是尾指针。size可以来表示元素个数,这样就不用遍历O(n),空间换时间。
三、接口函数的实现
1、初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
将两个指针都指向空指针。
2、销毁
void QueueDestory(Queue* pq)
{
assert(pq);
QNode* cur = pq->head;
while (cur)
{
QNode* del = cur;
cur = cur->next;
free(del);
}
pq->head = NULL;
pq->tail = NULL;
pq->size = 0;
}
将头指针和尾指针设置为空,避免野指针的出现。
3、入队/出队
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QNode* newnode = (QNode*)malloc(sizeof(QNode));
assert(newnode);
newnode->data = x;
newnode->next = NULL;
if (pq->tail == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;
}
pq->size++;
}
void QueuePop(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
if (pq->head == pq->tail)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QNode* prev = pq->head;
pq->head = prev->next;
free(prev);
}
pq->size--;
}
要判断元素为空的情况,而且要小心野指针。
4、队头/队尾
QDataType QueueFront(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->head->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
assert(!QueueEmpty(pq));
return pq->tail->data;
}
判断元素是否为空。
5、判空/元素个数
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL && pq->tail == NULL;
}
int QueueSize(Queue* pq)
{
assert(pq);
return pq->size;
}
头指针指向的是第一个元素,那么如果头指针指向的是空,那么这个队列就为空。
四、队列中元素的访问
while (!QueueEmpty(&q))
{
printf("%d ", QueueFront(&q));
QueuePop(&q);
}
printf("\n");
由于队列中的元素满足先进先出,后进后出的特点,所以我们只能访问头尾,想要访问第二个元素,必须删掉队头才行。因此,我们就可以结合上面的接口函数,模拟队列的实现。
总结
队列的特点就是先进后出。
人生应该如蜡烛一样,从顶燃到底,一直都是光明的。 —— 萧楚女