• 数据结构与算法系列研究二——栈和队列


    栈和队列的相关问题分析

    一、栈和队列定义

      栈和队列是两种重要的数据结构。从结构特性角度看,栈和队列也是线性表,其特殊性在于它们的基本操作是线性表的子集,是操作受限的线性表,可称为限定性的数据结构;从数据类型角度看,其操作规则与线性表大不相同,是完全不同于线性表的抽象数据类型。

                      

                           图1 栈的结构                                                 图2 队列的结构

      1.1、是限定在表的一端进行插入和删除操作的线性表。
        插入:称为进栈(或压栈)。
        删除:称为出栈(或退栈)。
        栈顶(top):允许进行插入和删除操作的一端。
        栈底(bottom):不允许插入和删除操作的一端。
     基本操作:
         Initstack(&s):           栈初始化
         Destroystack(&s):   撤消栈
         Clearstack(&s):       置栈空
         Stackempty(s) :      判栈空
         Stacklength(S):      求栈中元素个数
         Gettop(S,&e):        取栈顶元素
         Push(&S,e):           入栈
         Pop(&S,&e):          出栈
      用线性链表表示栈的存储结构,称为链栈。链表头指针始终指向栈顶,同样用top表示。 无必要加头结点,栈空时:top=NULL入栈:每当一个元素进栈时,申请一个新结点,插入到表头。出栈:每当一个元素出栈时,从表头释放一个结点。
      1.2、队列是限定在表的一端进行插入,在另一端进行删除的线性表。
        队头(Front):允许删除的一端。
        队尾(Rear):允许插入的一端。
      基本操作
         InitQueue (&Q):               队列初始化
         DestroyQueue (&Q):      撤消队列
         ClearQueue (&Q):          置队列空
         QueueEmpty(Q) :          判队列空
         Queuelength(Q):           求队列元素个数
         Gethead(Q,&e):            取队列头元素
         EnQueue (&Q,e):          入队列
         DeQueue (&Q,&e):        出队列


    二、用栈实现中缀式求解

      编程实现中缀式求解,设算符只有+,-,*,/,(,)。操作数0~9(可当字符用)。

    2.1、输入输出设计
        1.输入  从控制台读进一个字符串,由于设计时运用了string类中的成员函数,故此此处省略‘#’,只读进一个字符串。读入的时候可能有不同的情况,此处简单起见,设置默认情况下读入的操作数为0~9,另外还有+,-,*,/运算符,但是要按照中缀式的顺序从左到右输入。
        2.输出  此程序的功能是求一个中缀式的值,返回值应该是算式的结果,根据一系列的运算最终输出结果。其结果应该为一个实数值,适宜选用double类型存放。
    2.2、关键数据结构与算法描述
       数据结构:
         通过对问题的分析,此处选用栈类型的存储结构最为适宜,因为其先进后出的特性可以模拟中缀式的计算过程。具体构造如下,此处构造为类的形式:

     1 class Stack{
     2 public:
     3         Stack(){top=-1;}//构造函数,进行初始化操作
     4         ~Stack(){}//析构函数
     5         bool IsEmpty(){
     6             if(top==-1)
     7                 return true;
     8             else
     9                 return false;
    10         }//判断栈是否为空
    11         //返回栈顶元素
    12         T gettop()
    13         {
    14             return a[top];
    15         }
    16         //将元素压栈
    17         void push(T num)
    18         {
    19             if(top>=MAXSIZE)
    20                 return;
    21             a[++top]=num;
    22         }
    23         //将元素弹出栈
    24         T  pop()
    25         {
    26             if(top==-1)
    27                 return  OVER;
    28             return  a[top--];
    29         }
    30 private:
    31       T  a[MAXSIZE];
    32       int top;
    33 };
    View Code

    此处运用C++语言对模板栈进行了定义,定义了指针top和数组a[MAXSIZE];为以后的进栈和退栈操作提供了可能。
    2.3、算法描述
       要完成中缀式求值,最重要的是对读进的字符进行适当的处理,从而求出结果。 

       1.若读到字符就直接如数据栈; 

       2.若读进的是‘(’,直接入符号栈;

       3.若读进的是‘)’,就要判断在‘(’前面有无符号,若有符号,就要进行循环每次将数据栈中的两个元素退栈,按照一定顺序和已经退栈的符号进行运算,运算结果再压入数据栈中,直至到‘(’或为空栈(正常情况下不可能),弹出‘(’;  

       4.若读进的是符号,就要判断优先级,若字符优先级高于符号栈的顶部优先级或者为空栈直接压入符号栈,否则进行循环,弹出顶部元素且再从数据栈弹出两个元素进行运算,结果压入数据栈。直至字符优先级大于栈顶元素或为空栈,此时再将字符压入符号栈中。一直循环上述步骤,直至字符串被读完,然后对符号栈进行查看,若不为空,则继续进行运算,直至最后为空,此时数据栈中必只有一个元素,此元素即为所求结果。输出即可。

    具体算法如下:

     1 double  EvaluateExpression(string  &s)
     2 {
     3     double answer;
     4     for(int i=0;i<s.size();i++)
     5     {
     6         if(s[i]>='0'&&s[i]<='9')//若为0~9字符,直接进数据栈
     7         {
     8             OPND.push(s[i]-'0');
     9         }
    10         else
    11             if(s[i]=='(')//若为左括号直接进栈
    12             {    
    13                 OPTR.push(s[i]);
    14             }
    15             else if(s[i]==')')//若为右括号需判断
    16             {
    17                 while((OPTR.gettop())!='(')
    18                     Count();
    19                 OPTR.pop();
    20             }
    21             else if(OPTR.IsEmpty()||Rank(s[i])>Rank(OPTR.gettop()))//优先级高直接入栈
    22             {
    23                 OPTR.push(s[i]);
    24             }
    25             else
    26             {
    27                 while(Rank(s[i])<=Rank(OPTR.gettop())&&!OPTR.IsEmpty())//否则,先计算已有元素
    28                    Count();
    29                 OPTR.push(s[i]);//然后再进栈
    30             }
    31     }
    32     while(!OPTR.IsEmpty())//若运算符栈为空,就要计算
    33     {
    34         Count();
    35     }
    36 
    37     answer=OPND.pop();//得到最后结果,清空栈
    38     return answer;
    39 }
    View Code

    2.4、测试与理论
       (注意,此处的除号是数学意义上的除法,不是c语言上的整除)
       测试数据有:                   理论结果为:
       2+(4-1)/2*5;              9.5
      (2+3-5/2)*5/1+8           20.5
      ((3+5)*6+1)/7            7
       ((2+2)/2+3*5-8/2)/2-1     5.5
     运行程序得到的结果为:

    2.5、源代码(附录)

    中缀式求解的代码:

      1 #include"iostream"
      2 #include"string"
      3 using namespace std;
      4 #define MAXSIZE  200
      5 #define OVER     -2
      6 //创建模板栈,因有两个栈
      7 template <typename T>
      8 class Stack{
      9 public:
     10         Stack(){top=-1;}//构造函数,进行初始化操作
     11         ~Stack(){}//析构函数
     12         bool IsEmpty(){
     13             if(top==-1)
     14                 return true;
     15             else
     16                 return false;
     17         }//判断栈是否为空
     18 
     19         //返回栈顶元素
     20         T gettop()
     21         {
     22             return a[top];
     23         }
     24         //将元素压栈
     25         void push(T num)
     26         {
     27             if(top>=MAXSIZE)
     28                 return;
     29             a[++top]=num;
     30         }
     31         //将元素弹出栈
     32         T  pop()
     33         {
     34             if(top==-1)
     35                 return  OVER;
     36             return  a[top--];
     37         }
     38 private:
     39       T  a[MAXSIZE];
     40       int top;
     41 };
     42 //定义两个栈
     43 Stack<char> OPTR;
     44 Stack<double> OPND;
     45 //比较优先级,优先级越高,就越先计算
     46 int  Rank(char c)
     47 {
     48     if((c=='(')||(c==')'))
     49         return 1;
     50     else
     51         if((c=='+')||(c=='-'))
     52             return 2;
     53         else if((c=='*')||(c=='/'))
     54             return 3;
     55               else  
     56                   return  -1;
     57 }
     58 //对式子中的加减乘除进行计算
     59 void   Count()
     60 {
     61     double temp;
     62     switch(OPTR.pop())
     63     {
     64         case'+':
     65            temp=OPND.pop();
     66            temp+=OPND.pop();
     67            OPND.push(temp);
     68            break;
     69         case'-':
     70            temp=OPND.pop();
     71            temp=OPND.pop()-temp;
     72            OPND.push(temp);
     73            break;
     74         case'*':
     75            temp=OPND.pop();
     76            temp=OPND.pop()*temp;
     77            OPND.push(temp);
     78            break;
     79         case'/':   
     80            temp=OPND.pop();
     81            temp=OPND.pop()/temp;
     82            OPND.push(temp);
     83            break;
     84         default:
     85            cout<<"操作符有误"<<endl;
     86            break;
     87     }
     88 }
     89 /********************核心算法,计算表达式的算法*************/
     90 double  EvaluateExpression(string  &s)
     91 {
     92     double answer;
     93     for(int i=0;i<s.size();i++)
     94     {
     95         if(s[i]>='0'&&s[i]<='9')//若为0~9字符,直接进数据栈
     96         {
     97             OPND.push(s[i]-'0');
     98         }
     99         else
    100             if(s[i]=='(')//若为左括号直接进栈
    101             {   
    102                 OPTR.push(s[i]);
    103             }
    104             else if(s[i]==')')//若为右括号需判断
    105             {
    106                 while((OPTR.gettop())!='(')
    107                     Count();
    108                 OPTR.pop();
    109             }
    110             else if(OPTR.IsEmpty()||Rank(s[i])>Rank(OPTR.gettop()))//优先级高直接入栈
    111             {
    112                 OPTR.push(s[i]);
    113             }
    114             else
    115             {
    116                 while(Rank(s[i])<=Rank(OPTR.gettop())&&!OPTR.IsEmpty())//否则,先计算已有元素
    117                    Count();
    118                 OPTR.push(s[i]);//然后再进栈
    119             }
    120     }
    121     while(!OPTR.IsEmpty())//若运算符栈为空,就要计算
    122     {
    123         Count();
    124     }
    125 
    126     answer=OPND.pop();//得到最后结果,清空栈
    127     return answer;
    128 }
    129 /**********主函数用于测试************************/
    130 int main()
    131 {
    132     string s;
    133     double answer1;
    134     char c;
    135     while(1)
    136     {
    137 
    138       cout<<"请输入一个中缀式:"<<endl;
    139       cin>>s;
    140  
    141       answer1=EvaluateExpression(s);
    142       cout<<endl<<s<<"="<<answer1<<endl;
    143       cout<<"是否继续?(Y||N)"<<endl;
    144       cin>>c;
    145       if(c=='N'||c=='n')
    146         break;
    147     }
    148     return 0;
    149 }
    View Code

     将中缀式转换为后缀式

     1 void  ShowChange( string   &s)
     2 {
     3     for(int i=0;i<s.size();i++)
     4     {
     5         if(s[i]>='0'&&s[i]<='9')
     6         {
     7             cout<<s[i];
     8         }
     9         else if(s[i]=='(')
    10         {
    11             OPTR.push(s[i]);
    12         }
    13         else if(s[i]==')')
    14         {
    15             while(OPTR.gettop()!='('&&!OPTR.IsEmpty())
    16             {
    17                 cout<<OPTR.pop( );
    18             }
    19             OPTR.pop( );
    20         }
    21         else if(Rank(s[i])<=Rank(OPTR.gettop()))
    22         {
    23             while(OPTR.gettop()!='('&&!OPTR.IsEmpty())
    24              cout<<OPTR.pop();
    25             OPTR.push(s[i]);
    26         }
    27         else if(Rank(s[i])>Rank(OPTR.gettop())) 
    28         {
    29             OPTR.push(s[i]);
    30         }
    31     }
    32     while(!OPTR.IsEmpty())
    33     {
    34         cout<<OPTR.pop();
    35     }
    36 }
    View Code

     将中缀式转换为前缀式

     1 void Z2QChange(string &s)
     2 {
     3     for(int i=s.size()-1;i>=0;i--)
     4     {
     5         if(s[i]>='0'&&s[i]<='9')
     6         {
     7             OPND.push(s[i]);
     8         }
     9         else if(s[i]==')')
    10         {
    11             OPTR.push(s[i]);
    12         }
    13         else if(s[i]=='(')
    14         {
    15             while(OPTR.gettop()!=')'&&!OPTR.IsEmpty())
    16             {
    17                 OPND.push(OPTR.pop());
    18             }
    19             OPTR.pop();
    20         }
    21         else if(Rank(s[i])>=Rank(OPTR.gettop())||OPTR.IsEmpty()||OPTR.gettop()==')')
    22         {
    23             OPTR.push(s[i]);
    24         }
    25         else
    26         {
    27             while(Rank(s[i])<Rank(OPTR.gettop())&&!OPTR.IsEmpty())
    28               OPND.push(OPTR.pop());
    29             OPTR.push(s[i]);
    30 
    31         }
    32     }
    33     while(!OPTR.IsEmpty())
    34     {
    35         OPND.push(OPTR.pop());
    36     }
    37     while(!OPND.IsEmpty())
    38     {
    39         cout<<OPND.pop();
    40     }
    41 }    
    View Code

    三、入栈出栈问题

      入栈次序1234,则23前面出栈的情况有多少种?

        解:根据树状图将所有弹出的次序找出来,从中找到符合要求的情况

          易得结果共有7种,出栈情况分别为1234124321342143231423412431

    四、约瑟夫问题

     链表实现:

     1     #include<stdio.h>  
     2     #include<malloc.h>  
     3     typedef struct List  
     4     {  
     5         int  data;  
     6         struct List *next;  
     7     }LinkList;  
     8     int main()  
     9     {  
    10         LinkList *L,*r,*s;  
    11         L = (LinkList *)malloc(sizeof(LinkList));  
    12         r =L;  
    13         int n,i;  
    14         int k;  
    15         scanf("%d%d",&n,&k);  
    16         for(i = 1;i<=n;i++)         
    17         {  
    18             s = (LinkList *)malloc(sizeof(LinkList));  
    19             s->data = i;  
    20             r->next = s;  
    21             r= s;  
    22         }  
    23            
    24         r->next =L->next;                
    25         LinkList *p;  
    26         p = L->next;                      
    27            
    28         while(p->next != p)                            
    29         {  
    30             for(i = 1;i<k-1;i++)  
    31             {  
    32                 p = p->next;                         
    33             }  
    34             p->next = p->next->next;                 
    35             p = p->next;  
    36         }  
    37         printf("%d",p->data);  
    38         return 0;  
    39     }  
    View Code

    数组队列实现:

        #include<stdio.h>  
        int main()  
        {  
            int n, k;  
            scanf("%d%d", &n, &k);  
            int i;  
            int a[1001];  
            int dead = 0;                           
            int num = 0;                         
            for (i = 1; i<=n; i++)               
            {  
                a[i] = i;  
            }  
            for (i = 1;; i++)  
            {  
                if (i > n)  
                {  
                    i = i%n;                      
                }  
          
                if (a[i] > 0)                     
                  num++;  
                  
                if (k == num && dead != n-1)            
                {  
                    num = 0;  
                    a[i] = 0;  
                    dead++;  
                }  
                else if(k == num && dead == n-1) 
                {  
                    printf("%d", a[i]);  
                    break;  
                }  
                      
            }  
            return 0;  
        }  
    View Code

    公式法实现:

     1 #include <iostream>
     2 using namespace std;
     3 const int m = 3;
     4 int main()
     5 {
     6     int n, f = 0;
     7     cin >> n;
     8     for (int i = 1; i <= n; i++) f = (f + m) % i;
     9     cout << f + 1 << endl;
    10 }
    View Code
  • 相关阅读:
    简单的Servlet文档创建
    Web 项目结构与部署发布
    第二章 物理层
    《大数据背景下大学生就业服务的信息化建设研究》随笔
    《高校一卡通体育教学管理系统研究与分析》随笔
    《黔西南州舆情信息管理系统分析与设计》随笔
    《南京市青少年校园足球信息平台设计研究》随笔
    《基于JAVA的体育比赛管理系统的研究与实现》随笔
    《基于web体育运动会管理系统平台的设计与实现》随笔
    《基于UML网络信息管理系统的分析与设计》随笔
  • 原文地址:https://www.cnblogs.com/zyrblog/p/6864371.html
Copyright © 2020-2023  润新知