当前位置:   article > 正文

数据结构——队列(循环队列)_循环队列的数据结构

循环队列的数据结构

队列:也是一种受到限制的线性表,一端进行入队,另一端进行出队,我们将入队的一端一般称作队尾,另一端称作队头。

特点:先进先出。

那么顺序表实现的队列为什么叫做循环队列?

关于循环队列队列的三个难点:

第一个难点:怎么让队列也可以实现入队和出队的时间复杂度都是O(1)?

 

最开始这是由顺序表实现的一个队列,里面的数据为1到5。

我们将6入队,会发现入队的时间复杂度为O(1),只需将6放在5号下标的格子位置。

如果将1出队,那么我们需要将0号下标位置里的1删除,还要将后边的数据依次向前移一个格子,这个时候时间复杂度为O(n),因为需要移动数据。

那应该怎么办呢?这个时候就需要一个队头指针和一个队尾指针,队尾指针指向的是下一个存放数据的位置。

解决办法:想象成环形,让数据不动,指针动。

比如将1出队,我们只需要将0号下标位置里的元素删除,然后再将队头指针指向1号下标的位置。

同时我们还要将它想象成一个环状,如果2到9号下标都有元素,0号和1号下标位置为空,此时,队头指针在2号下标位置,但该队列并没有满(因为0号和1号下标),我们把它想象成环状,这个时候就可以将队尾指针指向0号下标,又可以入队。

注意:这个循环只是一个概念,真是并不存在!

 第二个难点:在第一个解决问题的基础上,会发现一个BUG,判空和判满条件一致(队头指针==队尾指针),怎么将其区分开?

队列为空时队头指针和队尾指针都指向同一个位置,队列满的时候也是相同情况(队头指针==队尾指针)

这里给出两种解决方案:

第一种:加上一个计数器cout(保存有效值个数)
队头指针==队尾指针&&cout==0(判空)
队头指针==队尾指针&&cout!=0(判满)
第二种:在队尾处:浪费一个空间不用,这个浪费的空间不在存储有效值,而是当做一个标记位

一般来说我们用第二种办法,这里的代码也是第二种办法。

第三个难点:怎么求有效长度?

这里记住公式:length=(rear-front+MAX_SIZE)%MAX_SIZE

rear(队尾指针),front(队头指针),MAX_SIZE(可以理解为格子数),此指针非彼指针。

循环队列结构体设计:

  1. // 循环队列结构体设计:
  2. typedef int ELEM_TYPE;
  3. typedef struct Queue
  4. {
  5. ELEM_TYPE* data;//这个指针用来接收malloc从堆里申请的数组
  6. int front;//队头指针(实际上保存的是数组下标,int)
  7. int rear;//队尾指针 (实际上保存的是数组下标,int)
  8. }Queue,*PQueue;

循环队列可执行函数声明:

  1. //初始化
  2. void Init_queue(PQueue pq);
  3. //入队
  4. bool Push(PQueue pq, ELEM_TYPE val);
  5. //出队(还要获取出队的值,需要借助一个输出参数rtval)
  6. bool Pop(PQueue pq, ELEM_TYPE* rtval);
  7. //获取队头值
  8. bool Top(PQueue pq, ELEM_TYPE* rtval);
  9. //判空
  10. bool IsEmpty(PQueue pq);
  11. //判满
  12. bool IsFull(PQueue pq);
  13. //获取有效长度
  14. int Get_length(PQueue pq);
  15. //查找
  16. int Search(PQueue pq, ELEM_TYPE val);
  17. //清空
  18. void Clear(PQueue pq);
  19. //销毁
  20. void Destroy(PQueue pq);
  21. //打印
  22. void Show(PQueue pq);

代码:

先设立一个宏

#define MAX_SIZE 100

初始化:

  1. //初始化
  2. void Init_queue(PQueue pq)
  3. {
  4. assert(pq!= NULL);
  5. pq->data = (ELEM_TYPE*)malloc(sizeof(ELEM_TYPE) * MAX_SIZE);
  6. assert(pq->data != NULL);
  7. pq->front = pq->rear = 0;
  8. }

