栈的应用

进制转换

关于进制转化问题,可以利用栈的先进后出选择很方便的实现,以二进制为例,将一个十进制数8转化为二进制的,实现思路如下图所示:

整个递归过程就是将要转化的10进制数不断除以2,将余数保存起来,当要转换的数据为0时,递归终止。将所有余数逆序输出,就是转换完的数,根据这一特性,可以将所有余数顺序入栈,逆序出栈。实现代码如下所示:


void convert(Stack& s, int n, int base)
{
    char digit[] = { '0' ,'1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' };
    if (0 < n)
    {
        convert(s, n / base, base);
        s.push(digit[n % base]);
    }
}

括号匹配

利用栈的先进后出规则,可以很方便的实现括号匹配。利用栈实现括号匹配的总体实现思路是先将所有的左括号入栈,然后再依次弹出,判断弹出的元素与入栈的元素是否相等,如果相等,则匹配,否则不匹配,最后,经过一次循环,如果栈中还有元素,说明整个表达式的括号不匹配。具体的实现思路,如下图所示:

根据以上思路,实现代码如下所示:

/*
//判断是否匹配,每次判断栈是否为空,
若栈已经弹出所有元素,则直接返回false

*/
bool brackets_match(const char exp[], int lo, int hi)
{
    Stack S;
    for (int i = lo; i <=hi; i++)
    {
        switch (exp[i])   //左括号入栈
        {
        case '(':
        case '[':
        case '{':
            S.push(exp[i]);
            break;
        case ')':
            if ((S.empty()) || (S.pop() != '(')) 
            {
                return false;
            }
            break;
        case ']':
            if ((S.empty()) || (S.pop() != '['))
            {
                return false;
            }
            break;
        case '}':
            if ((S.empty()) || (S.pop() != '{'))
            {
                return false;
            }
            break;
        }
    }
    return S.empty();  //匹配完之后,栈中还包含括号,则说明括号不匹配
}

逆波兰式

传统的表达式中,运算符位于操作数中间,这种表达方式虽然便于人类理解,但是,对于计算机而言,这样的操作非常不便于计算,计算机利用表达式求值时,通常将传统的自然表达方法转化为后缀表达式,这种后缀表达方式称之为逆波兰式,中缀表达式和后缀表达式的转换方式如下所示:
中缀表达式:(1+2)*3+6
后缀表达式:12+3*6+

需要注意的是表示运算顺序的括号在后缀表达式被去掉了,因为后缀表达式本身包含着运算顺序的信息。

