队列实现的这些细节你都注意到了吗?

这篇博客探讨了队列的实现细节,包括使用结构体包裹实现,初始化过程中的注意事项,以及在删除操作中如何避免野指针问题。文章强调了初始化时不应预先创建节点,删除时应区分单节点和多节点的情况,以及销毁队列以防止内存泄漏的重要性。
摘要由CSDN通过智能技术生成

🍌队列实现的这些细节你都注意到了吗?🍌

队列的实现代码

Queue.h

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>

typedef int QDataType;
typedef struct Node
{
	QDataType data;
	struct Node* next;
}Node;
typedef struct Queue
{
	Node* phead;
	Node* tail;
}Queue;

void QueueInit(struct Queue* st);
//因为队的性质,这儿只能在尾上插入,故没必要写明是
//尾插,因为只能在尾上插入
void QueuePush(struct Queue* st,QDataType x);
//因为一定是在头上删除,故只要写Pop就行了
void QueuePop(struct Queue* st);
QDataType QueueTop(struct Queue* st);
bool QueueEmpty(struct Queue* st);
int QueueSize(struct Queue* st);
void QueueDestory(struct Queue* st);

Queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

//这样写有个小问题,因为你一开始在初始化里面已经创建了一个节点,如果
//按我这种方法尾插的话,最后会对出一个节点来
//void QueueInit(struct Queue* st)
//{
//	assert(st);
//	st->phead = st->tail = (struct Node*)malloc(sizeof(struct Node));
//	st->tail->next = NULL;
//}
//
//
//void QueuePush(struct Queue* st, QDataType x)        
//{
//	assert(st);
//	struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
//	st->tail->data = x;
//	st->tail->next = newnode;
//	newnode->next = NULL;
//	st->tail = newnode;
//}
	
//换种写法,上面那种写法主要是因为你一开始初始化了导致的
void QueueInit(struct Queue* st)
{
	assert(st);
	st->phead = st->tail = NULL;
}

void QueuePush(struct Queue* st, QDataType x)
{
	assert(st);
	struct Node* newnode = (struct Node*)malloc(sizeof(struct Node));
	newnode->data = x;
	newnode->next = NULL;
	if (st->phead == NULL)
	{
		st->phead = st->tail = newnode;
	}
	else
	{
		st->tail->next = newnode;
		st->tail = newnode;
	}
}

void QueuePop(struct Queue* st)
{
	assert(st);
	assert(!QueueEmpty(st));
	//不可以将只有一个节点和多个节点的情况放在一起说
	//因为一个节点释放时会造成tail的野指针

	/*struct Node* nhead = st->phead->next;
	free(st->phead);
	st->phead = nhead;*/
	if (st->phead->next == NULL)
	{
		free(st->phead);
		//此时如果不把tail置成空,就会造成野指针
		st->phead = st->tail = NULL;
	}
	else
	{
		struct Node* nhead = st->phead->next;
		free(st->phead);
		st->phead = nhead;
	}
}

QDataType QueueTop(struct Queue* st)
{
	assert(st);
	assert(!QueueEmpty(st));
	return st->phead->data;
}

void QueuePop(struct Queue* st)
{
	assert(st);
	assert(!QueueEmpty(st));
	struct Node* nhead = st->phead->next;
	free(st->phead);
	st->phead = nhead;
}

bool QueueEmpty(struct Queue* st)
{
	assert(st);
	return st->phead == NULL;
}

int QueueSize(struct Queue* st)
{
	assert(st);
	int n = 0;
	struct Node* cur = st->phead;
	while (cur)
	{
		n++;
		cur = cur->next;
	}
	return n;
}

void QueueDestory(struct Queue* st)
{
	assert(st);
	struct Node* cur = st->phead;
	struct Node* next;
	while (cur)
	{
		next= cur->next;
		free(cur);
		cur = next; 
	}
	st->phead = st->tail = NULL;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"Queue.h"

int main()
{
	Queue st;
	QueueInit(&st);
	QueuePush(&st,1);
	QueuePush(&st,2);
	QueuePush(&st,3);
	printf("%d\n", QueueTop(&st));
	QueuePop(&st);
	printf("%d\n", QueueTop(&st));
	QueuePop(&st);
	printf("%d\n", QueueSize(&st));
	QueueDestory(&st);
	return 0;
}

这些细节你都注意到了吗?

🥖1 .
你是否注意到这次我们传的是结构体,并不是传指针的方式了。(这其实叫做结构体的包裹)
在这里插入图片描述
要明白,这整个队列其实主要是由两个结构体指针(phead,和tail)来完成的,tail负责队列的输入,phead则记录头,负责队列的输出。所以可以定义一个队列的结构体,结构体里面包涵这两个指针。
在这里插入图片描述
而每一个指针又是Node*类型的,其实也就是一个节点,故又要定义一个节点类型的结构体。
在这里插入图片描述
🍎2.
初始化时一定要想明白
在这里插入图片描述
如果用注释掉的方法来初始化,不是不可以,但不好,因为由于你一开始就创建了一个一节点,所以整个队列最后会多一个节点。

🚛3.
删除的时候要注意特殊情况,一不小心就野指针了!
大家看这段删除的代码,感觉也没有啥问题啊,当只有一个节点的时候,不也满足吗?
其实这儿你忽略了一个很重要的问题,当只有一个节点的时候,这个时候phead和tail指向同一个地址的内存,如果你free(st->phead),其实st->tail的那一块也释放了,因为他们指向的是同一块啊,而你之后只给st->phead赋值了,并没有给st->tail赋值,因此它成了野指针!
在这里插入图片描述
正确的代码应该这样写,要将两种情况分下来说
在这里插入图片描述

👶4.
最后就是一定不要忘记销毁哦,不然会出现内存泄漏的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个数学不怎么好的程序员

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值