数据结构之队列:初始化、入队、出队与源码全解析
目录
- 前言
- 一、队列
- 1.1 概念与结构
- 1.2 队列的实现
- 1.2.1 初始化
- 1.2.2 入队--队尾
- 1.2.3 判断队列是否为空
- 1.2.4 出队--对头
- 1.2.5 取对头和取队尾
- 1.2.6 队列中有效元素个数
- 1.2.7 销毁队列
- 二、队列完整源码
- 结语
前言
大家好啊,我是云泽Q,一名热爱计算机技术的在校大学生。近几年人工智能技术飞速发展,为了帮助大家更好地抓住这波浪潮,在开始正文之前,我想先为大家推荐一个非常优质的人工智能学习网站)。它提供了从基础到前沿的系列课程和实战项目,非常适合想要系统入门和提升AI技术的朋友,相信能对你的学习之路有所帮助。
一、队列
1.1 概念与结构
概念:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)的性质
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头
队列如果使用数组结构的话,头部的插入删除时间复杂就是O(n)
队列遵循 “先进先出”(FIFO)原则,通常需要两个指针(或索引):
一个指向队头(front),表示下一个要出队的元素
一个指向队尾(rear),表示下一个要入队的位置
出队操作:当队头元素被移除后,front 指针需要后移。此时如果要保持队列元素的连续性(让新的队头元素位于数组起始位置),就需要将所有剩余元素向前移动一位,这个操作的时间复杂度是 O (n),队尾插入数据时间复杂度为O(1)
若使用单链表来实现,尾插就要找尾节点,插入数据时间复杂度为O(n),删除数据时间复杂度为O(1)
虽然双向链表不论头尾,所有位置的操作时间复杂度都为O(1),但是单链表在结构体当中只有两个成员,占用8个字节,但是双向链表有三个成员,再加上内存对齐,空间会更大,所以一般情况下并不会使用双向链表
但是单链表的插入数据的时间复杂度还可以优化,删除数据是队头的操作,时间复杂度为O(1),可以让phead指针始终指向队列的头,只额外增加一个ptail指针指向尾节点,此时增加的成本也不高,插入数据的时间复杂度就为O(1)了
所以说这里用链表来实现队列更好一些
在整个队列结构,只用关心头尾的两个指针,由于这两个指针指向的是队列中的节点,所以两个指针是节点数据类型
//节点的结构
struct ListNode
{int data;struct ListNode* next;
}
//队列的结构
struct Queue
{struct ListNode* phead;//指向队头节点的指针struct ListNode* ptail;//指向队尾节点的指针
}
稍做优化
typedef int QDataType;
//定义节点结构
typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QueueNode;
//定义队列结构
typedef struct Queue
{QueueNode* phead;//对头QueueNode* ptail;//队尾
}Queue;
1.2 队列的实现
1.2.1 初始化
1.2.2 入队–队尾
这里分两种情况
第一种情况就是队列不为空
第二种情况就是队列为空
//入队--队尾
void QueuePush(Queue* pq, QDataType x);
1.2.3 判断队列是否为空
//判断队列是否为空
bool QueueEmpty(Queue* pq);
1.2.4 出队–对头
这里也分两种情况
第一种情况队列中大于等于2个节点
第二种情况队列只有一个节点
此时next指向为空,头节点free掉之后,phead指向next指针指向的节点,但是此时ptail就是野指针了
//出队--对头
void QueuePop(Queue* pq);
1.2.5 取对头和取队尾
//取对头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
1.2.6 队列中有效元素个数
//队列中有效元素个数
int QueueSize(Queue* pq);
但是这个方法的时间复杂度为O(n),还有优化方案,就是在队列的结构体多定义一个成员size,初始化size=0,入队列加加,出队列减减,这样计算有效元素个数方式时间复杂度就为O(1)
1.2.7 销毁队列
队列的销毁就是单链表的销毁
只要pcur不为空,就把下一个节点存起来,然后将pcur指向的节点销毁,然后pcur指向next指向的节点
如果此时pcur指向的节点不为空,先让next指针指向下一个待删除的节点,再将pcur销毁,循环往复
pcur为空,队列销毁完成
//销毁队列
void QueueDestory(Queue* pq);
二、队列完整源码
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;
}QueueNode;
//定义队列结构
typedef struct Queue
{QueueNode* phead;//队头QueueNode* ptail;//队尾//int size; //队列中有效元素个数
}Queue;//初始化
void QueueInit(Queue* pq);
//入队--队尾
void QueuePush(Queue* pq, QDataType x);
//判断队列是否为空
bool QueueEmpty(Queue* pq);
//出队--对头
void QueuePop(Queue* pq);
//取对头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列中有效元素个数
int QueueSize(Queue* pq);
//销毁队列
void QueueDestory(Queue* pq);
Queue.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"//初始化
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;//pq->size = 0;
}//入队--队尾
void QueuePush(Queue* pq, QDataType x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->phead == NULL){pq->phead = pq->ptail = newnode;//pq->size++;}else {pq->ptail->next = newnode;pq->ptail = pq->ptail->next;//pq->size++;}
}//判断队列是否为空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}//出队--对头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;//pq->size--;}else {QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;//pq->size--;}
}//取对头数据
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列中有效元素个数
int QueueSize(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){++size;pcur = pcur->next;}return size;//return pq->size;
}//销毁队列
void QueueDestory(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;
}
test.c
#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"void test01()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);//QueuePop(&q);//QueuePop(&q);//QueuePop(&q);//QueuePop(&q);//printf("队头:%d\n", QueueFront(&q));//printf("队尾:%d\n", QueueBack(&q));//printf("size:%d\n", QueueSize(&q));QueueDestory(&q);
}int main()
{test01();return 0;
}