【数据结构】栈的经典应用

点击查看关于栈的底层实现以及常用方法

由于栈先进后出的特性,所以很多问题借助栈来处理会变得简单很多。

1.括号匹配问题

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串,判断字符串是否有效。

注意:
1.左括号必须用相同类型的右括号闭合;
2.左括号必须以正确的顺序闭合;
3.注意空字符串可被认为是有效字符串。

思路:借助栈来完成,遍历字符串。遇见左括号便将其入栈,遇见右括号便弹出一个栈顶的元素与其进行匹配,栈为空或者括号不匹配直接返回false。遍历完后,如果栈非空,则说明左括号比右括号多,也返回false。

class Solution {
    public boolean isValid(String s) {
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            if(ch == '(' || ch == '[' || ch == '{') {
                stack.push(ch); 
            }else {
                if(stack.empty()) {  
                    return false;   //遇见右括号,栈空,返回false
                }
                char stackTop = stack.peek();  
                if((ch == ')' && stackTop == '(' )|| (ch == ']' && stackTop == '[') || (ch == '}' && stackTop == '{' ) ) {
                    stack.pop();   //括号匹配,弹出栈顶元素
                }else {
                    return false;  //括号不匹配,返回false
                }
            }
        }
        if(!stack.empty()) {
            return false;     //左括号比右括号多,返回false
        }
        return true;
    }
}

2.逆波兰表达式求值

根据逆波兰表示法,求表达式的值。有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

说明:
1.整数除法只保留整数部分;
2.给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

题目链接:逆波兰表达式求值

思路:
关于逆波兰表达式也叫后缀表达式,即将运算符写在操作数之后。
平常我们写的a+b,这种是中缀表达式,写成后缀表达式就是:ab+。再比如:(2 + 1) * 3写成逆波兰表达式就是:2 1 + 3 *;4 + (13 / 5)写成逆波兰表达式就是:4 13 5 / +。

为什么要将看似简单的中序表达式转换为复杂的逆波兰式呢?
原因就在于这个简单是相对人类的思维结构来说的,对计算机而言中序表达式是非常复杂的结构。相对的,逆波兰式在计算机看来却是比较简单易懂的结构。因为计算机普遍采用的内存结构是栈式结构,它执行先进后出的顺序。

有了这些了解,思路便明了了:
遇到数字便压入栈中,遇到运算符便取出栈顶两个元素:第一个放在运算符右边,第二个放在左边。然后进行运算,并将结果继续压入栈中。

class Solution {
    public int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        int left,right;
        for (int i = 0; i < tokens.length; i++) {
            String s = tokens[i];
            switch (s) {
            	//遇到运算符便弹出栈顶两个元素,分别放在运算符右左两侧进行运算
            	//然后将计算结果重新入栈
                case "+":
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left+right);
                    break;
                case "-":
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left-right);
                    break;
                case "*":
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left*right);
                    break;
                case "/":
                    right = stack.pop();
                    left = stack.pop();
                    stack.push(left/right);
                    break;
                    default:
                     	//遇到数字便压入栈中
                        stack.push(Integer.valueOf(s));
                        break;

            }
        }
        return stack.peek();
    }
}

3.用栈将递归转为循环

  • 为什么要将递归转化为循环?
    因为再递归调用次数非常多的时候,时间开销非常大,而且容易造成栈溢出。使用循环就可以避免这些问题。

  • 为什么可以使用栈将递归转化为循环?
    因为在使用递归的时候先调用的后结束,后调用的先结束。正好与栈先进后出的特性相匹配。还有就是有些递归直接转化为循环可能很难,这时候就可以转化为栈。

比如逆置打印一个链表:
1.递归法

public static void diaplay(Node head) {
	if(head.next == null) {
		System.out.println(head.data); 
	}
	diaplay(head.next);
}

2.使用栈转为循环

public static void display(Node head) {
        Stack<Node> stack = new Stack<>();
        Node cur = head;
        while (cur != null) {
            stack.push(cur);        //进栈
            cur = cur.next;
        }
        while (!stack.empty()) {
            Node top = stack.pop();        //出栈
            System.out.println(top.data);
        }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值