[关闭]
@rg070836rg 2015-08-16T15:09:23.000000Z 字数 3593 阅读 1705

队列的三种实现方式

data_structure

简介

三种实现方式,其实就是指,循环队列如何实现判空判满,区别就在这一块,原因是,如果不修改普通队列,会出现二义性,因为空满的状态其实是同一种状态。 下面介绍这三种方式。

方式一

通过空出一个位置,解决判空/满的冲突,这是第一次介绍循环队列,附上全部实现:

  1. //模板头文件
  2. template<class T,int MAXSIZE> //定义类模板CirQueue
  3. class CirQueue
  4. {
  5. public:
  6. CirQueue();
  7. ~CirQueue();
  8. void EnQueue(T x);//入栈操作,将元素x入栈
  9. T DeQueue(); //出栈操作,将队头元素出队
  10. T GetQueue(); //取队头元素
  11. bool IsFull(); //判断队列是否为满
  12. bool Empty(); //判断队列是否为空
  13. private:
  14. T data[MAXSIZE]; //元素域
  15. int front,rear; //队头和队尾的指针
  16. };

队列,其实很简单,他是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表,所以比较容易掌握。

下面附上具体函数的实现:

  1. /*
  2. * 无参构造函数,
  3. */
  4. template<class T,int MAXSIZE>
  5. CirQueue<T,MAXSIZE>::CirQueue()
  6. {
  7. front=rear=0;
  8. }
  9. /*
  10. * 入队函数,因为要求在队尾增加元素,
  11. * 所以,如果线性表未满
  12. * 让尾指针向后移,为了不出现假溢出,充分利用空间,
  13. * 所以有取模操作,当到达尾部最大值,则回到0,重新开始。
  14. * 插入时先移动指针,后插入
  15. */
  16. template <class T,int QueueSize>
  17. void CirQueue<T,QueueSize>::EnQueue(T x)
  18. {
  19. if(IsFull()) //判满
  20. {
  21. cout<<"上溢"<<endl;
  22. return;
  23. }
  24. rear = (rear+1) % QueueSize; //队尾指针在循环意义上加1
  25. data[rear] = x; //在队尾处插入元素
  26. }
  27. /*
  28. * 出队函数,在队头弹出元素,
  29. * 所以,如果线性表不空
  30. * 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子
  31. * 那么进行取模操作,回到0位置。
  32. */
  33. template <class T,int QueueSize>
  34. T CirQueue<T,QueueSize>::DeQueue()
  35. {
  36. if(Empty()) //判空
  37. {
  38. cerr<<"下溢"<<endl;
  39. }
  40. front = (front+1) % QueueSize; //队头指针在循环意义上加1
  41. return data[front]; //读取并返回出队前的队头元素
  42. }
  43. /*
  44. * 取队头元素,不出队伍。
  45. */
  46. template <class T,int QueueSize>
  47. T CirQueue<T,QueueSize>::GetQueue()
  48. {
  49. if(Empty()) //判空
  50. {
  51. cerr<<"下溢"<<endl;
  52. exit(0);
  53. }
  54. int i = (front+1) % QueueSize; //队头指针在循环意义上加1
  55. return data[i];
  56. }
  57. /*
  58. * 判空函数
  59. * 如果当两指针重合,队列为空
  60. */
  61. template <class T,int QueueSize>
  62. bool CirQueue<T,QueueSize>::Empty()
  63. {
  64. return front == rear;
  65. }
  66. /*
  67. * 判满函数
  68. * 如果尾指针逻辑后一格为头指针
  69. * 表示空间满
  70. */
  71. template <class T,int QueueSize>
  72. bool CirQueue<T,QueueSize>::IsFull()
  73. {
  74. if( (rear+1) % QueueSize == front)
  75. return true;
  76. else
  77. return false;
  78. }

下面对这个函数做个基本的测试。。

  1. int main()
  2. {
  3. CirQueue<int,5> Q;
  4. //塞满整个队列
  5. cout<<"塞满队列。。。"<<endl;
  6. for (int i=0;;i++)
  7. {
  8. if (!Q.IsFull())
  9. {
  10. Q.EnQueue(pow(2,i));
  11. }
  12. else
  13. break;
  14. }
  15. //测试访问头元素
  16. cout<<"取出头元素:";
  17. cout<<Q.GetQueue()<<endl;
  18. //依次出列
  19. cout<<"依次出列:";
  20. while (!Q.Empty())
  21. {
  22. cout<<Q.DeQueue()<<" ";//因为这个函数写了返回值。。
  23. }
  24. return 0;
  25. }

