前言
本文介绍队列的基本知识,包括概念、初始化、销毁、入队、出队、求队首队尾值以及计算有效元素个数
队列
- 前言
- 正文
- 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,希望三连支持一下谢谢。