栈与队列



【栈】

         栈是一种特殊的线性表。其特殊性在于限定插入和删除数据元素的操作只能在线性表的一端进行。也就是:后进先出(Last In First Out),简称为LIFO线性表。

栈的基本运算有六种:

构造空栈:InitStack(S)
判栈空: StackEmpty(S)
判栈满: StackFull(S)
进栈: Push(S,x)   可形象地理解为压入,这时栈中会多一个元素
退栈: Pop(S)     可形象地理解为弹出,弹出后栈中就无此元素了。
取栈顶元素:StackTop(S)   不同与弹出,只是使用栈顶元素的值,该元素仍在栈顶不会改变。


        由于栈也是线性表,因此线性表的存储结构对栈也适用,通常栈有顺序栈链栈两种存储结构,这两种存储结构的不同,则使得实现栈的基本运算的算法也有所不同。

        在顺序栈中有"上溢"和"下溢"的概念。"上溢"也就是栈顶指针指出栈的外面,显然是出错了。"下溢"本身可以表示栈为空栈,因此可以用它来作为控制转移的条件。

         链栈则没有上溢的限制,它就象是一条一头固定的链子,可以在活动的一头自由地增加链环(结点)而不会溢出,链栈不需要在头部附加头结点,因为栈都是在头部进行操作的,如果加了头结点,等于要在头结点之后的结点进行操作,反而使算法更复杂,所以只要有链表的头指针就可以了。


1、顺序栈

                          栈与队列

  1. #include "stdafx.h"      
  2. #include <iostream>    
  3. using namespace std;    
  4. #define MAX 10 // MAXIMUM STACK CONTENT    
  5. class stack       
  6. {       
  7. private:       
  8.     int arr[MAX];     
  9.     int top;     
  10. public:       
  11.     stack()     
  12.     {       
  13.         inItStack();     
  14.     }    
  15.     /************************************************************************/    
  16.     /* 初始化栈                                                                     */    
  17.     /************************************************************************/    
  18.     void inItStack()     
  19.     {     
  20.         top=-1;     
  21.     }     
  22.     /************************************************************************/    
  23.     /* 入栈                                                                     */    
  24.     /************************************************************************/    
  25.     void push(int a)     
  26.     {       
  27.         top++;    
  28.         if(top < MAX)  {       
  29.             arr[top]=a;     
  30.         }   else   {       
  31.             cout<<"STACK FULL!!"<<top;       
  32.         }       
  33.     }       
  34.     /************************************************************************/    
  35.     /* 出栈                                                                     */    
  36.     /************************************************************************/    
  37.     int pop()    
  38.     {        
  39.         if(isEmpty())   {       
  40.             cout<<"STACK IS EMPTY ";    
  41.             return NULL;       
  42.         } else {       
  43.             int data=arr[top];     
  44.             arr[top]=NULL;     
  45.             top--;    
  46.             return data;     
  47.         }       
  48.     }       
  49.     
  50.     /************************************************************************/    
  51.     /* 是否为空                                                                     */    
  52.     /************************************************************************/    
  53.     bool isEmpty()    
  54.     {    
  55.         if(top == -1) return true;    
  56.         else return false;    
  57.     }    
  58. };       
  59. int main()       
  60. {       
  61.     stack a;       
  62.     a.push(3);       
  63.     a.push(10);       
  64.     a.push(1);       
  65.     cout<<"Pop:"<<a.pop();          
  66.     return 0;       
  67. }    

        结论:由于栈的插入和删除操作具有它的特殊性,所以顺序栈并不存在插入删除数据元素时需要移动的问题,但栈容量难以扩充的弱点仍就没有摆脱。


