今天终于是把队列的基本操作学完了,队列和栈其实大同小异,队列遵循的是先进先出原则,而栈遵循的是先进后出原则,形象的来说,栈是一个杯子,而队列是一个水管,谁先进队自然就会先流出来。
1.队列创建以及初始化
这次还是用链表来实现,与栈不同的是,由于队列是一个两头都开口的结构,入队和出队分别在两个不同的方向进行,因此不仅需要头指针(head)还需要一个尾指针(tail)。
typedef struct NODE{
int data;
struct NODE*next;
}NODE,*LINK_NODE;//创建节点
typedef struct QUENE
{
LINK_NODE head,tail;//队列首尾
int size;//队列大小
}QUENE;
然后是初始化了,但是我自我觉得对节点的初始化好像一直没有什么用......
LINK_NODE initNODE(){
LINK_NODE newnode=(LINK_NODE)malloc(sizeof(NODE));
newnode->data=0;
newnode->next=NULL;
return newnode;
}//节点初始化
void initquene(QUENE* quene){
quene->head=NULL;
quene->size=0;//长度没有
quene->tail=NULL;//头尾指针为空
}//队列的初始化
2.队列的插入(入队)以及删除(出队)
其实对于链表,栈,队列而言,感觉这两个操作都是最核心的操作,因为这涉及一些指针的转换,顺序的问题,自我感觉在实现的时候有点绕。
1.入队,操作队尾
入队是这样的,在队尾不断添加新节点,看起来比较简单但是在实现的时候,我想分享一下我自己的问题。
我对tail指针有一些误解,我一开始以为tail是一个节点中的next指针,因为我是从第一个节点的插入开始想的,我在想,head是新节点的首地址,那么tail不就是新节点中的next指针吗?
但是实际上,tail应该是队列中最后一个节点的地址,并非最后一个节点的next指针。针对第一个节点而言,第一个节点和最后一个节点是一个,那head和tail就重叠了,这样才是正确的操作。(见下图)
所以这样之后后面的操作就很顺畅了,只需要每次新添加一个节点,第一步,先连接,把新节点和原来的队尾连接;第二步,再更新,把队尾更新为现在新节点的地址即可。请看代码。
void pushquene(QUENE* quene,int element){
LINK_NODE newnode=(LINK_NODE)malloc(sizeof(NODE));//新节点
newnode->data=element;
if(quene->size==0){ //对插入第一个节点单独讨论
quene->head=newnode;
quene->tail=newnode; //第一个节点就是最后一个节点,所以首尾指针一致
}
else{
quene->tail->next=newnode; //后面节点的插入,先连接
quene->tail=newnode; //再更新
}
quene->size++; //变更长度
}
2.出队,操作队首
出队的时候是先入先出,因此都是从队首出去的,这里也有几个小细节。
第一,首先要判断队列是不是已经空了,如果size=0,那么就直接返回错误。当然,我们在实操的时候不会这样,因为最后一个元素出队之后,次数就会结束,比如5个元素,那么只操作5次,就不会出现长度为零的情况(最后一次长度是为一的,操作完之后才会为零)。
第二,在指针的变化中,针对使用新变量,我总结了一下:要被释放的节点,第一是要用新变量存储其中的数据(释放了之后就用不了了,所以先存起来,返回的时候用新变量),第二是要用新指针存储该节点的后继(next指针),道理也是一样的,释放了之后就不能用了。
然后,操作的顺序问题,我的总结:先不要更改head指针(因为这个head是要被释放的),因此先直接释放原有的head节点,再用已经存在新指针里面的head->next指针给已经被释放的head赋值,这样才是正确的更新操作。
第三,当最后一个元素出队的时候,head指针和tail指针再一次重叠(是不是和之前入队的时候似曾相识?),那么由于head指针是要被更换成为head->next的,而此时head->next指针就是指向NULL的,因此相应的,我们也要把tail指针置为空,否则,tail指针还是会指向这个已经被释放的节点,这是没有意义的。
下面先看一下图,再上代码。
对了,这里一直想说一下这个关系(橙色部分),感觉还是挺重要的。
代码部分:
int popquene(QUENE* quene){
if (quene->size == 0) {
printf("Queue is empty.\n"); //队列为空
return -1;
}
LINK_NODE cur=quene->head->next;//新指针存储
int res=quene->head->data;//新变量存储数据
free(quene->head);
quene->head=cur; //更新头节点
if(quene->head==NULL){
quene->tail=NULL; //针对最后一个节点的操作
}
quene->size--; //更新长度
return res;
}
3.查找元素
这就很简单了,但是我还是出了一些问题,第一次写函数的时候灵光乍现竟然用了popquene(出队)函数,然后死循环了。在AI的帮助下我才明白出队会改变队列的内容啊!所以乖乖的用遍历就好了~~~
bool SearchQuene(QUENE* quene, int target) {
LINK_NODE current = quene->head;
while (current != NULL) {
if (current->data == target) {
return true;
}
current = current->next;
}
return false;
}
虽然队列好像很简单,但是好像写的是最长的一次,里面有很多个人的教训经验在里面,希望大家多多支持~~~