代码随想录算法训练营第九天

LeetCode题目

  • 232. 用栈实现队列
  • 225. 用队列实现栈
  • 20. 有效的括号
  • 1047. 删除字符串中的所有相邻重复项
  • 1123. 最深叶节点的最近公共祖先(每日一题)
  • 总结
  • 往期打卡


232. 用栈实现队列

跳转:232. 用栈实现队列

问题:

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

实现 MyQueue 类:

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

思路:

说是只用栈,先进后出后进,就可以先出(相当于翻转栈)
所以需要两个栈,需要取值时将值倒到另一个栈取出
由于另一个栈的出栈顺序就是先进先出,所以可以保持原样而无需倒回,等取空再倒

复杂度:

  • 所有操作都可以看作是 O ( 1 ) O(1) O(1)

代码(批量倾倒):

class MyQueue {
	Stack<Integer> in;
	Stack<Integer> out;
    public MyQueue() {
        in = new Stack<>();
        out = new Stack<>();
    }
    
    public void push(int x) {
        in.push(x);
    }
    private void inToOut(){
        if(!out.isEmpty()) return;
        while(!in.isEmpty()) {
        	out.push(in.pop());
        }
    }
    public int pop() {
    	inToOut();
        return out.pop();
    }
    
    public int peek() {
    	inToOut();
        return out.peek();
    }
    
    public boolean empty() {
        return in.isEmpty()&&out.isEmpty();
    }
}

复杂度:

  • pop和peek操作为 O ( n ) O(n) O(n)

代码(每步恢复栈):

class MyQueue {
	Stack<Integer> in;
	Stack<Integer> out;
    public MyQueue() {
        in = new Stack<>();
        out = new Stack<>();
    }
    
    public void push(int x) {
        in.push(x);
    }
    private void inToOut(){
        while(!in.isEmpty()) {
        	out.push(in.pop());
        }
    }
    private void outToIn(){
        while(!out.isEmpty()) {
        	in.push(out.pop());
        }
    }
    public int pop() {
    	inToOut();
        int n = out.pop();
        outToIn();
        return n;
    }
    
    public int peek() {
    	inToOut();
        int n = out.pop();
        in.push(n);
        outToIn();
        return n;
    }
    
    public boolean empty() {
        return in.isEmpty();
    }
}

225. 用队列实现栈

跳转: 225. 用队列实现栈

问题:

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

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false

思路:

先进先出先进,所以倒到另一个队列中会保持顺序
这里只能用笨方法,就是倒出留最后一个末尾
但是可以加一步优化,就是将自己的头倒到自身尾部,可以看作是一个循环的过程.

复杂度:

  • pop和top时间复杂度为 O ( n ) O(n) O(n)

代码(单队列循环):

class MyStack {

	private Queue<Integer> queue;
    public MyStack() {
        queue = new LinkedList<>();
    }
    
    public void push(int x) {
        queue.add(x);
    }
    private void dump() {
    	int len = queue.size();
    	while(--len>0) {
    		queue.add(queue.poll());
    	}
    }
    public int pop() {
    	dump();
    	return queue.poll();
    }
    
    public int top() {
    	dump();
    	int n = queue.poll();
    	queue.add(n);
    	return n;
    }
    
    public boolean empty() {
        return queue.isEmpty();
    }
}

代码(双队列轮换):

class MyStack {

	private Queue<Integer> queueA;
	private Queue<Integer> queueB;
    public MyStack() {
        queueA = new LinkedList<>();
        queueB = new LinkedList<>();
    }
    
    public void push(int x) {
        if(queueB.isEmpty()) queueA.add(x);
        else queueB.add(x);
    }
    private int dump() {
    	if(queueA.isEmpty()) {
    		while(queueB.size()>1) {
    			queueA.add(queueB.poll());
    		}
    		return 0;
    	}else {
    		while(queueA.size()>1) {
    			queueB.add(queueA.poll());
    		}
    		return 1;
    	}
    }
    public int pop() {
    	if(dump()==1) {
    		return queueA.poll();
    	}
    	else {
    		return queueB.poll();
    	}
    }
    