测试结果如图:
此处输入图片的描述

方式二

为了不浪费一个元素空间,加上一个布尔变量,在入队操作时候把他变为true,如果flag为真,并且front==rear,那么表明队伍满了;在出队的时候,把他设为false,如果最后一个操作为出队,那么又遇到了front==rear,那么表明,这个时候队伍是空的。代码实现如下:

  1. //与上一方式,在这边,仅加了一个bool的控制量
  2. private
  3. bool flag;

下面列出实现时候,与第一种的区别之处:

  1. template<class T,int MAXSIZE>
  2. CirQueue<T,MAXSIZE>::~CirQueue()
  3. {
  4. }
  5. /*
  6. * 无参构造函数,设置flag为假,保证,初始判断让程序判断为空
  7. */
  8. template<class T,int MAXSIZE>
  9. CirQueue<T,MAXSIZE>::CirQueue()
  10. {
  11. front=rear=0;
  12. flag=false;
  13. }
  14. /*
  15. * 入队函数,因为要求在队尾增加元素,
  16. * 所以,如果线性表未满
  17. * 让尾指针向后移,为了不出现假溢出,充分利用空间,
  18. * 所以有取模操作,当到达尾部最大值,则回到0,重新开始。
  19. * 插入时先移动指针,后插入
  20. * 同时,让flag置为true
  21. */
  22. template <class T,int QueueSize>
  23. void CirQueue<T,QueueSize>::EnQueue(T x)
  24. {
  25. if(IsFull()) //判满
  26. {
  27. cout<<"上溢"<<endl;
  28. return;
  29. }
  30. rear = (rear+1) % QueueSize; //队尾指针在循环意义上加1
  31. data[rear] = x; //在队尾处插入元素
  32. flag=true;
  33. }
  34. /*
  35. * 出队函数,在队头弹出元素,
  36. * 所以,如果线性表不空
  37. * 头指针后移,指向当前的第一个元素,如果当前头指针指向最后一个格子,
  38. * 那么进行取模操作,回到0位置。
  39. * 所以队头所指的空间是么有元素的。
  40. * 同时置flag为false
  41. */
  42. template <class T,int QueueSize>
  43. T CirQueue<T,QueueSize>::DeQueue()
  44. {
  45. if(Empty()) //判空
  46. {
  47. cerr<<"下溢"<<endl;
  48. }
  49. front = (front+1) % QueueSize; //队头指针在循环意义上加1
  50. flag=false;
  51. return data[front]; //读取并返回出队前的队头元素
  52. }
  53. /*
  54. * 取队头元素,不出队伍。
  55. */
  56. template <class T,int QueueSize>
  57. T CirQueue<T,QueueSize>::GetQueue()
  58. {
  59. if(Empty()) //判空
  60. {
  61. cerr<<"下溢"<<endl;
  62. exit(0);
  63. }
  64. int i = (front+1) % QueueSize; //队头指针在循环意义上加1
  65. return data[i];
  66. }
  67. /*
  68. * 判空函数
  69. * 如果当两指针重合,队列为空
  70. */
  71. template <class T,int QueueSize>
  72. bool CirQueue<T,QueueSize>::Empty()
  73. {
  74. if ( flag==false && front == rear )
  75. return true;
  76. else
  77. return false;
  78. }
  79. /*
  80. * 判满函数
  81. * 如果尾指针逻辑后一格为头指针
  82. * 表示空间满
  83. */
  84. template <class T,int QueueSize>
  85. bool CirQueue<T,QueueSize>::IsFull()
  86. {
  87. if ( flag==true && front == rear )
  88. return true;
  89. else
  90. return false;
  91. }

测试函数不做变动,测试结果如下:
此处输入图片的描述

方式三

用计数器来存储个数,当计数器等于0的时候,空;当计数器等于MAXSIZE的时候,满!
程序很简单,不多说。

  1. /*
  2. * 判空函数
  3. */
  4. template <class T,int QueueSize>
  5. bool CirQueue<T,QueueSize>::Empty()
  6. {
  7. return count==0;
  8. }
  9. /*
  10. * 判满函数
  11. */
  12. template <class T,int QueueSize>
  13. bool CirQueue<T,QueueSize>::IsFull()
  14. {
  15. return count==QueueSize;
  16. }

测试结果和方法二同。

总结:

三种方法

  • 空出一个格子
  • 加上flag判定
  • 加上计数器

还要注意一点:为了实现循环计数,要掌握下面的语句

  • raer=(rear+1)%MAXSIZE
  • front=(front+1)%MAXSIZE
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注