综上,根据逆波兰式的特点,利用栈先进后出的特点,可以很方便地实现中缀表达式转换为逆波兰式的步骤如下:

  1. 定义两个栈是s1,s2,一个用来存放运算符,另一个用来存储操作数和运算符。并默认‘#’是最低优先级的运算符,送入栈S1

  2. 遍历表达式字符串,对于每一个字符,如果是操作数,直接送入栈s1,如果是运算符,需要做一下判断:

  3. 将该运算符与栈s1栈顶的运算符做优先级比较,如果该运算符优先级大于栈顶运算符的优先级,将其压入S1栈中。否则,将s1的栈顶元素弹出,送入s2栈中,直至s1的栈顶运算符低于该运算符的优先级,最后将该运算符压入栈上s1中

  4. 如果遇到括号,如果是‘(’,则直接压入栈中,并且匹配最近的’)‘,并将括号之间的运算符全部从栈s1中弹出,压入栈s2中,最后,再将栈顶元素’(‘舍弃。

遍历整个字符串,重复执行2,3,4步。

最后,根据整个运算的步骤,用C++的实现代码如下所示:

#pragma once
/*
*
*  逆波兰表达式,后缀表达式
*  中缀表达式: (1+2)*3+4
*  后缀表达式:  12+3*4+
* 用栈实现: 步骤:1>定义两个栈,S1和S2,S1用来保存运算符,S2用来保存处理操作
* 
*
*/

//定义两个全局变量,用来接收返回的字符串
int length=0; //记录字符串长度,便于输出
char returnStr[256]; //记录字符串
void RPN(char* express)
{
    Stack s1;
    Stack s2;
      //返回的内容
    char* p = express;
    char c; //临时变量,用于记录栈中弹出的元素
    //length = 0;
    s1.push('#');  //默认#是最低优先级的运算符
    while (*p != '\0')
    {
    
        if(*p=='(')  //遇到(直接存入s1栈中
        {
            s1.push(*p);
        
        }
        else if (*p == ')')  //遇到)匹配最近的(,并将这些元素全部放入栈s2中
        { 
            
            while (s1.top() != '(')
            {
                
                s2.push(s1.pop());
                
            }
            s1.pop();  //记得抛弃入栈的'('符号
        }
        else if (*p >= '0' && *p <= '9') //操作数全部放入栈S2中
        {
            s2.push(*p);
            
            
        }
        else if (*p == '+' || *p == '-') //
        {
            /*
            //如果该运算符优先级小于s1栈顶元素,则将s1
            的栈顶元素弹出,送入s2栈中,直至s1的栈顶运算符低于该运算符的优先级
            */
            
            if (s1.top() == '*' || s1.top()=='/')           //小于s1的栈顶元素                        
            {
                                            //将s1的栈顶元素全部弹出,放入s2中
                                            //直到s1的栈顶运算符小于该运算符
                while (s1.top()!='#' )
                {
                    if (s1.top() == '(')
                    {
                        break;
                    }
                    else
                    {
                        
                        s2.push(s1.pop());
                        
                    }
                    
                }
            }
            s1.push(*p);
            
            
        }
        /*
        如果该运算符大于或者等于S1栈顶的运算符优先级,将其
        压入s1的栈中
        
        
        */
        else if (*p == '*' || *p == '/')
        {
            c = s1.top();
            
            if (c == '(')
            {
                break;
            }
            else
            {
                s1.push(*p);
            }
            
                
            

        }
        

        p++;
    }

    while (s1.top()!='#')
    {
        s2.push(s1.pop());
    }

    
    //栈中逆序,字符串中顺序
    while (!s2.empty())
    {
        returnStr[length++] = s2.pop();
        
        
    }
    

    
}

注意:栈s2中的所有元素都是逆序的,为了输出顺序的表达式,还需要将栈s2中的所有的元素逆序处理。

用逆波兰表达式实现表达式计算

将中缀表达式转换为逆波兰式之后,就可以很容易的实现表达式的计算,整个计算过程的步骤,如下所示:

  • 定义一个栈s,并且遍历每个字符:
    • 当前字符是操作数时直接压入栈s中;
    • 当前字符是运算符时,在栈s中弹出需要相应数量的运算符,利用该运算符进行计算,并将该运算后的结果重新压入栈s中。

综上所述:用C++实现的代码如下所示:

float compute(char* exp,int length)
{
    float a = 0; //临时变量,用来保存临时计算结果
    float b = 0;
    
    Stack s; //定义一个栈用来存储计算结果
    length = length - 1;
    while (length>=0)
    {
        if (exp[length] >= '0' && exp[length] <= '9') {
            s.push((float)(exp[length]-48));


        }
        else if (exp[length] == '+')
        {
            a = (float)s.pop();
            b = (float)s.pop();
            s.push(a + b);
        }
        else if (exp[length] == '-')
        {
            a = (float)s.pop();
            b = (float)s.pop();
            s.push(a - b);
        }
        else if (exp[length] == '*')
        {
            a = (float)s.pop();
            b = (float)s.pop();
            s.push(a * b);
        }
        else if (exp[length] == '/')
        {
            a = (float)s.pop();
            b = (float)s.pop();
            s.push(a / b);
        }
        length--;
    }
    //result = s.top();
    return s.top();
}

注意:在代码实现过程中,字符串中的数字是字符型的,需要将其转换为单浮点型数据,根据ASCII码,将每个字符减去48即可。

你可能感兴趣的:(栈的应用)