入队:

  1. bool Push(PQueue pq, ELEM_TYPE val)
  2. {
  3. assert(pq != NULL);
  4. if (IsFull(pq))//判满
  5. {
  6. return false;
  7. }
  8. pq->data[pq->rear] = val;
  9. //pq->rear++;//error
  10. pq->rear = (pq->rear + 1) % MAX_SIZE;
  11. return true;
  12. }

注意,这里的队尾指针不可以通过++方式向后移,而是pq->rear = (pq->rear + 1) % MAX_SIZE;

出队:

  1. //出队(还要获取出队的值,需要借助一个输出参数rtval)
  2. bool Pop(PQueue pq, ELEM_TYPE* rtval)
  3. {
  4. assert(pq != NULL);
  5. assert(rtval != NULL);
  6. if (IsEmpty(pq))//判空,如果空,返回假
  7. {
  8. return false;
  9. }
  10. *rtval = pq->data[pq->front];
  11. //pq->front++;//error
  12. pq->front = (pq->front + 1) % MAX_SIZE;
  13. return true;
  14. }

获取队头值

  1. //获取队头值
  2. bool Top(PQueue pq, ELEM_TYPE* rtval)
  3. {
  4. assert(pq != NULL);
  5. assert(rtval != NULL);
  6. *rtval = pq->data[pq->front];
  7. return true;
  8. }

判空,判满,获取有效长度,查找

  1. //判空
  2. bool IsEmpty(PQueue pq)
  3. {
  4. assert(pq != NULL);
  5. return pq->rear == pq->front;
  6. }
  7. //判满
  8. bool IsFull(PQueue pq)
  9. {
  10. assert(pq != NULL);
  11. return pq->front == (pq->rear + 1) % MAX_SIZE;
  12. }
  13. //获取有效长度
  14. int Get_length(PQueue pq)
  15. {
  16. assert(pq != NULL);
  17. return (pq->rear - pq->front + MAX_SIZE) % MAX_SIZE;
  18. }
  19. //查找
  20. int Search(PQueue pq, ELEM_TYPE val)
  21. {
  22. assert(pq != NULL);
  23. for (int i = pq->front; i != pq->rear; i = (i + 1) % MAX_SIZE)
  24. {
  25. if (pq->data[i] == val)
  26. {
  27. return i;
  28. }
  29. }
  30. return -1;
  31. }

清空,销毁,打印

  1. //清空
  2. void Clear(PQueue pq)
  3. {
  4. assert(pq != NULL);
  5. pq->front = pq->rear = 0;
  6. }
  7. //销毁
  8. void Destroy(PQueue pq)
  9. {
  10. assert(pq != NULL);
  11. free(pq->data);
  12. pq->data = NULL;
  13. pq->front = pq->rear = 0;
  14. }
  15. //打印
  16. void Show(PQueue pq)
  17. {
  18. assert(pq != NULL);
  19. for (int i = pq->front; i != pq->rear; i=(i + 1) % MAX_SIZE)
  20. {
  21. printf("%d ", pq->data[i]);
  22. }
  23. printf("\n");
  24. }

测试:

  1. int main()
  2. {
  3. Queue head;//建立一个循环队列
  4. Init_queue(&head);//初始化
  5. for(int i=0; i<20; i++)//120进队
  6. {
  7. Push(&head, i+1);
  8. }
  9. Show(&head);//打印
  10. ELEM_TYPE tmp;
  11. Pop(&head, &tmp);//出队
  12. printf("pop = %d\n", tmp);//打印出队的值
  13. Show(&head);//打印出队后的队列
  14. printf("length = %d\n", Get_length(&head));//打印队列有效长度
  15. ELEM_TYPE flag;
  16. Top(&head, &flag);//获取队头元素
  17. printf("top = %d\n", flag);//打印队头元素
  18. Show(&head);//打印队列,没有出队
  19. printf("length = %d\n", Get_length(&head));//打印有效长度
  20. Clear(&head);//清空
  21. Show(&head);//打印
  22. printf("length = %d\n", Get_length(&head));//打印有效长度
  23. Destroy(&head);//销毁
  24. return 0;
  25. }

结果:没有内存泄漏

 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号