数据结构与算法之美:队列

        Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!

b7315f7a686d4f82bf44ea40c787fc01.gif

我的博客:<但凡.

我的专栏:《数据结构与算法之美》《题海拾贝》《编程之路》(C语言基础)

欢迎点赞、关注!

 1、队列

队列(Queue)是一种常见的数据结构,它是一种 先进先出(FIFO, First In First Out) 的线性表,类似于排队的场景:最先进入队列的元素最先离开。

特点

  1. 先进先出 (FIFO):

    • 元素从队列的一端(通常称为“队尾”)插入。
    • 元素从队列的另一端(通常称为“队头”)移除。
  2. 两种操作:

    • 入队 (Enqueue): 将元素添加到队尾。
    • 出队 (Dequeue): 从队头移除元素。
  3. 受限访问:

          只能操作队列的两端:队尾插入和队头移除。

 30a8878f62d245e9836e3a55ceb36099.png

现在我们就用链表来模拟实现队列。

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;
}

输出结果:

f25f6185ea4e4b788b12b4f711572b73.png

         不知道大家发现了没有,我们在调用函数时传入的并不是二级指针,这是为什么呢?

        首先我们要明确一点,就是你要是想改变某个东西,就得传址调用。那我们现在只需要传入存放首尾节点的变量就好了呀(我们声明的第二个结构体类型)。我们传入的p是结构体变量那接收他的地址的形参就应该是一级指针。

        现在为什么接受的形参是一级指针这个问题解释清了,但是大家可能疑惑,为啥之前我们传入的都是一级指针,而这里是结构体变量呢?

我们拿一下单链表那一节初始化节点的代码:

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* Node = (SListNode*)malloc(sizeof(SListNode));
	Node->data = x;
	return Node;
}

        在这里,我们要给一个节点开辟空间,必须拿一个指针类型的变量来接收于是我们想引用某个节点,那就得传入这个指针的地址。那你又问了,顺序表呢?除了初始化,我们也没有用二级指针啊!原因是我们开辟空间是给结构体里的int*a(指针)开辟的空间,并不是直接给这个结构体变量开辟的,所以说我们也不需要二级指针作为形参。相信到这我就给大家解释清楚这个问题了。

        好了,今天的内容就分享到这,我们下期再见!

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值