当前位置:   article > 正文

队列的链表实现方式(C++版)_用链表实现队列c++

用链表实现队列c++

队列基于链表有两种实现方式:一种是带头结点的实现方式,一种是不带头结点的实现方式。

带头结点的实现方式:

基础代码:

  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #define ElemType int
  4. #define null NULL
  5. typedef struct LNode{ //链表结点
  6. ElemType data;
  7. struct LNode *next;
  8. }LNode;
  9. typedef struct QNode{ //队列指针
  10. LNode *front,*rear;
  11. }QNode;

队列初始化:

  1. //队列初始化
  2. bool initQueue(QNode *queue){
  3. LNode *head = (LNode *)malloc(sizeof(LNode));
  4. //内存空间分配失败
  5. if(head == null){
  6. return false;
  7. }
  8. head->next = null;
  9. queue->front = head;
  10. queue->rear = head;
  11. return true;
  12. }

队列判空:

  1. //队列判空
  2. bool isEmpty(QNode *queue){
  3. return queue->front == queue->rear;
  4. }

入队:

  1. //入队
  2. bool EnQueue(QNode *queue,ElemType e){
  3. //链表的入队不用判队列满
  4. LNode *s = (LNode *)malloc(sizeof(LNode));
  5. s->data = e;
  6. s->next = null;
  7. queue->rear->next = s;
  8. queue->rear = s;
  9. return true;
  10. }

出队:

  1. //出队
  2. bool DeQueue(QNode *queue,ElemType &x){
  3. //链表的出队要判队列空
  4. if(isEmpty(queue)){
  5. return false;
  6. }
  7. //最后一个结点
  8. if(queue->front->next == queue->rear){
  9. queue->rear = queue->front;
  10. }
  11. LNode *d = queue->front->next;
  12. x = d->data;
  13. queue->front->next = d->next;
  14. free(d);
  15. return true;
  16. }

队列的删除(保留头结点):

  1. //队列的删除(保留头结点)
  2. bool deleteQueue(QNode *queue){
  3. //队列为空,无元素可删
  4. if(isEmpty(queue)){
  5. return false;
  6. }
  7. while(queue->front->next != queue->rear){ //删除非最后一个元素
  8. LNode *d = queue->front->next;
  9. queue->front->next = d->next;
  10. free(d);
  11. }
  12. //删除最后一个元素需要将尾指针指向头结点
  13. queue->rear = queue->front;
  14. free(queue->front->next);
  15. queue->front->next=null;
  16. return true;
  17. }

队列的销毁(不保留头结点):

  1. //队列的销毁(不保留头结点)
  2. bool DestoryQueue(QNode *queue){
  3. //删除队列中元素
  4. deleteQueue(queue);
  5. //释放头结点
  6. free(queue->front);
  7. queue->front = null;
  8. queue->rear = null;
  9. return true;
  10. }

获取队头元素:

  1. //获取队头元素
  2. bool getQueueHead(QNode *queue,ElemType &x){
  3. if(isEmpty(queue)){
  4. return false;
  5. }
  6. x = queue->front->next->data;
  7. return true;
  8. }

队列的打印:

  1. //队列的打印
  2. void printQueue(QNode *queue){
  3. LNode *head = queue->front;
  4. while(head->next!=null){
  5. printf("%d\n",head->next->data);
  6. head=head->next;
  7. }
  8. }

测试代码及结果:

  1. int main(int argc, char *argv[])
  2. {
  3. //队列的两个指针在栈空间
  4. QNode queue;
  5. initQueue(&queue);
  6. EnQueue(&queue,1);
  7. EnQueue(&queue,2);
  8. EnQueue(&queue,3);
  9. EnQueue(&queue,4);
  10. EnQueue(&queue,5);
  11. ElemType x;
  12. getQueueHead(&queue,x);
  13. printf("队头元素是%d\n",x);
  14. getQueueHead(&queue,x);
  15. printf("队头元素是%d\n",x);
  16. ElemType e;
  17. DeQueue(&queue,e);
  18. printf("弹出的元素是%d\n",e);
  19. printQueue(&queue);
  20. EnQueue(&queue,6);
  21. EnQueue(&queue,7);
  22. printf("---------------\n");
  23. printQueue(&queue);
  24. //删除队列元素
  25. //deleteQueue(&queue);
  26. //printQueue(&queue);
  27. //bool flag = isEmpty(&queue);
  28. //printf("元素清空后为空吗%d\n",flag);
  29. //销毁队列元素
  30. DestoryQueue(&queue);
  31. bool flag = isEmpty(&queue);
  32. printf("队列销毁后为空吗%d\n",flag);
  33. return 0;
  34. }

不带头结点的实现方式:

队列的初始化:

  1. //队列初始化
  2. bool initQueue(QNode *queue){
  3. queue->front = null;
  4. queue->rear = null;
  5. return true;
  6. }

队列判空:

  1. //队列判空 queue->front == null || queue->rear == null 或者的判空关系也对
  2. bool isEmpty(QNode *queue){
  3. return queue->front == null && queue->rear == null;
  4. }

