🍌队列实现的这些细节你都注意到了吗?🍌
队列的实现代码
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.
最后就是一定不要忘记销毁哦,不然会出现内存泄漏的。