    public int top() {
    	if(dump()==1) {
    		int n = queueA.poll();
    		queueB.add(n);
    		return n;
    	}
    	else {
    		int n = queueB.poll();
    		queueA.add(n);
    		return n;
    	}
    }
    
    public boolean empty() {
        return queueA.isEmpty()&&queueB.isEmpty();
    }
}

20. 有效的括号

跳转: 20. 有效的括号

问题:

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

有效字符串需满足:

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

思路:

有效情况指左右括号可以不跨过未配对的括号一一配对(不能有孤立的情况)
所以用栈对右括号三种配对情况做分组判断即可

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码:

class Solution {
    public boolean isValid(String s) {
        Stack<Character> st = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char a = s.charAt(i);
            switch (a) {
                case ')':
                    if (st.isEmpty() || st.pop() != '(')
                        return false;
                    break;
                case '}':
                    if (st.isEmpty() || st.pop() != '{')
                        return false;
                    break;
                case ']':
                    if (st.isEmpty() || st.pop() != '[')
                        return false;
                    break;
                default:
                    st.add(a);
            }
        }
        return st.isEmpty();

    }
}

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

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

问题:

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

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

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

思路:

邻接配对相消,能看出用栈
可以直接用栈做最后再翻转成字符串

也可以直接原地(C语言可以直接操作字符串)
用慢指针模拟栈,最后取子数组

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)

代码(双指针原地模拟栈):

class Solution {
    public String removeDuplicates(String s) {
        char[] ch = s.toCharArray();
        int slow=0;
        for(int i=1;i<ch.length;i++){
            if(slow>-1&&ch[slow]==ch[i]){
                slow--;
            }else{
                ch[++slow] = ch[i];
            }
        }
        return new String(ch,0,slow+1);
    }
}

代码(直接用栈):

class Solution {
    public String removeDuplicates(String s) {
        char[] ch = s.toCharArray();
        int slow=0;
        for(int i=1;i<ch.length;i++){
            if(slow>-1&&ch[slow]==ch[i]){
                slow--;
            }else{
                ch[++slow] = ch[i];
            }
        }
        return new String(ch,0,slow+1);
    }
}

1123. 最深叶节点的最近公共祖先(每日一题)

跳转: 1123. 最深叶节点的最近公共祖先

问题:

给你一个有根节点 root 的二叉树,返回它 最深的叶节点的最近公共祖先

回想一下:

  • 叶节点 是二叉树中没有子节点的节点
  • 树的根节点的 深度0,如果某一节点的深度为 d,那它的子节点的深度就是 d+1
  • 如果我们假定 A 是一组节点 S最近公共祖先S 中的每个节点都在以 A 为根节点的子树中,且 A 的深度达到此条件下可能的最大值。

思路:

左子树深度等于右子树,才说明本节点是当前树叶节点的最近公共祖先

本题需要找所有最深叶子节点的公共祖先,所以只需要记录最深时最上方的最近公共祖先即可.

复杂度:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)

代码:

class Solution {
	private int max = 0;
	private TreeNode res;
    public TreeNode lcaDeepestLeaves(TreeNode root) {
        handle(root, 0);
        return res;
    }
    private int handle(TreeNode root,int curDeep) {
    	if(root==null) return curDeep;
    	int a = handle(root.left, curDeep+1);
    	int b = handle(root.right, curDeep+1);
    	if(a==b) {
    		if(max<=a) {
    			max = a;
    			res = root;
    		}
    	}
    	return Math.max(a,b);
    }
}

总结

练习了栈和队列的基本操作push pop topadd poll peek
练习了模拟栈和dfs

往期打卡

代码随想录算法训练营第一天
代码随想录算法训练营第二天
代码随想录算法训练营第三天
代码随想录算法训练营第四天
代码随想录算法训练营周末一
代码随想录算法训练营第五天
代码随想录算法训练营第六天
代码随想录算法训练营第七天
代码随想录算法训练营第八天

你可能感兴趣的:(代码随想录打卡,算法)