链式队列
链式队列主要应用的是单链表,先设计一个单链表:
typedef struct Node
{
int data;//数据域,没存放节点的值
struct Node* next;//指针域,存放下一个节点的地址
}Node,*PNode;
单链表的特点
优点: 插入和删除不用移动元素,头删的时间复杂度O(1) 尾删的时间复杂度O(n)
为什么头删的时间复杂度O(1) 尾删的时间复杂度O(n)呢
第一个节点和最后一个节点最直接差别在于: 第一个节点的地址我们一直用头结点来保存着,而最后一个节点我们得依次遍历过去。
- 我们可以改造一下,让头部操作和尾部操作时间复杂度都为O(1):
我们可以把头结点单独设计一下,不改动数据节点的结构:
//重新设计的头结点
typedef struct LQueue
{
struct Node* prior;
struct Node* tail;
}Lqueue,*PLQueue;
链式队列所需的操作
//增删改查
//初始化
void InitLQueue(PLQueue plq);
//入队
bool Push(PLQueue plq,int val);
//获取第一个节点的值,但不删除
bool Get_Top(PLQueue plq, int* rtval);
//出队,获取第一个节点的值,并且删除掉
bool Pop(PLQueue plq, int* rtval);
//判空
bool IsEmpty(PLQueue plq);
//链式队列不需要判满
//bool IsFull(PLQueue plq);
//获取元素有效个数
int Get_length(PLQueue plq);
//清空
bool Clear(PLQueue plq);
//销毁
void Destroy(PLQueue plq);
链式队列具体代码实现
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"lqueue.h"
//增删改查
//初始化
void InitLQueue(PLQueue plq)
{
assert(plq != NULL);
if (plq == NULL)
return;
plq->prior = NULL;//初始化对头和对尾
plq->tail = NULL;
}
//入队
bool Push(PLQueue plq, int val)
{
assert(plq != NULL);
if (plq == NULL)
return false;
Node* pnewnode = (Node*)malloc(sizeof(Node));//给插入的节点申请一个空间
assert(pnewnode != nullptr);//判断其不为空
pnewnode->data = val; //把要插入的值给申请的新节点
pnewnode->next = NULL;//当没有一个元素时,这一步不能少
if (plq->prior == NULL)//判断当链式队列中没有一个元素,需要改变两个指针域
{
plq->prior = pnewnode;//插入的新节点可以是队列的头,也可以是队列的尾
plq->tail = pnewnode;
}
else
{
pnewnode->next = plq->tail->next;//队尾的next域存放新节点
plq->tail->next = pnewnode;//将新节点的值赋给队尾的next域
plq->tail = pnewnode;//之后队尾的新节点成为新队列的队尾
}
return true;
}
//获取第一个节点的值,但不删除
bool Get_Top(PLQueue plq, int* rtval)//rtval是一个指针,指向第一个节点
{
assert(plq != NULL && rtval != NULL);//断言
if (plq == NULL || rtval == NULL)
return false;
//判空
if (IsEmpty(plq)) //保证最起码有一个节点
return false;
//先通过输入参数获取值
Node* p = plq->prior; //将队头赋给Node *类型的指针p
*rtval = p->data; //将指针p获取的值赋给指针rtval
return true;
}
//出队,获取第一个节点的值,并且删除掉
bool Pop(PLQueue plq, int *rtval)
{
assert(plq != NULL && rtval != NULL);//断言
if (plq == NULL||rtval==NULL)
return false;
//判空
if (IsEmpty(plq)) //保证最起码有一个节点
return false;
//先通过输入参数获取值
Node* p = plq->prior;
*rtval = p->data;
//出队的是否是唯一的一个节点
if (p->next == NULL)
{
plq->prior = plq->tail = NULL; //如果队列的next域为空,出队唯一一个节点,则队头和队尾都为空(也就是队头和队尾都指向next域)
}
else
{
plq->prior = p->next;//如果队列不是只有一个节点,则队头变成队头指向的next域
}
//释放掉出队的元素
free(p);
p = NULL;
return true;
}
//判空
bool IsEmpty(PLQueue plq)
{
assert(plq != NULL);
if (plq == NULL)
return false;
return plq->prior == NULL;//队头为空则整个队列为空
}
//获取元素有效个数
int Get_length(PLQueue plq)
{
assert(plq != NULL); //断言
if (plq == NULL)
return -1;
int count = 0;//count为一个计数器,用来计算队列的长度
for (Node* p = plq->prior; p != NULL; p = p->next)
{
count++;
}
return count;
}
//清空
bool Clear(PLQueue plq)
{
Destroy(plq);
return true;
}
//销毁
void Destroy(PLQueue plq)
{
assert(plq != NULL);
if (plq == NULL)
return ;
Node* p = NULL;//申请一个临时指针,接下来需要一直指向第一个节点
while (IsEmpty(plq))//只要队列不空,就一直删除第一个节点
{
p = plq->prior;//p指向第一个节点
plq->prior = p->next;//头结点的指针域指向第二个节点
free(p);//释放指针p
p = NULL;
}
plq->prior = plq->tail = NULL;//当队头和队尾都为空时,销毁结束
return;
}
测试代码:
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include"lqueue.h"
int main()
{
LQueue lq;
InitLQueue(&lq);
for (int i = 0; i < 20; i++)
{
Push(&lq, i);
}
printf("count = %-5d", Get_length(&lq));
printf("\n");
int val;
while (!IsEmpty(&lq))
{
Pop(&lq, &val);
printf("%-5d", val);
}
printf("\n");
printf("count = %-5d", Get_length(&lq));
return 0;
}