2、链栈

         若是栈中元素的数目变化范围较大或不清楚栈元素的数目,就应该考虑使用链式存储结构。人们将用链式存储结构表示的栈称作"链栈"。链栈通常用一个无头结点的单链表表示。

  1. #include "stdafx.h"      
  2. #include <stdio.h>      
  3. #include "stdlib.h"    
  4. #include <iostream>    
  5. using namespace std;    
  6. //宏定义      
  7. #define TRUE   1      
  8. #define FALSE   0      
  9. #define OK    1      
  10. #define ERROR   0      
  11. #define INFEASIBLE -1      
  12. #define OVERFLOW -2     
  13. #define STACKEMPTY -3      
  14.     
  15. #define LT(a,b)   ((a)<(b))      
  16. #define N = 100             
  17.     
  18. typedef int Status;      
  19. typedef int ElemType;      
  20.     
  21. typedef struct LNode{      
  22.     ElemType        data;                   
  23.     struct LNode   *next;         
  24. }LNode, *LinkList;     
  25.     
  26. typedef struct stack{    
  27.     LinkList top;    
  28. } STACK;    
  29.     
  30.     
  31. /************************************************************************/    
  32. /*     接口:  
  33. */    
  34. /************************************************************************/    
  35. void InitStack(STACK &S);    
  36. void Push(STACK &S,ElemType e);    
  37. void Pop(STACK &S, ElemType *e);    
  38. ElemType GetTop(STACK S,ElemType *e);    
  39. int StackEmpty(STACK S);    
  40.     
  41. /************************************************************************/    
  42. /*   
  43. */    
  44. /************************************************************************/    
  45. void InitStack(STACK &S)    
  46. {    
  47.     S.top=NULL;    
  48. }    
  49.     
  50. /************************************************************************/    
  51. /* 入栈   
  52. */    
  53. /************************************************************************/    
  54. void Push(STACK &S,ElemType e)    
  55. {    
  56.     LinkList p;    
  57.     p = (LinkList )malloc(sizeof(LNode));    
  58.     if (!p) exit(OVERFLOW);    
  59.     p->data = e;    
  60.     p->next = S.top;    
  61.     S.top = p;    
  62. }    
  63. /************************************************************************/    
  64. /* 出栈  
  65. */    
  66. /************************************************************************/    
  67. void Pop(STACK &S, ElemType *e)    
  68. {    
  69.     LinkList p;    
  70.     if(StackEmpty(S)) exit(STACKEMPTY);    
  71.     *e = S.top->data;    
  72.     p = S.top;    
  73.     S.top = p->next;     
  74.     free(p);    
  75. }    
  76. /************************************************************************/    
  77. /* 获取栈顶元素内容  
  78. */    
  79. /************************************************************************/    
  80. ElemType GetTop(STACK S, ElemType *e)    
  81. {    
  82.     if(StackEmpty(S)) exit(STACKEMPTY);    
  83.     *e = S.top->data;    
  84. }    
  85.     
  86. /************************************************************************/    
  87. /* 判断栈S是否空   
  88. */    
  89. /************************************************************************/    
  90. int StackEmpty(STACK S)     
  91. {    
  92.     if(S.top==NULL) return TRUE;    
  93.     return   FALSE;    
  94. }    
  95.     
  96. void main()      
  97. {      
  98.     
  99.     STACK S;    
  100.     InitStack( S);    
  101.     Push(S, 3);    
  102.     Push(S, 4);    
  103.     ElemType e;    
  104.     Pop(S,&e);    
  105.     cout<<"Pop elem:"<<e;    
  106. }      


3、栈的应用

1) 数制转换

2)语法词法分析

3)表达式求值等


【队列】

         队列(Queue)也是一种运算受限的线性表,它的运算限制与栈不同,是两头都有限制,插入只能在表的一端进行(只进不出),而

删除只能在表的另一端进行(只出不进),允许删除的一端称为队尾(rear),允许插入的一端称为队头 (Front)。

        队列的操作原则是先进先出的,所以队列又称作FIFO表(First In First Out)

队列的基本运算也有六种:

置空队 :InitQueue(Q)

判队空: QueueEmpty(Q)

判队满: QueueFull(Q)

入队 : EnQueue(Q,x)

出队 : DeQueue(Q)

取队头元素: QueueFront(Q),不同与出队,队头元素仍然保留。


          队列也有顺序存储和链式存储两种存储结构,前者称顺序队列,后者为链队


1、顺序队列

       由于是顺序存储结构的存储空间是静态分配的,所以在添加数据的时,有可能没有剩余空间的情况。

       解决这种“假溢出”情况,使用循环队列。在c语言中,不能用动态分配的一维数组来实现循环队列。若使用循环队列,必须设置

