算法练习-栈和队列(二)

算法练习-栈和队列(二)

1.计算器

链接:https://leetcode.cn/problems/calculator-lcci

1.1 题目

给定一个包含正整数、加(+)、减(-)、乘(*)、除(/)的算数表达式(括号除外),计算其结果。

表达式仅包含非负整数,+, - ,*,/ 四种运算符和空格 。 整数除法仅保留整数部分。

示例 1:

输入: “3+2*2”
输出: 7
示例 2:

输入: " 3/2 "
输出: 1
示例 3:

输入: " 3+5 / 2 "
输出: 5

1.2 题解

创建了两个栈,一个存储运算数,另一个存储运算符

如何进行入栈和出栈,见prior

class Solution {
    
    
    public int calculate(String s) {
    
    
        Stack<Integer> nums = new Stack<>();
        Stack<Character> ops = new Stack<>();
        int i = 0;
        int n = s.length();
        while (i < n) {
    
    
            char c = s.charAt(i);
            if (c == ' ') {
    
    
                i++;
            } else if (isDigit(c)) {
    
    
                int num = 0;
                while (i < n && isDigit(s.charAt(i))) {
    
    
                    num = num * 10 + (s.charAt(i) - '0');
                    i++;
                }
                nums.push(num);
            } else {
    
    
                if (ops.isEmpty() || prior(c, ops.peek())) {
    
    
                    ops.push(c);
                } else {
    
    
                    while (!ops.isEmpty() && !prior(c, ops.peek())) {
    
    
                        fetchAndCal(nums, ops);
                    }
                    ops.push(c);
                }
                i++;
            }
        }

        while(!ops.isEmpty()) {
    
    
            fetchAndCal(nums, ops);
        }

        return nums.pop();
    }

    public boolean prior(char a, char b) {
    
    
        if ((a == '*' || a == '/') && (b == '+' || b == '-')) {
    
    
            return true;
        }
        return false;
    }

    public int cal(char op, int num1, int num2) {
    
    
        switch(op) {
    
    
            case '+' : return num1 + num2;
            case '-' : return num1 - num2;
            case '*' : return num1 * num2;
            case '/' : return num1 / num2;
        }
        return -1;
    }

    public boolean isDigit(char c) {
    
    
        return c >= '0' && c <= '9';
    }

    public void fetchAndCal(Stack<Integer> nums, Stack<Character> ops) {
    
    
        int num2 = nums.pop();
        int num1 = nums.pop();
        char op = ops.pop();
        int res = cal(op, num1, num2);
        nums.push(res);
    }
}



2. 删除字符串中所有相邻重复项

链接:https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string

2.1 题目

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

示例:

输入:“abbaca”
输出:“ca”
解释:
例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

2.2 题解

class Solution {
    
    
    public String removeDuplicates(String s) {
    
    
        Deque<Character> deque = new LinkedList<>();
        int n = 0;
        int num = s.length();
        while (n < num) {
    
    
            char c = s.charAt(n);
            if (deque.isEmpty() || deque.peekLast() != c) {
    
    
                deque.addLast(c);
            } else {
    
    
                deque.pollLast();
            }
            n++;
        }
        StringBuilder sb = new StringBuilder();
        while (!deque.isEmpty()) {
    
    
            sb.append(deque.pollFirst());
        }
        return sb.toString();
    }
}

3 栈的压入、弹出序列

3.1 题目

链接:https://leetcode.cn/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

3.2 题解

按照给定的入栈和出栈的元素进行操作,如果不能正常入栈和出栈,则序列错误

class Solution {
    
    
    public boolean validateStackSequences(int[] pushed, int[] popped) {
    
    
        Stack<Integer> stack = new Stack<>();
        int i = 0;
        int j = 0;
        int k = 0;
        while (k < pushed.length + popped.length) {
    
    
            k++;
            // 如果栈不为空并且出栈序列有元素,栈顶元素等于出栈序列的元素
            // 出栈
            if (!stack.isEmpty() && j < popped.length && stack.peek() == popped[j]) {
    
    
                stack.pop();
                j++;
                continue;
            }
            
            // 入栈
            if (i < pushed.length) {
    
    
                stack.push(pushed[i]);
                i++;
                continue;
            }
            return false;
        }
        return true;
    }
}

4 每日温度

