关于链队列,其实就是同时拥有队头指针和队尾指针的单链表,一般都是带头结点的,不带头结点的队空为front=rear=NULL,而带头结点的话,只需要front=rear即可
#include <stdio.h>
#include <stdlib.h>
//带头结点的链队列
//队列结点结构体
typedef struct queue{
int data;
struct queue *next;
}squeue;
squeue p;//这里写的是结构体变量,如果不带头结点front=rear=NULL即可,若带头结点,利用front和rear开辟头结点即可
//如果写的是结构体指针
//队头队尾指针域
//我为什么要写这个结构体,我本来是想写在queue结构体里面的。但是我转念一想不对,这个front和rear要指向队头队尾,
//一个队列里面只有一组,所以我就单独写一个结构体来存储front和rear
//然后我存储的指针肯定是为对应结点的结构体指针类型,因为我要指向他,所以为squeue *类型
typedef struct aaa{
squeue *front;
squeue *rear;
};
aaa l;
//初始化
void initqueue(aaa &l)
{
l.front=l.rear=(squeue *)malloc(sizeof(squeue));
l.front->next=NULL;
}
//判断队空
bool emptyqueue(aaa l)
{
//判空条件:front=rear
if(l.front==l.rear) return true;
return false;
}
//入队操作
bool push(aaa &l,int x)
{
squeue *s;
s=(squeue *)malloc(sizeof(squeue));
s->data=x;
l.rear->next=s;
s->next=NULL;
l.rear=s;
return true;
}
//出队操作
bool pop(aaa &l,int &x)
{
//先判空
if(emptyqueue(l)) return false;
squeue *p=l.front->next;
//先删掉这个元素数值
x=p->data;
//front指针不动,将front的next指向front的next的next
l.front->next=p->next;
//删到只剩一个结点要特殊处理,因为如果不处理的话会出现删掉了最后一个节点之后rear成为野指针,野指针不指向任何东西,而且会导致无法正常运行
//而且野指针也不为NULL,NULL可以理解为计算机中有一块特殊的内存区域,但还是什么都没有,和用malloc开辟的区域是不一样的
if(l.rear==p)
{
l.rear=l.front;
}
free(p);
return true;
}
int main()
{
//初始化
initqueue(l);
//开始插入
int j=1;
int x;
//其实这里我的写法不太好,可以换一种写法就是先在循环外面先进行第一个元素的输入
//然后进入循环之后我们while循环条件不变,然后先进行第一个元素的插入,然后再写"请输入你要插入的元素"接着再进行输入
//像以上这种写法可以避免一种错误,就是我先面这种写法会犯的错,我本来第一次写的时候没有写if(x==9999) break
//这也就导致了在我输入完9999之后将9999传入到了队列中,而上面的写法可以避免在书写时会犯这种错误
while(x!=9999)
{
printf("输入你要插入的元素(输入9999结束):\n");
scanf("%d",&x);
if(x==9999) break;
int i=push(l,x);
if(i) printf("插入成功,插入了第%d个元素%d\n",j,l.rear->data);
j++;
}
int k;
char c;
while(1)
{
int a;
k=pop(l,a);
if(k) printf("删除成功,删除掉的元素为%d\n",a);
else printf("队列为空,删除失败\n");
printf("是否继续删除y/n:");
getchar();//注意这里在输入完9999时敲了一个回车,由于scanf只会读取键盘输入的数据,所以这个回车会遗留到下一个循环中
//导致你还没有输入对应c的值,c就吸收了这个回车,所以要用一个getchar()来吸收掉这个多余的回车,而且要注意书写的位置
scanf("%c",&c);//如果我这样写的话会出问题不知道为什么程序到这里会出现卡死的情况,于是我输出了这里的c的值发现这里的c是一个回车
//然后我运用了判断语句if若这里的c为回车则打印111发现还是会卡住不会打印111
//scanf只会接受键盘输入的回车
if(c=='n') break;
}
}
通过今天这次代码书写我首先是学会了链队列的创建判空入队出队操作,其次我在写代码的时候碰到了一些问题,
首先就是结构体部分,一开始我想把所有指针全写在一个结构体里面,但是这样是错的,因为front和rear是凌驾于队列结点结构体之外的,分别都只有一个,所以还需要再写一个结构体来存储front和rear,由于front和rear需要指向对应结点所以应该定义成队列结构体指针类型
第二个就是初始化,链队列初始化是用front和rear指针来开辟空间初始化的,我上面写的是带头结点的,如果是不带头结点的链队列的话,我们初始化的写法为:front=rear=NULL(当然你也可以用front和rear开辟一个空间但是这样写的话就会使得下面插入工作变得稍微麻烦一点),而带头结点的初始化写成front=rear即可,我们可以这样看他们判空条件:
在插入第一个元素结点的时候,我们不带头结点的链队列第一个元素结点是插入到队列中的第一个结点的,也就是插入到所谓的头结点的位置,然后rear是否需要往后移动呢,如果rear往后移动了也就是说我每插入一个结点rear就往后移一位开辟一个新内存,也就是说rear永远指向最后一个元素结点的下一块位置,队列删除元素删到还剩最后一个的时候再进行一次删除,此时front往后移一位就到了rear的位置,此时front=rear,也就是与带头结点的链队列初始化相似,那么我们如果rear不往后移动呢,就是说rear和front都指向元素结点,当你删到最后一个结点的时候,你会发现了不同之处,就是当还剩最后一个结点的时候,此时rear=front,因此他两的判空条件是一样的
第三个就是回车的遗留问题,我们首先要知道scanf只能识别从键盘输入的数据,所以如果在进行scanf输入之前我们有一个回车没有被吸收会导致输入的错误,这时你还没有进行输入,这个scanf里的值就被赋值为回车了,因此我们有的时候就需要在scanf之前写上一个getchar()用来吸收回车,这样才可以继续正常输入
在我最后三行的注释中的问题就是我粗心没有考虑到带头结点的删除操作删除到还剩一个结点的时候需要特殊处理,因为你还剩一个结点的时候,再进行删除操作,由于front始终指向的是头结点,而rear始终指向的是元素结点,我删除掉最后一个元素结点这时rear就成了野指针,野指针指不指向任何东西的指针,他和NULL是不相等的,前面说了带头结点的链队列的判空条件为front=rear,所以我们只需要将删除最后一个结点(队列中只有一个结点)的情况进行特殊处理,当删除到最后一个结点的时候另front=rear即可
总结以下比较重要的点:
不带头结点的链队列:
1,初始化:front=rear=NULL
2,判空:front=rear
带头结点的链队列:
1,初始化:front=rear=(squeue *)malloc(sizeof(squeue)) front->next=NULL
2,判空:front=rear