最大队列长度,若无法估计最大长度,就使用链式队列。

  1. #include "stdafx.h"      
  2. #include <stdio.h>      
  3. #include "stdlib.h"    
  4. #include <iostream>    
  5. using namespace std;    
  6. //宏定义      
  7. #define TRUE   1      
  8. #define FALSE   0      
  9. #define OK    1      
  10. #define ERROR   0      
  11. #define INFEASIBLE -1      
  12. #define OVERFLOW -2     
  13. #define QUEUEEMPTY  -3      
  14.           
  15. #define MAX_QUEUE 10 //队列的最大数据元素数目    
  16.     
  17. typedef int Status;      
  18. typedef int ElemType;      
  19.     
  20. typedef struct queue{      
  21.     ElemType        elem[MAX_QUEUE] ;     ///假设当数组只剩下一个单元时认为队满              
  22.     int front;      //队头指针    
  23.     int rear;       //队尾指针       
  24. }QUEUE;     
  25.     
  26.     
  27. /************************************************************************/    
  28. /*     各项基本操作算法。  
  29. */    
  30. /************************************************************************/    
  31. void InitQueue(QUEUE *&Q);    
  32. void EnQueue(QUEUE *Q,ElemType elem);    
  33. void DeQueue(QUEUE *Q,ElemType *elem);    
  34. int QueueEmpty(QUEUE Q);    
  35.     
  36. /************************************************************************/    
  37. /*   
  38.   初始化  
  39.   直接使用结构体指针变量,必须先分配内存地址,即地址的指针  
  40. */    
  41. /************************************************************************/    
  42. void InitQueue(QUEUE *&Q)     
  43. {    
  44.     
  45.     Q = (QUEUE *) malloc (sizeof(QUEUE));    
  46.     Q->front = Q->rear = -1;    
  47.     
  48. }    
  49. /************************************************************************/    
  50. /*     入队                                                                
  51. */    
  52. /************************************************************************/    
  53.      
  54. void EnQueue(QUEUE *Q, ElemType elem)    
  55. {    
  56.     if((Q->rear+1)% MAX_QUEUE == Q->front) exit(OVERFLOW);    
  57.     Q->rear = (Q->rear + 1)%MAX_QUEUE;    
  58.     Q->elem[Q->rear] = elem;     
  59. }    
  60. /************************************************************************/    
  61. /*     出队                                                                 
  62. */    
  63. /************************************************************************/    
  64. void DeQueue(QUEUE *Q,ElemType *elem)    
  65. {    
  66.     if (QueueEmpty(*Q))  exit(QUEUEEMPTY);    
  67.     Q->front =  (Q->front+1) % MAX_QUEUE;    
  68.     *elem=Q->elem[Q->front];    
  69. }    
  70. /************************************************************************/    
  71. /*    获取队头元素内容                                                              
  72. */    
  73. /************************************************************************/    
  74.     
  75. void GetFront(QUEUE Q,ElemType *elem)     
  76. {    
  77.     if ( QueueEmpty(Q) )  exit(QUEUEEMPTY);    
  78.     *elem = Q.elem[ (Q.front+1) % MAX_QUEUE ];    
  79. }    
  80. /************************************************************************/    
  81. /*    判断队列Q是否为空                                                               
  82. */    
  83. /************************************************************************/    
  84. int QueueEmpty(QUEUE Q)    
  85. {    
  86.     if(Q.front==Q.rear) return TRUE;    
  87.     else return FALSE;    
  88. }    
  89.     
  90. void main()      
  91. {      
  92.     
  93.     QUEUE *Q;    
  94.     InitQueue( Q);    
  95.     EnQueue( Q, 1);    
  96.     EnQueue( Q, 2);    
  97.     ElemType e;    
  98.     DeQueue( Q,&e);    
  99.     cout<<"De queue:"<<e;    
  100. }      

