思考: 所谓队列 ,就是在一端进行删除出队,一段进行入队, 可是其未规定这两端的的距离 .
在上一节 ,我们采用包含 队首指针和队尾指针的头结点来指向链表的队首和队尾节点 ,我们难道不能直接在节点上定义指针吗? 队尾指针指向 链表最后一个节点,然后队尾节点再直接定位指向队首,
这样我们就实现了队列的首尾指针的定义
说白了,就是在单链表上加了一个rear队尾结点,用来指向最后一个节点 , 逻辑上是一个队列
那链队只需要定义一个尾指针即可 , 我们插入元素时,让队尾元素指向新元素 , 队尾指针指向新元素 ,然后新元素的后继指针指向队头元素
● 链队四要素
•队空条件: rear = NULL
• 队满条件: 不考虑
• 进队e操作 : 将包含e的节点插入到单链表表尾
• 出队操作: 删除单链表首节点
注意: 我们的链队 是用单链表承接的, 所以每个节点只包含数据域 和 指针域(我们只是定义了一个单链表,然后变换了指针,然后把他看成了队列而已)
定义单链表结点的指针和数据 typedef struct LNode { ElemType data; //节点的数据区,用类型定义的结构属性 struct LNode *next; }LinkList;
●初始化队算法:
将链表的队尾指针置空即可
//传入要置空的队列 void initQueue(LinkList *&rear) { //直接置空即可 rear=NULL; }
● 判断队空的算法
即判断链队的队尾指针指向的是否有节点
bool queueEmpty(LinkList *rear) { return(rear==NULL); }
● 进队的算法
先把新元素打造成一个新节点,然后插入到队列末尾即可
//传入要插入的链队 , 存新元素的变量
void enQueue(LinkList *&rear,ElemType x) {
打造新节点 ,传入新元素
LinkList*p; p=(LinkList*)malloc(Linkist)); p->data=x;
先判断链队是否为空,为空,则 新节点作为唯一一个节点, 其队首指针也指向新节点
if (rear==NULL) { //单链表节点的后继指针指向自己很合理 p->next=p; //rear也是一个单链表节点,其将p赋值给自己,两个管理者管理同一个地方,即代表同一个链队 rear=p; }
不为空则把新节点插入到队尾节点的后面,并且移动相关指针
原来队尾节点的后继指针指向新节点 , 然后 新节点的后继指针指向队头 ,并且标志队尾的节点也移向新元素,标志队尾
else { //新节点的后继指针指向队头元素 rear->next(存放着队头元素的地址) p->next=rear->next; //插入前的队尾节点的地址存放在rear中 ,让其后继指针指向p即可 rear->next=p; //rear向后移动到 p,标志队尾节点 rear=p; } }
注意: 这时候,有的朋友会疑惑 ,我们为什么要这种顺序呢? 换一下行不行 ,我们还是那句话,涉及到指针的时候一定要非常小心 ,我们要把队尾元素 an 指向 新元素x ,我们现在已经知道x的地址 , 还有 队尾节点 an的地址(存放在 rear中) ,
我们直接 第一步写: rear->next = p
此时会把 rear->next的指针覆盖掉 ,rear->next 指向的是队头节点 a1,我们就丢失了后续节点信息,无法接着链接
所以要先把 rear -> next 利用完,再进行相应覆盖
所以先把新节点的后继指针指向 a1
即 : p->next=rear->next;
然后再将队尾元素an和 新节点 p 链接起来
即: rear -> next = p
最后把 rear 指向 p
●出队算法
就是把 队尾指针rear 指向的元素删除,然后 rear指向上一个元素,
(1)当链队是空的时候 ,我们当然出队不了元素
(2)当队列中只有一个元素 ,我们就不需要考虑其他节点了,直接将出队节点释放,然后置空rear 即可
(3)当队列中不止一个节点,说明我们出队完, 需要把 rear 指向前一个节点,前一个节点的后继指针指向 队首的第一个节点
//传入 要出队的链表 ,存储出队元素的地址 bool deQueue(LinkList*&rear,ElemType&x) { //要出队的元素位于队头,给要出队的节点起名,存储其地址,当其出队后,即可释放其空间 LinkList*q; //如果链队为空,则无法出队元素 if (rear==NULL) { return false; } //如果链队只有一个元素,就是队尾指针rear和队首rear->next都指向同一个节点时 else if(rear->next==rear) { //把要出队的节点传出 x=rear->data; //释放要出队的节点空间 free(rear); //节点置空 rear=NULL; } //经过上两个条件的筛选,下面的链队不止一个元素 else { //先出队元素地址赋值给q,方便后续释放 //我们要出队的元素就位于队尾节点的后继节点 q=rear->next; //将出队元素数据传出 x=q->data; //然后将rear的后继指针指向 q的下一个节点 rear->next=q-next; //释放队首节点 free(q); } return true; }