入队:

  1. //入队
  2. bool EnQueue(QNode *queue,ElemType e){
  3. //链表的入队不用判队满
  4. LNode *s = (LNode *)malloc(sizeof(LNode));
  5. s->data = e;
  6. s->next = null;
  7. //入队时是第一个元素
  8. if(queue->rear == null){
  9. queue->front = s;
  10. queue->rear = s;
  11. return true; //没这个return,最终结果也正确,但是第一个结点会在第二个结点没有入队时成环(自己指向自己)
  12. }
  13. //入队时不是第一个元素
  14. queue->rear->next = s;
  15. queue->rear = s;
  16. return true;
  17. }

出队:

  1. //出队
  2. bool DeQueue(QNode *queue,ElemType &x){
  3. //链表的出队要判队空
  4. if(isEmpty(queue)){
  5. return false;
  6. }
  7. LNode *d = queue->front;
  8. x = d->data;
  9. queue->front = d->next;
  10. free(d);
  11. //删除到最后一个结点了
  12. if(queue->front == null){
  13. queue->rear = null;
  14. }
  15. return true;
  16. }

队列的销毁:

  1. //队列的销毁
  2. bool DestoryQueue(QNode *queue){
  3. //队列为空,无元素可删
  4. if(isEmpty(queue)){
  5. return false;
  6. }
  7. while(queue->front != null){
  8. LNode *d = queue->front;
  9. queue->front = d->next;
  10. free(d);
  11. }
  12. //删完了,queue->rear指针置为null
  13. queue->rear=null;
  14. return true;
  15. }

获取队头元素:

  1. //获取队头元素
  2. bool getQueueHead(QNode *queue,ElemType &x){
  3. if(isEmpty(queue)){
  4. return false;
  5. }
  6. x=queue->front->data;
  7. return true;
  8. }

队列的打印:

  1. //队列的打印
  2. void printQueue(QNode *queue){
  3. LNode *head = queue->front;
  4. while(head!=null){
  5. printf("%d\n",head->data);
  6. head=head->next;
  7. }
  8. }

测试代码及结果:

  1. int main(int argc, char *argv[])
  2. {
  3. QNode *queue=(QNode *)malloc(sizeof(QNode));
  4. initQueue(queue);
  5. EnQueue(queue,1);
  6. EnQueue(queue,2);
  7. EnQueue(queue,3);
  8. EnQueue(queue,4);
  9. EnQueue(queue,5);
  10. ElemType x;
  11. getQueueHead(queue,x);
  12. printf("队头元素是%d\n",x);
  13. getQueueHead(queue,x);
  14. printf("队头元素是%d\n",x);
  15. ElemType e;
  16. DeQueue(queue,e);
  17. printf("出队的元素是%d\n",e);
  18. printQueue(queue);
  19. EnQueue(queue,6);
  20. EnQueue(queue,7);
  21. printf("---------------\n");
  22. printQueue(queue);
  23. bool flag1 = isEmpty(queue);
  24. printf("元素销毁前为空吗%d\n",flag1);
  25. DestoryQueue(queue);
  26. printf("销毁后的链表打印\n");
  27. printQueue(queue);
  28. bool flag = isEmpty(queue);
  29. printf("元素销毁后为空吗%d\n",flag);
  30. return 0;
  31. }

带头结点和不带头结点的测试方法是有差异的,一个是声明了一个结构体,一个是声明了一个指针指向了堆内存的QNode型对象。

这两种声明方式都能执行出正确的结果, 第一种声明结构体的方式,相当于front指针,rear指针在栈空间声明。销毁链表也不用管这块存储空间,他会随着函数的调用完成而消失。

第二种声明方式,相当于在堆内存中开辟了一个结点,去存储front指针,rear指针。这里我们链表销毁只是释放了LNode结点,并没有将QNode结点释放。这里我们用的是一级指针。一级指针是无法将QNode结点释放的,准确的说,一级指针可以将QNode所占的堆内存回收,但是无法改变main函数中QNode指针的指向,导致main函数中的QNode指针指向了一块被回收的未知区域。

<<链表操作的二级指针问题>>有说明这个问题。

正确的做法是: 我们函数的参数使用二级指针即可。

在main函数中写QNode *head; 不主动为其在堆内存中分配空间,程序依然不报错,是程序默认为其在堆内存中分配了一个结点空间吗?有没有懂C/C++的大佬帮忙解惑一下。

结论:

无论是带头结点还是不带头结点的队列的实现方式。我觉得复杂度差不多。

出队的时候都要判断是否为最后一个元素,进而改变尾指针的指向。

最好是两种都实现一下,自己体会一下。

队列的变种:

1.双端队列(允许两端插入、两端删除)

2.输入受限的双端队列(允许一端插入、两端删除)

3.输出受限的双端队列(允许两端端插入、一端删除)

队列的变种经常会给出 入队顺序、判断合法的出队顺序。

针对2而言。输入受限,所以根据这一特性,把输出元素序列的第一个元素之前的元素统统入队。

针对3而言,输出受限。所以根据这一特性,先把输出顺序对应好,看元素按顺序从两端入队能否得到此输出序列。

双端队列的实现,如果使用链表实现,需要使用双向链表。如果使用静态数组实现,需要使用逻辑上循环的静态数组。

如有错误,望指正!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/706969
推荐阅读
相关标签
  

闽ICP备14008679号