链接:https://leetcode.cn/problems/daily-temperatures

4.1 题目

给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。

示例 1:

输入: temperatures = [73,74,75,71,69,72,76,73]
输出: [1,1,4,2,1,1,0,0]
示例 2:

输入: temperatures = [30,40,50,60]
输出: [1,1,1,0]
示例 3:

输入: temperatures = [30,60,90]
输出: [1,1,0]

4.2 题解

4.2.1 暴力解法(超出时间限制)

class Solution {
    
    
    public int[] dailyTemperatures(int[] temperatures) {
    
    
        int n = temperatures.length;
        int[] result = new int[n];
        for (int i = 0; i < n; ++i) {
    
    
            for (int j = i + 1; j < n; j++) {
    
    
                if (temperatures[j] > temperatures[i]) {
    
    
                    result[i] = j - i;
                    break;
            }
        }
    }
    return result;
}
}

4.2.2单调栈

class Solution {
    
    
    public int[] dailyTemperatures(int[] temperatures) {
    
    
        int n = temperatures.length;
        int result[] = new int[n];
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < n; i++) {
    
    
            while (!stack.isEmpty() && temperatures[stack.peek()] < temperatures[i]) {
    
    
                int idx = stack.peek();
                result[idx] = i - idx;
                stack.pop();
            }
            stack.push(i);
        }
        return result;
    }
}

5 接雨水(hard)

链接:https://leetcode.cn/problems/trapping-rain-water

5.1 题目

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

请添加图片描述

示例 1:

输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:

输入:height = [4,2,0,3,2,5]
输出:9

5.2 题解

5.2.1 暴力解法

每个主子上的承载水量=min(左侧最高柱子lh,右侧最高柱子rh)-这个柱子高度h的总接水量=每个柱子之上的承载水量总和

class Solution {
    
    
    public int trap(int[] height) {
    
    
        int n = height.length;
        int result = 0;
        // 遍历每个柱子,找到左面最高的柱子和右面最高的柱子
        for (int i = 0; i < n - 1; ++i) {
    
    
            int lh = 0;
            for (int j = 0; j < i; j++) {
    
    
                if (height[j] > lh) {
    
    
                    lh = height[j];
                }
            }
            int rh = 0;
            for (int j = i + 1; j < n; j++) {
    
    
                if (height[j] > rh) {
    
    
                    rh = height[j];
                }
            }

            int carry = Math.min(lh, rh) - height[i];
            if (carry < 0) {
    
    
                carry = 0;
            }
            result += carry;
        }
        return result;
    }
}

5.2.2 前缀/后缀统计解法

利用空间换时间

创建两个数组,分别存储当前位置左侧最大高度和右侧最大高度

先循环填充两个数组

class Solution {
    
    
    public int trap(int[] height) {
    
    
        int n = height.length;
        // 前缀max
        int[] lmax = new int[n];
        int max = 0;
        for (int i = 0; i < n; ++i) {
    
    
            lmax[i] = Math.max(max, height[i]);
            max = lmax[i];
        }

        // 后缀max
        int[] rmax = new int[n];
        max = 0;
        for (int i = n - 1; i >= 0; --i) {
    
    
            rmax[i] = Math.max(max, height[i]);
            max = rmax[i];
        }

        int result = 0;
        for (int i = 0; i < n - 1; i++) {
    
    
            result += Math.min(lmax[i], rmax[i]) - height[i];
        }
        return result;
    }
}

5.2.3 单调栈

前面两种方法按照垂直计算,该方法水平计算,一层一层计算存水量

class Solution {
    
    
    public int trap(int[] height) {
    
    
        int n = height.length;
        int result = 0;
        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < n; i++) {
    
    
            if (stack.isEmpty()) {
    
    
                stack.push(i); // 存下标
                continue;
            }
            while (!stack.isEmpty()) {
    
    
                int top = stack.peek();
                if (height[top] >= height[i]) {
    
    
                    stack.push(i);
                    break;
                } else {
    
     // 找到凹槽了
                    top = stack.pop();
                    if (stack.isEmpty()) {
    
    
                        stack.push(i);
                        break;
                    }
                    int left = stack.peek();
                    int h = Math.min(height[left], height[i]) - height[top];
                    int w = i - left - 1;
                    result += h * w;
                }
            }
        }
        return result;
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_62759952/article/details/129205673