Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!
我的博客:<但凡.
我的专栏:《数据结构与算法之美》、《题海拾贝》、《编程之路》(C语言基础)
欢迎点赞、关注!
1、队列
队列(Queue)是一种常见的数据结构,它是一种 先进先出(FIFO, First In First Out) 的线性表,类似于排队的场景:最先进入队列的元素最先离开。
特点
-
先进先出 (FIFO):
- 元素从队列的一端(通常称为“队尾”)插入。
- 元素从队列的另一端(通常称为“队头”)移除。
-
两种操作:
- 入队 (Enqueue): 将元素添加到队尾。
- 出队 (Dequeue): 从队头移除元素。
-
受限访问:
只能操作队列的两端:队尾插入和队头移除。
现在我们就用链表来模拟实现队列。
2、队列的模拟实现
2.1、节点的声明
typedef int Qdate;
typedef struct Qnode
{
Qdate a;
struct Queue* next;
}Qnode;
typedef struct Queue
{
Qnode* head;
Qnode* tail;
}Queue;
其中,Qnode是队列里面的节点,Queue是用来记录队列的首尾。
2.2、初始化和创建节点
void QueueInit(Queue* p)//初始化
{
assert(p);
p->head = p->tail = NULL;
}
Qnode* Buynode(Qdate x)//创建节点
{
Qnode* p = (Qnode*)malloc(sizeof(Qnode));
if (p == NULL)
{
printf("开辟失败!");
return 1;
}
p->a = x;
p->next = NULL;
return p;
}
2.3、插入(尾插)
我们在这里只用写尾插就好,因为我们的队列遵循先进后出,进我们就让他尾插,出的话我们就让他头删,这是队列的特点,所以我们只用实现尾插。
void Queuepush(Queue* p,Qdate a)//这里的p是传址
{
assert(p);
Qnode* pcur = Buynode(a);
if (p->tail == NULL)
{
p->head = p->tail = pcur;
}
else
{
Qnode* pt = p->tail;
pt->next = pcur;
p->tail=pcur;
}
}
2.4、删除(头删)
void Queuepop(Queue* p)
{
assert(p);
if (p->head == NULL)
{
printf("无法删除");
return 1;
}
else
{
Qnode* ph = p->head;
p->head = ph->next;
free(ph);
ph = NULL;
}
}
2.5、打印头部元素
我们这里使用的思路是,打印一个,删除一个,这样更灵活些,那如果说我想每次打印自动删除,也是可以实现的,大家可以上手敲一下。
Qdate QueueFront(Queue* p)
{
assert(p);
assert(p->head);//我将要访问谁,你就得assert谁或者写一个if判断,不然就是访问野指针或者空指针。
return (p->head)->a;
}
2.6、查找队列元素个数
int Qsize(Queue* p)
{
assert(p);
Qnode* pc = p->head;//p记录数据的开始和结束
int count = 0;
while (pc!=p->tail)
{
count++;
pc = pc->next;
}
count++;
return count;
}
2.7销毁队列
void QueueDestory(Queue* p)
{
if (p = NULL)
{
printf("已销毁!");
}
else
{
Qnode* pcur = p;
while (pcur)//引用空没有任何问题,但不能引用野指针
{
Qnode* next = pcur->next;
free(pcur);
pcur = next;
}
printf("销毁完成!");
}
}
2.8、测试
#include"FileName.h"
//队列
int main()
{
Queue p;
int i = 0;
QueueInit(&p);
for (i = 0;i < 10;i++)
{
Queuepush(&p, i);
}
printf("数据个数:%d\n", Qsize(&p));
int c = 5;
while(c--)
{
printf("%d-->", QueueFront(&p));//打印一个删除一个
Queuepop(&p);
}
printf("NULL\n");
printf("数据个数:%d\n", Qsize(&p));
QueueDestory(&p);
return 0;
}
输出结果:
不知道大家发现了没有,我们在调用函数时传入的并不是二级指针,这是为什么呢?
首先我们要明确一点,就是你要是想改变某个东西,就得传址调用。那我们现在只需要传入存放首尾节点的变量就好了呀(我们声明的第二个结构体类型)。我们传入的p是结构体变量,那接收他的地址的形参就应该是一级指针。
现在为什么接受的形参是一级指针这个问题解释清了,但是大家可能疑惑,为啥之前我们传入的都是一级指针,而这里是结构体变量呢?
我们拿一下单链表那一节初始化节点的代码:
SListNode* BuySListNode(SLTDateType x)
{
SListNode* Node = (SListNode*)malloc(sizeof(SListNode));
Node->data = x;
return Node;
}
在这里,我们要给一个节点开辟空间,必须拿一个指针类型的变量来接收。于是我们想引用某个节点,那就得传入这个指针的地址。那你又问了,顺序表呢?除了初始化,我们也没有用二级指针啊!原因是我们开辟空间是给结构体里的int*a(指针)开辟的空间,并不是直接给这个结构体变量开辟的,所以说我们也不需要二级指针作为形参。相信到这我就给大家解释清楚这个问题了。
好了,今天的内容就分享到这,我们下期再见!