代码随想录第十天|232.用栈实现队列、225. 用队列实现栈、20. 有效的括号、1047. 删除字符串中的所有相邻重复项

题目:232. 用栈实现队列

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

实现 MyQueue 类:

  • void push(int x):将元素 x 推到队列的末尾。
  • int pop():从队列的开头移除并返回元素。
  • int peek():返回队列开头的元素。
  • boolean empty():如果队列为空,返回 true;否则,返回 false

说明:

  • 你只能使用标准的栈操作 —— 即 push to toppeek/pop from topsizeis empty 操作。
  • 你可以使用 listdeque 模拟一个栈,只要是标准的栈操作即可。

解题思路

使用两个栈 stackInstackOut 来实现队列的 FIFO(先进先出)特性:

  1. push 操作:直接将元素压入 stackIn,保持 stackIn 中的顺序。
  2. pop 操作:当 stackOut 为空时,将 stackIn 中的所有元素依次弹出并压入 stackOut,此时 stackOut 的顶部元素即为队列的前端元素。然后从 stackOut 弹出元素即可。
  3. peek 操作:类似于 pop 操作,首先将 stackIn 的元素移到 stackOut,但此操作不移除元素,只返回队列前端的元素。
  4. empty 操作:当 stackInstackOut 都为空时,队列为空。

反思

  1. 效率优化:原代码在 poppeek 操作中将 stackIn 的元素来回倒入 stackOut,造成不必要的重复操作。优化后,stackOut 只在为空时才从 stackIn 导入元素。
  2. 空间管理:双栈方案利用两个栈倒腾元素,使得队列保持 FIFO 特性。通过判断 stackOut 是否为空,可以减少元素转移次数,提高效率。
  3. 易错点:需要注意 peek 操作后不能将元素移除,否则会导致数据丢失。

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() {
        while(!stackIn.isEmpty()){
            stackOut.push(stackIn.pop());
        }
        int temp = stackOut.pop();
        while(!stackOut.isEmpty()){
            stackIn.push(stackOut.pop());
        }
        return temp;
    }
    
    public int peek() {
        while(!stackIn.isEmpty()){
            stackOut.push(stackIn.pop());
        }
        int temp = stackOut.pop();
        stackIn.push(temp);
        while(!stackOut.isEmpty()){
            stackIn.push(stackOut.pop());
        }
        return temp;
    }
    
    public boolean empty() {
        return stackIn.isEmpty();
    }
}

题目:225. 用队列实现栈

使用队列实现栈的下列操作:

  • push(x) – 将元素 x 入栈。
  • pop() – 移除栈顶元素。
  • top() – 获取栈顶元素。
  • empty() – 返回栈是否为空。

解题思路

为了用队列实现栈的后进先出(LIFO)特性,我们可以使用两个队列来模拟栈的操作:

  1. push:将元素添加到主队列 queue 中。
  2. pop:需要移除栈顶元素,而队列只能从队首移除元素,因此要将 queue 中的前 size-1 个元素移到辅助队列 helperQueue 中,然后队列剩下的唯一元素即为栈顶元素,将其移除并返回。之后将辅助队列与主队列互换以保持状态。
  3. top:获取栈顶元素,逻辑与 pop 类似,但不移除最后的元素。
  4. empty:检查主队列是否为空。

反思

  1. 效率优化:本代码实现了用一个队列来存储元素和一个辅助队列来处理“栈顶”元素的获取。通过在每次 poptop 操作时借助辅助队列,减少了重复操作。
  2. 空间管理:实现了队列的双重互换以模拟栈的 LIFO 行为。
  3. 边界条件:考虑栈为空时的情况,确保 poptop 在队列为空时能正确处理。

代码

class MyStack {
    Deque<Integer> quene;
    int size = 0;
    public MyStack() {
        quene = new ArrayDeque<>();
    }
    
    public void push(int x) {
        size++;
        quene.push(x);
    }
    
    public int pop() {
        int temp = 0;
        for(int i=0;i<size-1;i++){
            temp = quene.pop();
            quene.push(temp);
        }
        size--;
        return quene.pop();
    }
    
    public int top() {
        int temp = 0;
        for(int i=0;i<size;i++){
            temp = quene.pop();
            quene.push(temp);
        }
        return temp;
    }
    
    public boolean empty() {
        if(size>0){
            return false;
        }else {
            return true;
        }
    }
}
class MyStack {
    Queue<Integer> queue1; // 和栈中保持一样元素的队列
    Queue<Integer> queue2; // 辅助队列

