学习目标:
- 队列的设计和实现
队列的特点:先进先出。
学习内容:
一、队列的头文件
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next;
QDataType data;
}QueueNode;
typedef struct Queue
{
QueueNode* head;//头指针
QueueNode* tail;//尾指针
//注意:利用头尾指针维护队列。所以通过Queue变量的地址就能操作里面的内容,也就能操作队列。
}Queue;
void QueueInit(Queue* pq);
//如果不定义Queue结构体,则Queue* pq的head和tail由QueueNode** pphead, QueueNode** pptail实现
//void QueueInit(QueueNode** pphead, QueueNode** pptail);
//注意:如果形参是结构体,则实参传结构体地址。如果形参是二级指针,则实参传一级指针的地址。
void QueueDestroy(Queue* pq);
//1、头作队头,尾作队尾 头删出队、尾插入队,头删时间复杂度为O(1),尾插时间复杂度为O(1),适合
//2、头做队尾,尾作对头 尾删出队、头插入队,尾删时间复杂度为O(1),头插时间复杂度为O(1),适合
//注意:这里使用第一种方式。且不需要哨兵位结点。
//队尾入 - 尾插
void QueuePush(Queue* pq, QDataType x);
//队头出 - 头删
void QueuePop(Queue* pq);
//获取队头数据
QDataType QueueFront(Queue* pq);
//获取队尾数据
QDataType QueueBack(Queue* pq);
int QueueSize(Queue* pq);
bool QueueEmpty(Queue* pq);
注意:使用带头结点和尾结点的单链表实现队列结构。
二、代码实现
1、void QueueInit(Queue* pq);初始化队列
void QueueInit(Queue* pq)
{
assert(pq);
pq->head = NULL;
pq->tail = NULL;
}
2、void QueueDestroy(Queue* pq);销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* cur = pq->head;
while (cur != NULL)//注意:不是while (cur != pq->tail)否则最后一个结点没有删除
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->head = NULL;
pq->tail = NULL;
}
3、void QueuePush(Queue* pq, QDataType x);队尾入 - 尾插
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
return;
newnode->data = x;
newnode->next = NULL;
//没有结点时,空队列
if (pq->head == NULL)
{
pq->head = pq->tail = newnode;
}
else
{
pq->tail->next = newnode;
pq->tail = newnode;//更新尾节点
}
}
4、void QueuePop(Queue* pq);队头出 - 头删
//方法1:
void QueuePop(Queue* pq)
{
assert(pq);
//判断队列是否为空,队列为空时,再删队尾的数据,NULL指针访问
assert(!QueueEmpty(pq));//!QueueEmpty(pq)为假,则QueueEmpty(pq)为真,相当于assert(pq->head);
//方法1:
QueueNode* next = pq->head->next;//注意pq->head为空,则解引用报错
free(pq->head);
pq->head = next;//当删除最后一个结点的时候,head被置为NULL,但tail并没有置NULL
//方法2:
//QueueNode* del = pq->head;
//pq->head = pq->head->next;//当删除最后一个结点的时候,head被置为NULL,但tail并没有置NULL
//free(del);
//即当只有一个元素(结点)的时候,head和tail指向同一个地址。调用QueuePop后,head被置为NULL了。但tail并不没有置空,tail为野指针。
//故需要这里再判断,当head为空时,将tail也置空。
if (pq->head == NULL)
pq->tail = NULL;
//通过调试观察现象。
}
//方法2:
void QueuePop(Queue* pq)
{
assert(pq);
//队列为空时,再删队尾的数据,NULL指针访问
assert(pq->head);
//只有一个结点
if (pq->head->next == NULL)
{
free(pq->head);
pq->head = pq->tail = NULL;
}
else
{
QueueNode* next = pq->head->next;
free(pq->head);
pq->head = next;
}
}
5、QDataType QueueFront(Queue* pq);获取队头数据
QDataType QueueFront(Queue* pq)
{
assert(pq);
//队列为空时,再取队尾的数据,NULL指针访问
assert(!QueueEmpty(pq));//注意pq->head为空,队列为空,解引用报错
return pq->head->data;
}
6、QDataType QueueBack(Queue* pq);获取队尾数据
QDataType QueueBack(Queue* pq)
{
assert(pq);
//队列为空时,再取队尾的数据,NULL指针访问
assert(!QueueEmpty(pq));//注意pq->tail为空,队列为空,解引用报错
return pq->tail->data;
}
7、int QueueSize(Queue* pq);队列大小
int QueueSize(Queue* pq)
{
assert(pq);
int n = 0;
QueueNode* cur = pq->head;
while (cur != NULL)//注意:不是while (cur != pq->tail)否则少算了一个
{
++n;
cur = cur->next;
}
return n;
//或者在Queue结构体加一个size成员变量,在Push或者Pop函数中,++或--
//那么这里就可以直接使用下面代码
//return pq->size;
}
8、bool QueueEmpty(Queue* pq);判断队列是否为空
扫描二维码关注公众号,回复:
14879429 查看本文章
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->head == NULL;
}
三、代码测试
#define _CRT_SECURE_NO_WARNINGS 1
#include "Queue.h"
void TestQueue1()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
//QueuePop(&q);//队列为空,继续删除,报错
//QueuePop删除最后一个元素,没有将tail置空,当调试发现tail为野指针
//printf("%d\n", QueueBack(&q));//注释掉:QueueBack中的assert(!QueueEmpty(pq));会打印随机值
QueuePush(&q, 5);
QueuePush(&q, 6);
QueueDestroy(&q);
}
//先进先出
void TestQueue2()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QDataType front = QueueFront(&q);
printf("%d ", front);
QDataType front = QueueFront(&q);
printf("%d ", front);
QueuePop(&q);
QueuePush(&q, 5);
//遍历队列
while (!QueueEmpty(&q))
{
QDataType front = QueueFront(&q);
printf("%d ", front);
QueuePop(&q);
}
printf("\n");//1 2 3 4 5
//队列的数据是绝对位置
QueueDestroy(&q);
}
int main()
{
//TestQueue1();
TestQueue2();
return 0;
}