队列的顺序及链式实现
队列定义
队列的工作原理和现实中的队列完全一致,比如排队上车,排前面的先上车,排后面的后上车。队列只支持两种操作,入队和出队,是一种先进先出的数据结构(First In First Out,FIFO),顺序和链式两种结构的队列如下:
队列基本操作
- 初始化队列 initQueue():初始化队列空间及队首队尾指针 head、tail
- 判断队列是否为空 isEmpty():当队首、队尾相等时,队为空,即 head == tail 是返回真,否返回假
- 入队 enQueue():将元素从队尾 tail 处加入队列,并且更新队尾指针,即 tail++
- 出队 delQueue():从队首元素开始出队,并且更新队首指针,即 head++
- 读队首元素 getHeadValue():读取队首元素的值
顺序队列实现
#include <stdio.h>
#include <string.h>
#define true 1
#define false 0
#define MAX_SIZE 100
typedef struct {
char data[MAX_SIZE];
int head;
int tail;
}Queue;
void initQueue(Queue *queue){
memset(queue -> data, 0, MAX_SIZE);
queue -> head = 0;
queue -> tail = 0;
printf("######## 队列初始化完成! ########\n");
}
int isEmpty(Queue *queue){
if(queue -> head == queue -> tail) {
printf("完成出队! Queue -> head = Queue -> tail = %d\n", queue -> head);
return true;
}
return false;
}
char getHeadValue(Queue *queue){
return queue -> data[queue -> head];
}
void enQueue(Queue *queue, char value){
queue -> data[queue -> tail] = value;
queue -> tail++;
printf("元素 %c 入队!\n",queue -> data[queue -> tail - 1]);
}
void delQueue(Queue *queue){
printf("元素 %c 将出队!\n",getHeadValue(queue));
queue -> data[queue -> head] = 0;
queue -> head++;
}
int main()
{
Queue Q;
initQueue(&Q);
char str[MAX_SIZE] = {0};
printf("请输入数据,按Enter结束!\n");
scanf("%s", str);
int i;
printf("######### 开始入队 #########\n");
for(i = 0; i < strlen(str); i++) {
enQueue(&Q, str[i]);
}
printf("######### 开始出队 #########\n");
while(1) {
if(isEmpty(&Q)) {
break;
}
delQueue(&Q);
}
return 0;
}
该程序模拟队列基本操作,执行过程如下:
链式队列实现
链式队列通过链表来存储数据,因此需要建立一个链表结构(关于链表看这里),在此基础上再建立队列结构,定义如下:
typedef struct ListNode{
char data;
struct ListNode *next;
}Lnode;
typedef struct ListQueue{
Lnode *head;
Lnode *tail;
}Lqueue;
初始化链式队列
链式队列头尾指针指向同一个空节点
void Init_ListQueue(Lqueue *queue) {
queue -> head = queue -> tail = (Lnode *)malloc(sizeof(Lnode));
if(queue -> head == NULL) {
printf("Malloc Failed!\n");
exit(0);
}
queue -> head -> next = NULL;
}
元素入队
由队尾添加元素,队尾指针始终指向新增节点(node)的地址,移动 tail 指针
void Push_ListValue(Lqueue *queue, char value) {
Lnode *node = (Lnode *)malloc(sizeof(Lnode));
node -> data = value;
node -> next = NULL;
queue -> tail -> next = node;
queue -> tail = queue -> tail -> next;
printf("Push: %c\n", queue -> tail -> data);
}
元素出队
移动队头指针出队,head -> next 指向第一个节点地址,当 head -> next 等于队尾指针时,已到链队末尾,队尾指针出队,出队结束,释放 head -> next 空间,链队空!
void Pop_ListValue(Lqueue *queue) {
if(queue -> head -> next == queue -> tail) {
printf("Pop: %c\n", queue -> tail -> data);
free(queue -> head -> next);
queue -> head -> next = NULL;
printf("队列已空!\n");
return;
}
else {
Lnode *tmp = queue -> head -> next;
printf("Pop: %c\n", tmp -> data);
queue -> head -> next = tmp -> next;
free(tmp);
tmp = NULL;
}
}
测试代码
当队列为空时,需要释放头节点指针 LQ.head. 此处多一次Pop操作来验证队列是否已释放空
void test_1() {
Lqueue LQ;
Init_ListQueue(&LQ);
Push_ListValue(&LQ, 'A');
Push_ListValue(&LQ, 'B');
Push_ListValue(&LQ, 'C');
Pop_ListValue(&LQ);
Pop_ListValue(&LQ);
Pop_ListValue(&LQ);
Pop_ListValue(&LQ);
free(LQ.head);
}
执行结果打印如下:
Push: A
Push: B
Push: C
Pop: A
Pop: B
Pop: C
队列已空!
循环队列
循环队列最多存储(MAXSIZE - 1)个队列元素,因此当循环队列中只剩下一个空存储单元时,说明队列已经满了,即队列判满条件是 head = (tail + 1) % MAXSIZE;队列判空的条件是 head == tail;
在实际开发中,比较常见的队列是循环双端队列,就是队列两端都可以进出的队列,如下图:
对于循环双端队列的实现,Leetcode上面有一道题目,小编之前有总结一篇博客,这里贴出链接C语言实现循环双端队列
好了,关于队列的学习总结就到这了,之后有新的感悟再做更新…
参考文章
所有博客均在CSDN首发 Caso_卡索 https://blog.csdn.net/xiaoma_2018