当前位置:   article > 正文

栈及其应用_栈混洗是什么

栈混洗是什么

代码来源:《数据结构(c++语言版)(第三版)》,邓俊辉编著,ISBN: 978-7-302-33064-6


栈是典型的后进先出的结构,且只有一段(栈顶)可以操作数据。


主要操作有:

栈支持的操作接口
操作接口功能
size()报告栈的规模
empty()判断是否为空
push(e)将e插至栈顶
pop()删除栈顶对象
top()引用栈顶对象








1、进制转换


在利用短除法进行进制转换时,所得余数是从上向下排列,而输出是由下向上。这时可以使用栈结构。
  1. void convert ( Stack<char>& S, __int64 n, int base ) { //十进制正整数n到base进制的转换(递归版)
  2. static char digit[] //0 < n, 1 < base <= 16,新进制下的数位符号,可视base取值范围适当扩充
  3. = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  4. if ( 0 < n ) { //在尚有余数之前,反复地
  5. S.push ( digit[n % base] ); //逆向记录当前最低位,再
  6. convert ( S, n / base, base ); //通过递归得到所有更高位
  7. }
  8. } //新进制下由高到低的各数位,自顶而下保存于栈S中


2、括号匹配


括号匹配即是找到一串括号,看左右括号是否能够一一对应。对于这种情况,无论是把它分成两部分、还是不断消减,都无法很好的达到效果。

这时考虑栈。当遇到左括号时,入栈,遇到右括号时,出栈。

  1. bool paren ( const char exp[], int lo, int hi ) { //表达式括号匹配检查,可兼顾三种括号
  2. Stack<char> S; //使用栈记录已发现但尚未匹配的左括号
  3. for ( int i = lo; i <= hi; i++ ) /* 逐一检查当前字符 */
  4. switch ( exp[i] ) { //左括号直接进栈;右括号若与栈顶失配,则表达式必不匹配
  5. case '(': case '[': case '{': S.push ( exp[i] ); break;
  6. case ')': if ( ( S.empty() ) || ( '(' != S.pop() ) ) return false; break;
  7. case ']': if ( ( S.empty() ) || ( '[' != S.pop() ) ) return false; break;
  8. case '}': if ( ( S.empty() ) || ( '{' != S.pop() ) ) return false; break;
  9. default: break; //非括号字符一律忽略
  10. }
  11. return S.empty(); //整个表达式扫描过后,栈中若仍残留(左)括号,则不匹配;否则(栈空)匹配
  12. }

3、栈混洗

栈混洗就是将栈A中的元素按某种顺序,压入S中,再由S弹出压入B完成某种排列。

其中,栈混洗的总共排列个数,就是卡特兰数(Catalan number)( 2n )! / ( (n + 1)! * n! )

考虑只含三个元素1 2 3 的栈,易知,它的栈混洗不会是3 1 2,推而广之,在栈中,有三个元素i j k,如果新栈里有k i j的形式,那么它一定不是栈混洗。换而言是,k j i的出现是栈混洗的必要条件。

Knuth证明,这也是其充要条件。


4、中缀表达式求值

对于求解一个复杂的计算式,没办法简单地从左到右计算,借助栈,将待执行的元素堆入栈,遇到优先级低的符号就计算上一个表达式,最后计算全部。

  1. float evaluate ( char* S, char*& RPN ) { //对(已剔除白空格的)表达式S求值,并转换为逆波兰式RPN
  2. Stack<float> opnd; Stack<char> optr; //运算数栈、运算符栈
  3. optr.push ( '\0' ); //尾哨兵'\0'也作为头哨兵首先入栈
  4. while ( !optr.empty() ) { //在运算符栈非空之前,逐个处理表达式中各字符
  5. if ( isdigit ( *S ) ) { //若当前字符为操作数,则
  6. readNumber ( S, opnd ); append ( RPN, opnd.top() ); //读入操作数,并将其接至RPN末尾
  7. } else //若当前字符为运算符,则
  8. switch ( orderBetween ( optr.top(), *S ) ) { //视其与栈顶运算符之间优先级高低分别处理
  9. case '<': //栈顶运算符优先级更低时
  10. optr.push ( *S ); S++; //计算推迟,当前运算符进栈
  11. break;
  12. case '=': //优先级相等(当前运算符为右括号或者尾部哨兵'\0')时
  13. optr.pop(); S++; //脱括号并接收下一个字符
  14. break;
  15. case '>': { //栈顶运算符优先级更高时,可实施相应的计算,并将结果重新入栈
  16. char op = optr.pop(); append ( RPN, op ); //栈顶运算符出栈并续接至RPN末尾
  17. if ( '!' == op ) { //若属于一元运算符
  18. float pOpnd = opnd.pop(); //只需取出一个操作数,并
  19. opnd.push ( calcu ( op, pOpnd ) ); //实施一元计算,结果入栈
  20. } else { //对于其它(二元)运算符
  21. float pOpnd2 = opnd.pop(), pOpnd1 = opnd.pop(); //取出后、前操作数
  22. opnd.push ( calcu ( pOpnd1, op, pOpnd2 ) ); //实施二元计算,结果入栈
  23. }
  24. break;
  25. }
  26. default : exit ( -1 ); //逢语法错误,不做处理直接退出
  27. }//switch
  28. }//while
  29. return opnd.pop(); //弹出并返回最后的计算结果
  30. }

这里设置了两个栈,分别存储数字和字符,而字符通过优先级判断。如果现在的运算符比栈顶运算符高,不执行栈顶运算符,继续压入栈。


5、逆波兰表达式

逆波兰表达式可以方便的计算中缀表达式的值。对于其计算,只需要用括号把所有运算分别包括,然后把运算符移至紧邻的括号右边,最后删去括号即可。


( 0 ! + 1 ) * 2 ^ ( 3 ! + 4 ) - ( 5 ! - 67 - ( 8 + 9 ) )

其逆波兰表达式为

0 ! 1 + 2 3 ! 4 + ^ 5 ! 67 - 8 9 + - - 


6、队列

队列和栈是完全相反的操作,为先进先出规律。只能从一端输入数据,另一端删除数据。


队列ADT支持的操作接口
操作功能
size()报告队列的规模(元素总数)
empty()判断队列是否为空
enqueue(e)将e插入队尾
dequeue()删除队首元素
front()引用队首元素

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/869047
推荐阅读
相关标签
  

闽ICP备14008679号