带头链表
链表中有一种结构是带头结构,这个链表中会有一个哨兵位置的结构体这个结构体与链表的其他数据结构是一样的但是并不存储有效数据,只是单单作为一个标识符存在。有了这个哨兵位置我们就可以将其视为头部,以后的所有操作都与其他结点操作相同不必区分是否需要对头结点指针进行二级指针的传参,可以方便后续的操作。
队列
队列结构与栈结构都是一种特殊的线性结构,其中队列结构是对于数据的进出有一定的限制,先进入的数据必需先出去,我们想象有一个通道,每个人都是从通道尾部进入,然后从通道的前部出去若通道的宽度只能容纳一个人后面的人必须排在前一个人的后面,这样出去的时候必定是先进来人先走出通道。
队列就是数据先进先出(FIFO)的一种线性表。
为什么用链表实现队列
我们为什么用链表而不是数组实现呢,数组的数据插入方式适合后插,因为当我们前插的时候是需要频繁的挪动数组内的数据的,所以我们越晚插入的数据所在的位置越后,当我们拿出这个数据的时候是数组第一个元素最早进来的所以需要先出,这样我们出了第一个位置的数据那这个位置我们是空着还是用下一个数据去填充呢,空着就浪费空间,填充的话就跟头插一样需要频繁的挪动数据两头不到岸,所以数组并不适合实现队列。
链表实现队列是非常合适的。因为链表对于尾插操作和头删操作都非常方便,而队列新数据的入队就是从队尾进入,出队就是从对头出队。
链表结构的队列实现
创建队列
我们这里定义了两个结构体一个是用于存储数据的,一个是用于管理队列,管理队列的结构体分别包含两个指针,一个指向队列的哨兵位置,一个指向队列中最后一个结点,这样我们再进行入队时就不需要遍历链表了。
这里是对队列的初始化函数,传入的值是管理队列的结构体的地址,我们先是创建了一个队列的哨兵位,这时还没数据在队列中,将其next指针指向NULL,这时的头尾结点都是哨兵位所以将管理队列的结构体中的两个指针都指向哨兵结点。
入队
有了队列我们就能插入数据了,这里并不需要判断队列的是否为空,因为至少都有一个哨兵位在队列中,而且有无有效数据操作都是一样的,新结点是尾插所以将原来的尾巴结点next指针指向新结点,将尾结点指针赋值为新结点。这里我们创建指针是另外写了一个函数如下
出队
这里是一个出队操作,先进行判空,若队列为空不需要出队了直接返回。这里的还有一个特殊情况是当队列中只有一个有限数据的时候,当队中有多个数据时头删操作并不会影响到队尾的,而只有一个数据时我们不去理会尾指针当这个结构体被释放后尾指针就会成为野指针,所以我们判断,当队列只有一个数据的时候即哨兵位的next指向的结点就是尾指针指向的结点的时候,将尾指针从新指向哨兵位。然后我们需要定义一个临时指针指向第一个有效数据结点,将哨兵位的next指针指向第二个有效数据,再释放掉临时指针的空间便完成出队。
销毁队列
这里我们再使用完队列后因为时动态开辟的空间,需要对齐进行释放,这里我们直接循环调用出队函数释放掉所有的结点,然后将管理队列的结构体中的两个指针置NULL便可,这样一个队列的基本结构就完成了。
队列使用中的辅助函数
其实再队列的使用中我们对队列很多情况并不了解的,管理用的结构体也只有头尾指针,若是再加入其余的数据这个数据是否需要使用是个问题,而且也不能一直添加新的管理数据,所以我们会有其他对队列进行操作的函数来辅助队列的使用。
队列的有效数据个数
int Quesize(Que* p)//获取有效数据个数
{
int count = 0;
Qnode* tmp=p->front->next;
while (tmp)
{
count++;
tmp = tmp->next;
printf("%d ", count);
}
return count;
}
遍历队列返回有效数据个数。
队列判空
int Queempty(Que* p)//检查队列是否为空
{
if (p->front->next == NULL)
return 1;
return 0;
}
打印队列
void Queprint(Que* p)//打印队列
{
if (p->front->next == NULL)
{
printf("链表为空\n");
return;
}
Qnode* tmp = p->front->next;
while (tmp)
{
printf("%d ", tmp->val);
tmp = tmp->next;
}
printf("\n");
}
取队列头元素值
QueueDate Quefront(Que* p)//获取队列头元素
{
if (p->front->next == NULL)
{ printf("链表为空\n");
return;
}
return p->front->next->val;
}
取尾部元素值
QueueDate Queback(Que* p)//获取队列尾部元素
{
if (p->front->next == NULL)
{
printf("链表为空\n");
}
return p->rear->val;
}