力扣刷题day10|232用栈实现队列、225用队列实现栈、20有效的括号、1047删除字符串中的所有相邻重复项

232.用栈实现队列

力扣题目链接

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty):

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾

  • int pop() 从队列的开头移除并返回元素

  • int peek() 返回队列开头的元素

  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

注意:

  • 只能 使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。

  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。

示例:

输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]

解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false

思路

队列:先进先出

使用栈来模式队列的行为,如果仅仅用一个栈,是一定不行的,所以需要两个栈一个输入栈,一个输出栈,这里要注意输入栈和输出栈的关系。

难点

在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。

最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。

public class MyQueue {

    Stack<Integer> stackIn;
    Stack<Integer> stackOut;

    public MyQueue() {
        stackIn = new Stack<>(); // 负责进栈
        stackOut = new Stack<>(); // 负责出栈
    }

    public void push(int x) {
        stackIn.push(x);
    }

    public int pop() {
        if (stackOut.isEmpty()) {
            while (!stackIn.empty()) {
                stackOut.push(stackIn.peek());
                stackIn.pop();
            }
        }
        return stackOut.pop();
    }

    public int peek() {
        if (stackOut.isEmpty()) {
            while (!stackIn.empty()) {
                stackOut.push(stackIn.peek());
                stackIn.pop();
            }
        }
        return stackOut.peek();
    }

    public boolean empty() {
        return stackIn.isEmpty() && stackOut.isEmpty();
    }
}

/**
 * Your MyQueue object will be instantiated and called as such:
 * MyQueue obj = new MyQueue();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.peek();
 * boolean param_4 = obj.empty();
 */

225. 用队列实现栈

力扣题目链接

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppopempty)。

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。

  • int pop() 移除并返回栈顶元素。

  • int top() 返回栈顶元素。

  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

注意:

  • 你只能使用队列的基本操作 —— 也就是 push to backpeek/pop from frontsizeis empty 这些操作。

  • 你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

  • 你可以假设所有操作都是有效的(例如, 对一个空的栈不会调用 pop 或者 top 操作)。

示例:

输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]

解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False

思路

栈:先进后出

用2个队列:用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。

难点

用1个队列:一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。

public class MyStack {
    // Deque 接口继承了 Queue 接口
    // 所以 Queue 中的 add、poll、peek等效于 Deque 中的 addLast、pollFirst、peekFirst
    Deque<Integer> que1;

    public MyStack() {
        que1 = new ArrayDeque<>();
    }

    public void push(int x) {
        que1.addLast(x);
    }

    public int pop() {
        // 将最后一个元素前面的元素全部取出
        // 队列大小
        int size = que1.size();
        // 最后一个入队的除外
        size--;
        // 将前面的元素全部拿出来重新入队
        while (size-- > 0) {
            que1.addLast(que1.peekFirst()); // 加到队尾
            que1.pollFirst(); // 队头弹出
        }

        return que1.pollFirst(); // 弹出现在的第一个元素
    }

    public int top() {
        return que1.peekLast(); // 返回队尾
    }

    public boolean empty() {
        return que1.isEmpty();
    }
}
/**
 * Your MyStack object will be instantiated and called as such:
 * MyStack obj = new MyStack();
 * obj.push(x);
 * int param_2 = obj.pop();
 * int param_3 = obj.top();
 * boolean param_4 = obj.empty();
 */

20. 有效的括号

力扣题目链接

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例1:

输入:s = "()"
输出:true

示例2:

输入:s = "()[]{}"
输出:true

示例3:

输入:s = "(]"
输出:false

思路

  • 遍历到左括号时将相匹配的右括号放入栈中(方便匹配)
  • 遍历到右括号时判断是否与栈顶元素相等
难点1:剪枝操作
  • 剪枝操作:如果字符串长度为奇数,那么括号是肯定不会匹配的,可以直接返回false
难点2:三种不匹配的情况
  1. 字符串里左方向的括号多余了,所以不匹配。

image-20221003203622340

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

  1. 括号没有多余,但是括号的类型没有匹配上。

image-20221003203630858

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

  1. 字符串里右方向的括号多余了,所以不匹配。

image-20221003203639643

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

那么什么时候说明左括号和右括号全都匹配了呢?就是字符串遍历完之后,栈是空的,就说明全都匹配了。

在判断时需要先判断第三种情况:栈已经为空了(以免对空栈操作),再判断第二种情况:左右括号不匹配

public boolean isValid(String s) {
    Stack<Character> stack = new Stack<>();

    // 剪枝操作
    if (s.length() % 2 == 1) {
        return false;
    }

    // 遍历
    for (int i = 0; i < s.length(); i++) {
        // 碰到左括号,就把相应的右括号入栈
        if (s.charAt(i) == '(') {
            stack.push(')');
        }else if (s.charAt(i) == '{') {
            stack.push('}');
        }else if (s.charAt(i) == '[') {
            stack.push(']');
        }else if (stack.isEmpty() || stack.peek() != s.charAt(i)) {
            // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false
            // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false
            return false;
        }else {
            //如果是右括号和栈顶元素匹配
            stack.pop();
        }
    }
    // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true
    return stack.isEmpty();
}

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

力扣题目链接

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

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

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

示例:

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

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

思路

该题也是匹配问题,可以用堆栈来解决

栈里应该放的是前面已经遍历过的、不重复的元素,在遍历下一个元素的时候。在栈里查看是否和上一个元素相同,相同则将栈顶pop掉

难点

在遍历数组存放进栈时,首先判断栈是否为空或者是否有相同元素,然后再进行push操作。

在最后对字符串的加减时,不能用str += deque.pop(),要用str = deque.pop() + str

方法一:Deque 作为堆栈
public String removeDuplicates(String s) {
// 使用 Deque 作为堆栈
    Deque<Character> que = new ArrayDeque<>();

    // 遍历
    for (int i = 0; i < s.length(); i++) {
        if (que.isEmpty() || que.peek() != s.charAt(i)) {
            que.push(s.charAt(i));
        }else {
            que.pop();
        }
    }
    // 剩下的元素即是不重复的元素
    String str = "";
    while (!que.isEmpty()) {
        str = que.pop() + str;
    }
    return str;
}
方法二:字符串直接作为栈

拿字符串直接作为栈,省去了栈还要转为字符串的操作。

// 拿字符串直接作为栈,省去了栈还要转为字符串的操作
public String removeDuplicates1(String s) {
    // 将 res 当做栈
    StringBuffer res = new StringBuffer();
    // len为 res 的长度
    int len = -1;
    for (int i = 0; i < s.length(); i++) {
        if (len < 0 || res.charAt(len) != s.charAt(i)) {
            res.append(s.charAt(i));
            len++;
        }else {
            res.deleteCharAt(len);
            len--;
        }
    }

    return res.toString();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值