    public MyStack() {
        queue1 = new LinkedList<>();
        queue2 = new LinkedList<>();
    }
    
    public void push(int x) {
        queue2.offer(x);
        while(!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        Queue<Integer> queue3 = new LinkedList<>();
        queue3 = queue1;
        queue1 = queue2;
        queue2 = queue3;
    }
    
    public int pop() {
        return queue1.poll();
    }
    
    public int top() {
        return queue1.peek();
    }
    
    public boolean empty() {
        return queue1.isEmpty();
    }
}

题目:20. 有效的括号

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


解题思路

本题的目标是判断括号是否有效配对。可以通过栈的后进先出特性来实现括号的有效匹配:

  1. 栈的使用:将所有左括号的对应右括号压入栈中。当遇到右括号时,检查栈顶元素是否是相匹配的右括号。
  2. 入栈和出栈规则
    • 遍历字符串中的每个字符,遇到左括号时将对应的右括号入栈。
    • 遇到右括号时,检查栈是否为空以及栈顶元素是否匹配:
      • 若栈为空或不匹配,则返回 false
      • 若匹配,则弹出栈顶元素继续匹配。
  3. 结束检查:遍历结束后检查栈是否为空,如果栈为空说明所有括号均匹配,返回 true;否则返回 false

反思

  1. 栈的选择:虽然题目为用队列实现栈,但在这种场景下,栈更适合用来处理成对的符号检查,而队列则适合先进先出(FIFO)场景。
  2. 代码简化:可以在一轮遍历中完成括号匹配的检查,减少不必要的判断逻辑。
  3. 边界条件:对于空栈或多余的右括号情况,应提前返回 false 以提高代码的健壮性。

代码

class Solution {
    public boolean isValid(String s) {
		Deque<Character> sb = new LinkedList<>();
		for(int i=0;i<s.length();i++){
			if(s.charAt(i)=='('){
				sb.push(')');
			} else if (s.charAt(i)=='{') {
				sb.push('}');
			} else if (s.charAt(i)=='[') {
				sb.push(']');
			}else if (s.charAt(i)==')' || s.charAt(i)=='}' || s.charAt(i)==']') {
				if(!sb.isEmpty()){
					if(s.charAt(i)==sb.peek()){
						sb.pop();
					}else {
						return false;
					}
				}
				else {
					return false;
				}
			}
		}
		if(!sb.isEmpty()){
			return false;
		}else {
			return true;
		}
    }
}

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

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

S 上反复执行重复项删除操作,直到无法继续删除。完成所有重复项删除操作后,返回最终的字符串。答案保证唯一。


解题思路

可以利用栈的特性来实现该操作的高效执行:

  1. 使用栈(或双指针法):遍历字符串中的每个字符,使用一个栈来存储字符。
    • 遇到与栈顶相同的字符时,出栈,表示移除相邻的重复项。
    • 遇到不同字符则入栈。
  2. 双指针实现:也可以使用双指针来优化空间,省去栈的空间开销。
    • 用两个指针 fastslowfast 指针遍历整个字符串,slow 维护没有重复项的部分。
    • fast 指向的字符与 slow - 1 相同时,指针 slow 回退;否则将字符放入 slow 位置。

反思

  1. 栈的使用:此题可以通过栈来实现,利用栈的后进先出特性方便处理相邻字符的重复问题。
  2. 双指针优化:使用双指针可以节省空间,因为仅需在原数组上直接操作,提高了效率。
  3. 边界情况:如空字符串、全部重复或无重复项的字符串,都需要确保代码能够正常处理。

代码

class Solution {
    public String removeDuplicates(String s) {
        StringBuffer res = new StringBuffer();
		int right = 0;
		for(int i=0;i<s.length();i++){
			char c = s.charAt(i);
			if(right == 0 || c!=res.charAt(right-1)){
				res.append(c);
				right++;
			}else{
					res.deleteCharAt(right-1);
					right--;
			}
		}
		return res.toString();
    }
}
class Solution {
    public String removeDuplicates(String s) {
		char[] ch = s.toCharArray();
		int fast = 0;
		int slow = 0;
		while(fast<s.length()){
			ch[slow] = ch[fast];
			if(slow>0 && ch[slow]==ch[slow-1]){
				slow--;
			}else {
				slow++;
			}
			fast++;
		}
		return new String(ch,0,slow);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值