队列(C/C++)
前言
本文介绍队列的基本知识,包括概念、初始化、销毁、入队、出队、求队首队尾值以及计算有效元素个数
队列
- 前言
- 正文
- 1概念与结构
- 2.常见操作基本实现
- Queue.h
- Queue.c
- 初始化与销毁
- 入队
- 判断队列是否为空
- 出队
- 取队头或队尾数据
- 队列中的有效元素
- test.c
- 结语
正文
1概念与结构
概念:只允许在一端进行插入的数据操作,在另一端进行删除数据操作的特殊线性表
队列具有先入先出
入队列:进行插入的一段称为队尾
出队列:进行删除操作的一段称为队头
那么队列的底层结构如何选选择?数组or链表
数组
入队,可以直接找到,时间复杂度为O(1)
出队,需要遍历,时间复杂度为O(N)
链表
出队,对头出队,O(1)
入队,需要找到尾指针,O(N)
这样看来似乎哪个都行没有区别,但是,对于链表而言,如果我们直接定义一个队尾的指针,时间复杂度岂不是O(1).
代码如下:
//队列结点的结构
typedef struct QueueNode
{
QDataType data;
struct QueueNode* next;
}QueueNode;
//队列的结构
typedef struct Queue
{
QueueNode* phead;
QueueNode* ptail;
}Queue;
2.常见操作基本实现
本文依旧是多文件形式
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 QueueDestroy(Queue* pq);
//入队——队尾
void QueuePush(Queue* pq, QDataType x);
//出队——队头
void QueuePop(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//队列有效元素个数
int QueueSize(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
Queue.c
初始化与销毁
初始化:令phead和ptail为NULL
销毁:定义pcur指针,保存pcur->next
,然后逐个销毁pcur。
//初始化
void QueueInit(Queue* pq)
{
assert(pq);
pq->phead = pq->ptail = NULL;
//pz->size = 0;
}
//销毁队列
void QueueDestroy(Queue* pq)
{
assert(pq);
QueueNode* pcur = pq->phead;
while (pcur)
{
QueueNode* next = pcur->next;
free(pcur);
pcur = next;
}
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;
}
//队列非空
else {
pq->ptail->next = newnode;
pq->ptail = pq->ptail->next;
}
//pq->size++
}
判断队列是否为空
//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
assert(pq);
return pq->phead == NULL;
}
出队
只有一个结点phead和ptail置为空,
有多个结点,因为对头出队,所有先要保存头结点的下一个指针phead->next
,释放头指针phead
,让phead->next
成为新的头指针
//出队——对头
void QueuePop(Queue* pq)
{
assert(!QueueEmpty(pq));
//只有一个结点,phead和ptail都置为空
if (pq->phead == pq->ptail)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
else
{
QueueNode* next = pq->phead->next;
free(pq->phead);
pq->phead = next;
}
//pq->size--;
}
取队头或队尾数据
注意:这里是拿出来用并不是销毁,该数据还在队列中
直接返回对应位置的data
值即可
//取队头数据
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);
//方法一:遍历列表——适用于不频繁调用队列有效数据个数的场景(空间复杂度较高O(N))
QueueNode* pcur = pq->phead;
int size = 0;
while (pcur)
{
size++;
pcur = pcur->next;
}
return size;
//方法二:遍历链表——适用于频繁调用队列有效数据个数的场景
//return pq->size;
}
test.c
用于测试
#include"Queue.h"
void test()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePop(&q);
int front = QueueFront(&q);
int rear = QueueBack(&q);
printf("front:%d\n", front);
printf("rear:%d\n", rear);
printf("size:%d", QueueSize(&q));
QueueDestroy(&q);
}
int main()
{
test();
return 0;
}
结语
这篇介绍链表的基本实现,但如需要进一步来学习栈与队列,请关注下一篇栈与队列OJ,希望三连支持一下谢谢。