代码随想录

代码随想录第十一天 | LeetCode 20.有效的括号 LeetCode:1047. 删除字符串中的所有相邻重复项 三、LeetCode:150. 逆波兰表达式求值


前言

注意:我的很多思路都会在代码中的注释中显示,因此要注意看代码,当然,前面的文章更会透漏我的思路。主要·是栈的一些应用和细节的处理。

一、LeetCode:20. 有效的括号

题目解析:括号是一一对应的,且它先出现的话,它的另一半就会后出现。这和栈的先进后出非常的相似。因此感觉这道题目是真的令人惊喜的感觉到出的好妙。
其实本题目不是有效括号的就只有三种情况:1.多出一个左括号。2.两个括号不匹配。3.多出一个右括号。因此更可以总结出来,1+3 = 如果括号的个数是奇数的话,那么一定不是有效括号。
因此核心的一个思考:我们怎么去判断是否是匹配项呢,其实匹配最好的判断就是是否相等。所以我们在读入左括号的时候就直接存入它的另一半右括号,等会弹出右括号直接和便利的右括号进行比较,如果相等的话就是匹配的,不相等就是情况二,如果栈不为空,那么就是多出了一个左括号,如果最后栈已经用完,但是我们还没有遍历完的话,那就是多出了一个右括号,只有当遍历的右括号和栈中的右括号同时遍历完并且每个都相等时,才可以说所有的括号都是有效。
代码实现时也有一个很重要的逻辑:我们肯定是用for循环来往栈里存放前一半,后一半就要来判断是否匹配了。1,2,3,三种情况我们要如何判断呢,顺序非常的重要,我们在for循环中遍历的是括号数组的元素,因此,我们遍历完了数组,证明此时右括号已经遍历完了,已经尽力了,数量为零。那就是已经匹配,左边也已经合格,就看栈里的右括号了,他就决定了到底可不可以通过。(其实还有一点需要理解清楚,就是说在for循环里我们也是要判断栈是不是空,这里的操作排除了栈里的小于数组里的数量的情况了,我们在外面还要判断大于的情况。)

一些思路我都会写在代码的注释中

C++代码如下:

class Solution {
public:
    bool isValid(string s) {
        if (s.size() % 2 != 0) return false; // 如果s的长度为奇数,一定不符合要求
        stack<char> st;
        for (int i = 0; i < s.size(); i++) {//先用一层for循环往栈里存入元素,并且注意不是简单存放
            if (s[i] == '(') st.push(')');
            else if (s[i] == '{') st.push('}');
            else if (s[i] == '[') st.push(']');
            // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
            // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
            //这一步很是精妙,简单说:有没有问题,没问题我就接着往下走。有问题直接退出。
            else if (st.empty() || st.top() != s[i]) return false;//在我还在for循环时,你是肯定不可以empty的。
            else st.pop(); // st.top() 与 s[i]相等,栈弹出元素
        }
        // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
        return st.empty();
     }
};

JAVA代码如下

class Solution {
    public boolean isValid(String s) {
        Deque<Character> deque = new LinkedList<>();
        char ch;
        for (int i = 0; i < s.length(); i++) {//一次for循环搞定
            ch = s.charAt(i);
            //碰到左括号,就把相应的右括号入栈
            if (ch == '(') {
                deque.push(')');
            }else if (ch == '{') {
                deque.push('}');
            }else if (ch == '[') {
                deque.push(']');
            } else if (deque.isEmpty() || deque.peek() != ch) {//判断第二三种情况
                return false;
            }else {//如果是右括号判断是否和栈顶元素匹配
                deque.pop();//判断第一种情况。
            }
        }
        //最后判断栈中元素是否匹配
        return deque.isEmpty();
    }
}

二、LeetCode:1047. 删除字符串中的所有相邻重复项

题目解析:一一对应,需要消除,其实这类的问题用栈来讲非常的容易。(不是删除完一个就完事了,是所有的都不可以删除。)在本题中,栈中储存的是已经遍历过的元素。(这一想其实也可以用哈希来写。)
重要思路:两个元素相同的话是两个都删除掉,这里还要谈到的是if else的先后逻辑,我们其实可以说 if(如果是空或者说是不相等的话,我们都要去加入栈)else(其他情况)。也可以说 if(栈不为空并且不相等为一种情况),else(其他情况)。这个归根到底是我们对遍历过程中入栈和出栈的情况的考查。

C++代码:

class Solution {
public:
    string removeDuplicates(string S) {
        stack<char> st;//一个栈
        for (char s : S) {//遍历我们的目标数组。
            if (st.empty() || s != st.top()) {//循环往下的一个条件。再之后->可以添加到栈的元素。
                st.push(s);//加入栈
            } else {// s 与 st.top()相等的情况
                st.pop(); //不需要入栈,反而要弹出。因为我们要加入两个元素。
            }
        }
        string result = "";
        while (!st.empty()) { // 将栈中元素放到result字符串汇总
            result += st.top();
            st.pop();
        }
        reverse (result.begin(), result.end()); // 此时字符串需要反转一下,因为是进入了一次栈嘛~~
        return result;

    }
};

