1、队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除操作的特殊线性表,队列具有__先进先出__FIFO(First In First Out)
入队列:进行插入操作的一端称为__队尾__。
出队列:进行删除操作的一端称为__对头__。
数据存放:现在有数据:A、B、C、D要存放,那存放数据顺序就是一次存入,并且存放的位置如下:
1.1、队列的实现概念
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果数组的结构,出队列在数组头上出数据,效率会比较低。
因为数组队列在处一个数据后们需要整体挪动数据向前,所以麻烦,这里不如链表。而链表在出数据是,把节点释放,然后将phead在指向要出数据的后一个数据即可,并且我们也记录个尾节点ptail,用来链接放在队列中新数据即可。
所以队列的实现主要以链表队列为主要。
2、链队列的实现
队列主要有以下接口函数
- 初始化(QueueInit)
- 销毁(QueueDestroy)
- 扩容(BuyQueueNode)
- 队尾插入数据(QueuePush)
- 对头删除数据(QueuePop)
- 取队尾数据(QueueBack)
- 取对头数据(QueueFront)
- 统计队列中数据个数(QueueSize)
- 判断队列是否为空(QueueEmpty)
2.1、定义结构体
这里需要重点说明这个结构体的定义。
1、首先我们定义一个QueueNode结构体,这个结构体就是动态扩容的结点。里面有一个Next指针域,还有一个data数据域。如下:
typedef struct QueueNode
{struct QueueNode* next;QDataType data;
}QNode;
2、然后是重点来了:这里我们需要创建两个QueueNode*类型的指针:head,tail。分别用来队列的头结点和尾节点。如下:
typedef struct Queue
{struct QueueNode* head;struct QueueNode* tail;
}Queue;
然后整体结构体定义就如下:
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int QDataType;typedef struct QueueNode
{QDataType data;struct QueueNode* Next;
}QNode;typedef struct Queue
{struct QueueNode* head;struct QueueNode* tail;
}Queue;
2.2、队列初始化
将头结点和尾节点置为NULL即可。
//队列初始化
void QueueInit(Queue* pq)
{assert(pq);pq->head = NULL;pq->tail = NULL;
}
2.3、销毁
依次遍历结点进行销毁
//销毁
void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->head;while (cur){QNode* next = cur->Next;free(cur);cur = next;}pq->head = pq->tail = NULL;
}
2.4、扩容
//扩容
QNode* BuyQueueNode(QDataType x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->Next = NULL;return newnode;
}
2.5、队尾插入数据
队列在队尾入数据,因为我们提前设置了尾结点(tail),那操作就容易了,先将tail的Next域指向新节点的地址,然后再将新节点的地址赋值给tail,便于下次再尾插。
但是由于我们没创建哨兵位,所以在进行结点的插入和删除时,需要考虑边界问题。
那这里,特殊情况就是,如果是第一次插入数据,也就是当pq->head == NULL时,我们可以直接将新节点的地址赋值给头结点和尾节点。
//对尾插入数据
void QueuePush(Queue* pq, QDataType x)
{QNode* newnode = BuyQueueNode(x);if (pq->head == NULL){pq->head = pq->tail = newnode;}else{pq->tail->Next = newnode;pq->tail = newnode;}
}
2.6、对头删除数据
核心思想:先保存头结点的后驱结点的地址,然后释放头结点,然后将先前保存头结点的后驱结点的地址赋值给头结点。
特殊情况:当pq->head == NULL时,也就意味着整个队列为空了,所以我们还需要将pq->tail = NULL。
//队头删除数据
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QNode* next = pq->head->Next;free(pq->head);pq->head = next;if (pq->head == NULL){pq->tail = NULL;}
}
2.7、取队尾数据
//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}
2.8、取对头数据
//取对头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->head->data;
}
2.8、统计队列元素个数
使用个计数器,依次遍历队列中的结点,来统计队列元素个数。
//统计队列元素个数
int QueueSize(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));int count = 0;QNode* cur = pq->head;while (cur){count++;cur = cur->Next;}return count;
}
2.10、判断队列是否为空
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}
2.11、main函数测试程序
下面测试程序功能:遍历输出队列中的数据。
#include "queue.h"int main()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePush(&q, 5);QueuePush(&q, 6);while (!QueueEmpty(&q)){printf("%d ", QueueFront(&q));QueuePop(&q);}printf("\n");QueueDestroy(&q);return 0;
}
输出:
3、全代码展示
这里使用三个文件:
- queue.h:用于结构体、各种函数接口的声明
- queue.c:用于各种函数接口的定义。
- test.c:用于创建链表,实现链表。
3.1、queue.h
#pragma once#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>typedef int QDataType;typedef struct QueueNode
{QDataType data;struct QueueNode* Next;
}QNode;typedef struct Queue
{struct QueueNode* head;struct QueueNode* tail;
}Queue;//队列初始化
void QueueInit(Queue* pq);//销毁
void QueueDestroy(Queue* pq);//扩容
QNode* BuyQueueNode(QDataType x);//对尾插入数据
void QueuePush(Queue* pq, QDataType x);//队头删除数据
void QueuePop(Queue* pq);//取队尾数据
QDataType QueueBack(Queue* pq);//取对头数据
QDataType QueueFront(Queue* pq);//统计队列元素个数
int QueueSize(Queue* pq);//判断队列是否为空
bool QueueEmpty(Queue* pq);
3.2、queue.c
#include "queue.h"//队列初始化
void QueueInit(Queue* pq)
{assert(pq);pq->head = NULL;pq->tail = NULL;
}//销毁
void QueueDestroy(Queue* pq)
{assert(pq);QNode* cur = pq->head;while (cur){QNode* next = cur->Next;free(cur);cur = next;}pq->head = pq->tail = NULL;
}//扩容
QNode* BuyQueueNode(QDataType x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){printf("malloc fail\n");exit(-1);}newnode->data = x;newnode->Next = NULL;return newnode;
}//对尾插入数据
void QueuePush(Queue* pq, QDataType x)
{QNode* newnode = BuyQueueNode(x);if (pq->head == NULL){pq->head = pq->tail = newnode;}else{pq->tail->Next = newnode;pq->tail = newnode;}
}//队头删除数据
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QNode* next = pq->head->Next;free(pq->head);pq->head = next;if (pq->head == NULL){pq->tail = NULL;}
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->tail->data;
}//取对头数据
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->head->data;
}//统计队列元素个数
int QueueSize(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));int count = 0;QNode* cur = pq->head;while (cur){count++;cur = cur->Next;}return count;
}//判断队列是否为空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->head == NULL;
}
3.3、test.c
#include "queue.h"int main()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);QueuePush(&q, 5);QueuePush(&q, 6);while (!QueueEmpty(&q)){printf("%d ", QueueFront(&q));QueuePop(&q);}printf("\n");QueueDestroy(&q);return 0;
}