2、链队

  1. #include "stdafx.h"      
  2. #include <stdio.h>      
  3. #include "stdlib.h"    
  4. #include <iostream>    
  5. using namespace std;    
  6. //宏定义      
  7. #define TRUE   1      
  8. #define FALSE   0      
  9. #define OK    1      
  10. #define ERROR   0      
  11. #define INFEASIBLE -1      
  12. #define OVERFLOW -2     
  13. #define QUEUEEMPTY  -3      
  14.           
  15.     
  16. typedef int Status;      
  17. typedef int ElemType;      
  18.     
  19. typedef struct LNode {          //链式队列的结点结构    
  20.     ElemType elem;          //队列的数据元素类型    
  21.     struct LNode *next;      //指向后继结点的指针    
  22. }LNode, *LinkList;    
  23.     
  24. typedef struct queue{   //链式队列    
  25.     LinkList front;     //队头指针    
  26.     LinkList rear;      //队尾指针    
  27. }QUEUE;     
  28.     
  29. /************************************************************************/    
  30. /*     各项基本操作算法。  
  31. */    
  32. /************************************************************************/    
  33. void InitQueue(QUEUE *Q);    
  34. void EnQueue(QUEUE *Q,ElemType elem);    
  35. void DeQueue(QUEUE *Q,ElemType *elem);    
  36. void GetFront(QUEUE Q,ElemType *elem) ;    
  37. bool QueueEmpty(QUEUE Q);    
  38.     
  39. /************************************************************************/    
  40.     
  41.     
  42. /*初始化队列Q  */    
  43. void InitQueue(QUEUE *Q)    
  44. {    
  45.     Q->front = (LinkList)malloc(sizeof(LNode));    
  46.     if (Q->front==NULL) exit(ERROR);    
  47.     Q->rear= Q->front;    
  48. }    
  49. /*入队 */     
  50. void EnQueue(QUEUE *Q,ElemType elem)    
  51. {    
  52.     LinkList s;    
  53.     s = (LinkList)malloc(sizeof(LNode));    
  54.     if(!s) exit(ERROR);    
  55.     s->elem = elem;    
  56.     s->next = NULL;    
  57.     Q->rear->next = s;    
  58.     Q->rear = s;    
  59. }    
  60.     
  61. /*出队  */     
  62. void DeQueue(QUEUE *Q,ElemType *elem)    
  63. {    
  64.     LinkList s;    
  65.     if(QueueEmpty(*Q)) exit(ERROR);    
  66.     *elem = Q->front->next->elem;    
  67.     s = Q->front->next;    
  68.     Q->front->next = s->next;    
  69.     free(s);    
  70. }    
  71. /*获取队头元素内容  */     
  72.     
  73. void GetFront(QUEUE Q,ElemType *elem)     
  74. {    
  75.     if(QueueEmpty(Q)) exit(ERROR);    
  76.     *elem = Q.front->next->elem;    
  77. }    
  78. /*判断队列Q是否为空   */     
  79. bool QueueEmpty(QUEUE Q)    
  80. {    
  81.     if(Q.front == Q.rear) return TRUE;    
  82.     return FALSE;    
  83. }    
  84.     
  85. void main()      
  86. {      
  87.     
  88.     QUEUE Q;    
  89.     InitQueue( &Q);    
  90.     EnQueue( &Q, 1);    
  91.     EnQueue( &Q, 2);    
  92.     ElemType e;    
  93.     DeQueue( &Q,&e);    
  94.     cout<<"De queue:"<<e;    
  95. }      

3、应用

【举例1】银行排队
【举例2】模拟打印机缓冲区。
在主机将数据输出到打印机时,会出现主机速度与打印机的打印速度不匹配的问题。这时主机就要停下来等待打印机。显然,这样会降低主机的使用效率。为此人们设想了一种办法:为打印机设置一个打印数据缓冲区,当主机需要打印数据时,先将数据依次写入这个缓冲区,写满后主机转去做其他的事情,而打印机就从缓冲区中按照先进先出的原则依次读取数据并打印,这样做即保证了打印数据的正确性,又提高了主机的使用效率。由此可见,打印机缓冲区实际上就是一个队列结构。
【举例3CPU分时系统
在一个带有多个终端的计算机系统中,同时有多个用户需要使用CPU运行各自的应用程序,它们分别通过各自的终端向操作系统提出使用CPU的请求,操作系统通常按照每个请求在时间上的先后顺序,将它们排成一个队列,每次把CPU分配给当前队首的请求用户,即将该用户的应用程序投入运行,当该程序运行完毕或用完规定的时间片后,操作系统再将CPU分配给新的队首请求用户,这样即可以满足每个用户的请求,又可以使CPU正常工作。