C++代码二如下:

class Solution {
public:
    string removeDuplicates(string S) {
        stack<char> st;
        for (char s : S) {
            if(!st.empty() && st.top() == s ){
                st.pop();
            }else{
                st.push(s);
            }
        }
        string result = "";
        while (!st.empty()) { // 将栈中元素放到result字符串汇总
            result += st.top();
            st.pop();
        }
        reverse (result.begin(), result.end()); // 此时字符串需要反转一下
        return result;

    }
};

JAVA代码如下:

class Solution {
    public String removeDuplicates(String s) {
        char[] cs = s.toCharArray();
        Deque<Character> d = new ArrayDeque<>();
        for (char c : cs) {//循环往下的一个条件。再之后->可以添加到栈的元素。
            if (!d.isEmpty() && d.peekLast().equals(c)) {
                d.pollLast();
            } else {
                d.addLast(c);
            }
        }
        StringBuilder sb = new StringBuilder();
        while (!d.isEmpty()) sb.append(d.pollLast());
        sb.reverse();//因为一次进栈出栈,所以我们要重新反转一下
        return sb.toString();
    }
}

//模拟栈 拿字符串直接作为栈,省去了栈还要转为字符串的操作。
class Solution {
    public String removeDuplicates(String s) {
        // 将 res 当做栈
        StringBuffer res = new StringBuffer();
        // top为 res 的长度
        int top = -1;
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            // 当 top > 0,即栈中有字符时,当前字符如果和栈中字符相等,弹出栈顶字符,同时 top--
            if (top >= 0 && res.charAt(top) == c) {
                res.deleteCharAt(top);
                top--;
            // 否则,将该字符 入栈,同时top++
            } else {
                res.append(c);
                top++;
            }
        }
        return res.toString();
    }
}

// 拓展:双指针
class Solution {
    public String removeDuplicates(String s) {
        char[] ch = s.toCharArray();
        int fast = 0;
        int slow = 0;
        while(fast < s.length()){
            // 直接用fast指针覆盖slow指针的值
            ch[slow] = ch[fast];
            // 遇到前后相同值的,就跳过,即slow指针后退一步,下次循环就可以直接被覆盖掉了
            if(slow > 0 && ch[slow] == ch[slow - 1]){
                slow--;
            }else{
                slow++;
            }
            fast++;
        }
        return new String(ch,0,slow);
    }
}


三、LeetCode:150. 逆波兰表达式求值

题目解析:逆波兰表达式:是一种后缀表达式,所谓后缀就是指运算符写在后面。如 ( 1 + 2 ) * ( 3 + 4 ) 。该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) .
逆波兰表达式主要有以下两个优点:1.去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。2.适合用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。
用栈来实现:遇到数字就入栈,遇到出栈就弹出来运算并且是一次弹出弹出两个元素(因为一个运算符一次只能有前后两个元素)。
总之:栈可以进行已知元素的匹配问题。

C++代码如下

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> st;//一个栈结构
        for (int i = 0; i < tokens.size(); i++) {
            if (tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {//判断有符号就开始弹出并且记录两个元素。
                int num1 = st.top();//取元素1
                st.pop();//弹元素1
                int num2 = st.top();//取元素2
                st.pop();//弹元素2
                //再将结果添加到栈。以方便下一次计算。
                if (tokens[i] == "+") st.push(num2 + num1);
                if (tokens[i] == "-") st.push(num2 - num1);
                if (tokens[i] == "*") st.push(num2 * num1);
                if (tokens[i] == "/") st.push(num2 / num1);
            } else {
                st.push(stoi(tokens[i]));
            }
        }
        int result = st.top();//取出最后的结果
        st.pop(); // 把栈里最后一个元素弹出(其实不弹出也没事)
        return result;
    }
};

JAVA代码如下:(JAVA的遇到符号是直接整体的进行了处理,直降当成一个整体了,没有在设置一个数来代表那个值,直接求出来然后入栈。)

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new LinkedList();//栈结构
        for (String s : tokens) {//循环遍历表达式
            if ("+".equals(s)) {        // leetcode 内置jdk的问题,不能使用==判断字符串是否相等,首先要判断符号,并且取出两个元素进行
                stack.push(stack.pop() + stack.pop());      // 注意 - 和/ 需要特殊处理,
            } else if ("-".equals(s)) {
                stack.push(-stack.pop() + stack.pop());
            } else if ("*".equals(s)) {
                stack.push(stack.pop() * stack.pop());
            } else if ("/".equals(s)) {
                int temp1 = stack.pop();
                int temp2 = stack.pop();
                stack.push(temp2 / temp1);
            } else {
                stack.push(Integer.valueOf(s));
            }
        }
        return stack.pop();
    }
}


总结

今天的内容比较简单,但是还是可以再进行一些优化。主要是栈的一些典型的特性吧,相邻,或者一一匹配的问题还是可以想到用栈的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值