力扣刷题之-----栈(共10道题,难度全部是简单)(2021年5月28-30日)

  1. 有效的括号
    难度:简单

给定一个只包括 ‘(’,’)’,’{’,’}’,’[’,’]’ 的字符串 s ,判断字符串是否有效。
有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

示例 1:
输入:s = “()”
输出:true

示例 2:
输入:s = “()[]{}”
输出:true

示例 3:
输入:s = “(]”
输出:false

示例 4:
输入:s = “([)]”
输出:false

示例 5:
输入:s = “{[]}”
输出:true

提示:
1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成

解题思路:
使用map集合以及栈,其中map的作用是先将有效括号对存储起来,然后依次遍历字符串:

(1)当遇到右括号时(来自map当中的key),弹出栈顶的左括号,两者比较,如果栈为空或者两者不相等说明不匹配,如果匹配,则将栈顶元素出栈
(2)当遇到左括号时,将其加入栈中

最后判断栈中的是否为空,如果是有效的括号,一定匹配,最后栈中一定为空

class Solution {
    public boolean isValid(String s) {
    int n=s.length();
    if(n % 2 != 0){
        return false;
    }
  Map<Character,Character> pairs=new HashMap<Character,Character>();
  pairs.put(')','(');
  pairs.put(']','[');
  pairs.put('}','{');
  Deque<Character> stack=new LinkedList<Character>();

for(int i=0;i<n;i++){
    char ch=s.charAt(i);
    if(pairs.containsKey(ch)){
        if(stack.isEmpty() || stack.peek() != pairs.get(ch)){
            return false;
        }
        stack.pop();
    }else{
        stack.push(ch);
    }
}
return stack.isEmpty();

    }
}

2 二叉树的中序遍历(难度:简单)
题目:给定一个二叉树的根节点 root ,返回它的 中序 遍历。

示例 1:
输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:
输入:root = []
输出:[]

示例 3:
输入:root = [1]
输出:[1]

示例 4:
输入:root = [1,2]
输出:[2,1]

示例 5:
输入:root = [1,null,2]
输出:[1,2]

提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100

进阶: 递归算法很简单,你可以通过迭代算法完成吗?

解题思路:
递归方式:中序遍历(左子树–根节点—右子树)
写一个递归函数,然后:传入的左子树节点,将根节点加入结果集中,传入右子树节点

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
    List<Integer> res=new ArrayList<Integer>();
    inorder(root,res);
    return res;
    }
    public void inorder(TreeNode root,List<Integer> res){
        if(root==null){
            return ;
        }
        inorder(root.left,res);
        res.add(root.val);
        inorder(root.right,res);
    }
}

非递归解法:
定义一个栈,将根节点压入栈中,然后判断左子树上的节点,若存在然后将其压入栈中,当不存在时将栈顶元素栈,然后重复。。。

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
   //定义一个栈,将根节点压入栈中,然后判断左子树上的节点,若存在然后将其压入栈中,当不存在时将栈顶元素栈,然后重复
   List<Integer> res=new ArrayList<Integer>();
   Deque<TreeNode> stack=new LinkedList<TreeNode>();

   while(root != null || !stack.isEmpty()){
      while(root != null){ //不停将根节点左子树上的节点加入栈中
          stack.push(root);
          root = root.left;
      }
      root=stack.pop();//弹出没有左子树节点
      res.add(root.val);
      root = root.right;//以当前节点的右子树root继续,,,     
   }
   return res;
    }
}
  1. 二叉树的前序遍历(难度:简单)
    题目:给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:
输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:
输入:root = []
输出:[]

示例 3:
输入:root = [1]
输出:[1]

示例 4:
输入:root = [1,2]
输出:[1,2]

示例 5:
输入:root = [1,null,2]
输出:[1,2]

提示:
树中节点数目在范围 [0, 100] 内
-100 <= Node.val <= 100

递归解法:前序遍历(根节点–左子树节点—右子树节点)
(1)写一个递归函数,先确定出口,当处理的root为null时,直接返回
(2)然后先将root加入结果集中,再递归处理左子树节点,最后递归 处理右子树节点

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
     List<Integer> res = new ArrayList<Integer>();
     Deque<TreeNode> stack =new LinkedList<TreeNode>();
    preorder(root,res);
    return res;

    }
    public void preorder(TreeNode root,List<Integer> res){
        if(root == null){
            return;
        }
        res.add(root.val);
        preorder(root.left,res);
        preorder(root.right,res);
    }
}

非递归解法:
先将根节点加入结果集中,然后将其加入栈中,再去处理左节点,当左子节点为null时,弹出栈顶元素,并处理其右子节点。

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
     List<Integer> res = new ArrayList<Integer>();   
     Deque<TreeNode> stack =new LinkedList<TreeNode>();   
    while(root != null || !stack.isEmpty()){
        while(root != null){
            res.add(root.val);//先将该节点加入结果集中
            stack.push(root); 
            root = root.left;  //处理其左子节点
                
        }//当左子节点为null时,跳出while,去处理栈中节点的右子节点
        root=stack.pop();//当左子树节点为空时,弹出栈顶元素,因为已经将该元素加入结果集中了,直接处理其右子树节点即可
        root=root.right;      
    }
     return res;  
    }
}
  1. 二叉树的后序遍历(难度:简单)
    题目:给定一个二叉树,返回它的 后序 遍历。

示例:
输入: [1,null,2,3]
1

2
/
3
输出: [3,2,1]

递归解法:后序遍历(左节点–右节点–根节点)
(1)写一个递归函数,先确定出口
(2)首先处理根节点的左子树,然后处理根节点的右子树,最后将根节点加入结果集中

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
    List<Integer> res=new ArrayList<Integer>();
    afterorfer(root,res);
    return res;
    }
    public void afterorfer(TreeNode root,List<Integer> res){
        if(root==null){
            return ;
        }
        afterorfer(root.left,res);
        afterorfer(root.right,res);
        res.add(root.val);
    }
}

非递归解法:

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
     List<Integer> res=new ArrayList<Integer>();
     Deque<TreeNode> stack=new LinkedList<TreeNode>();

    TreeNode prev = null;
    while(root != null || !stack.isEmpty()){
        while(root != null){
            stack.push(root);//根节点入栈
            root=root.left;//左子节点入栈
        }
        root = stack.pop();//左边所有子节已入栈,处理栈顶元素
        if(root.right == null || root.right == prev){//如果栈顶元素无右子树右子树已处理
            res.add(root.val); //将其加入结果集中
            prev = root ;     //后序用于判断是否已经处理过右子树
            root = null;
        }else{
           stack.push(root);   //如果栈顶元素的右子树不为空,将其加入栈中
           root = root.right;  //右子节点入栈
        }
    }
     return res;
    }
}

5.最小栈(难度:简单)
题目:设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。
push(x) —— 将元素 x 推入栈中。
pop() —— 删除栈顶的元素。
top() —— 获取栈顶元素。
getMin() —— 检索栈中的最小元素。

示例:
输入:
[“MinStack”,“push”,“push”,“push”,“getMin”,“pop”,“top”,“getMin”]
[[],[-2],[0],[-3],[],[],[],[]]
输出:
[null,null,null,null,-3,null,0,-2]
解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.

解题思路:
初始化一个正常的栈,和一个用于始终存储最小值的栈
push:将值push进正常栈,最小值栈中push的值是:min(最小栈中栈顶元素,要入栈的值)
pop:分别将正常栈和最小值栈的元素使用pop()弹出即可
top:使用peek()获取正常栈中的栈顶元素
getMin:使用peek()获取最小值栈中的栈顶元素

class MinStack {
    /** initialize your data structure here. */
    //使用辅助栈来存储最小值
    Deque<Integer> stack;
    Deque<Integer> minStack;

    public MinStack() {
    stack = new LinkedList<Integer>();
    minStack = new LinkedList<Integer>();
    minStack.push(Integer.MAX_VALUE);
    }
    
    public void push(int val) {
    stack.push(val);
    minStack.push(Math.min(minStack.peek(),val));
    }
    
    public void pop() {
    stack.pop();
    minStack.pop();
    }
    
    public int top() {
    return  stack.peek();
    }
    
    public int getMin() {
   return  minStack.peek();
    }
}
  1. 用队列实现栈(难度:简单)
    题目:请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)。
    实现 MyStack 类:
    void push(int x) 将元素 x 压入栈顶。
    int pop() 移除并返回栈顶元素。
    int top() 返回栈顶元素。
    boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。

示例:
输入:
[“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

class MyStack {
     Deque<Integer> queue1;
     Deque<Integer> queue2;
    /** Initialize your data structure here. */
    //准备两个队列:队列1和队列2,然后分三步来模拟栈
    //第一步:每次将元素先放入队列2中
    //第二步:将队列1中的元素依次出队加入队列2中
    //第三步:交换队列1和队列2
    public MyStack() {
        queue1 =new LinkedList<Integer>();
        queue2=new LinkedList<Integer>();
    }
    public void push(int x) {
        queue2.offer(x);
        while(!queue1.isEmpty()){
            queue2.offer(queue1.poll());
        }
        Deque<Integer> temp=queue1;
        queue1=queue2;
        queue2=temp;
    }

    public int pop() {
    return  queue1.poll();
    }
    public int top() {
     return queue1.peek();
    }
    public boolean empty() {
   return  queue1.isEmpty();
    }
}
  1. 用栈实现队列(难度:简单)
    题目:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

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

push:将元素入栈1即可
pop:判断栈2是否为空,如果为空将栈1中的元素全部出栈,然后压入栈2,这样入栈1的顺序是12345,出栈1的顺序是54321,入栈2的顺序是54321,这样出栈2的顺序即为12345,这样就和入栈1时一致,也就模拟了出队列

peek:与pop()原理一致
empty():判断两个栈是否都是空

class MyQueue {
    Deque<Integer> stack1;
    Deque<Integer> stack2;
    public MyQueue() {
        stack1 = new LinkedList<Integer>();
        stack2 = new LinkedList<Integer>();
    }    
    public void push(int x) {
        stack1.push(x);
    }
    public int pop() {
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
    return stack2.pop();
    }
    public int peek() {
    if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
    return stack2.peek();
    }
    public boolean empty() {
     return stack2.isEmpty() && stack1.isEmpty();
    }
}
  1. 下一个更大元素 I(难度:简单)
    给你两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。
    请你找出 nums1 中每个元素在 nums2 中的下一个比其大的值。nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。

示例 1:
输入: nums1 = [4,1,2], nums2 = [1,3,4,2].
输出: [-1,3,-1]
解释:
对于 num1 中的数字 4 ,你无法在第二个数组中找到下一个更大的数字,因此输出 -1 。
对于 num1 中的数字 1 ,第二个数组中数字1右边的下一个较大数字是 3 。
对于 num1 中的数字 2 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

示例 2:
输入: nums1 = [2,4], nums2 = [1,2,3,4].
输出: [3,-1]
解释:
对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。
对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。

解法1:
根据题意,先在nums2中找到nums[i]所在的位置,记录该位置,在nums2中从记录的位置开始向右查找第一个比nums[i]大的数

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;

        int[] res=new int[len1];
         int k=0;

       for(int i = 0 ; i < len1 ; i++){
           for(int j = 0 ;j < len2 ; j++){
             if(nums1[i] == nums2[j]){
                  k=j;
                  break;
                }
            }
           for( ; k <= len2-1 ; k++){
               if(nums2[k] > nums1[i]){
                    res[i] =  nums2[k];
                    break;
               }
                res[i] = -1;
           }          
       }
       return res;
    }
}

解法2:
将nums2及其右边第一个比它大的数以键值对的形式存储在hashmap中,然后以nums1作为查询条件去hashmap中查找

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;

        int[] res=new int[len1];
        HashMap<Integer,Integer> map=new HashMap<Integer,Integer>();
        for(int i = 0 ; i < len2 ; i++){
            for(int k = i  ; k < len2 ; k++){
                if(nums2[k] > nums2[i]){
                   map.put(nums2[i],nums2[k]);
                   break;
                }
                 map.put(nums2[i],-1); 
            }
                   
        }
       for(int j = 0 ; j < len1 ; j++){
          res[j] = map.get(nums1[j]);
       }
       return res;
    }
}

解法3:
使用栈和hashMap
如果栈为空,则将元素存入栈中(说明当前是第一个元素,或者右边不存在比当前元素更大的值)
当栈不为空时,弹出栈顶元素和当前元素比较,如果栈顶元素(当前元素左边的元素)小于当元素,则将其存入hashMap中

class Solution {
    public int[] nextGreaterElement(int[] nums1, int[] nums2) {
        int len1 = nums1.length;
        int len2 = nums2.length;

        int[] res=new int[len1];
        Deque<Integer> stack=new ArrayDeque<Integer>();
        HashMap<Integer,Integer> map=new HashMap<Integer,Integer>();
        
        for(int i = 0 ;i < len2 ; i++){
            while(!stack.isEmpty() && stack.peekLast() < nums2[i]){
                map.put(stack.removeLast(),nums2[i]);
            }
            stack.addLast(nums2[i]);
        }
        for(int j = 0 ;j < len1 ; j++){
            res[j] = map.getOrDefault(nums1[j],-1); 
        }     
       return res;
    }
}
  1. 棒球比赛(难度:简单)
    题目:你现在是一场采用特殊赛制棒球比赛的记录员。这场比赛由若干回合组成,过去几回合的得分可能会影响以后几回合的得分。比赛开始时,记录是空白的。你会得到一个记录操作的字符串列表 ops,其中 ops[i] 是你需要记录的第 i 项操作,ops 遵循下述规则:
    整数 x - 表示本回合新获得分数 x
    “+” - 表示本回合新获得的得分是前两次得分的总和。题目数据保证记录此操作时前面总是存在两个有效的分数。
    “D” - 表示本回合新获得的得分是前一次得分的两倍。题目数据保证记录此操作时前面总是存在一个有效的分数。
    “C” - 表示前一次得分无效,将其从记录中移除。题目数据保证记录此操作时前面总是存在一个有效的分数。
    请你返回记录中所有得分的总和。

示例 1:
输入:ops = [“5”,“2”,“C”,“D”,"+"]
输出:30
解释:
“5” - 记录加 5 ,记录现在是 [5]
“2” - 记录加 2 ,记录现在是 [5, 2]
“C” - 使前一次得分的记录无效并将其移除,记录现在是 [5].
“D” - 记录加 2 * 5 = 10 ,记录现在是 [5, 10].
“+” - 记录加 5 + 10 = 15 ,记录现在是 [5, 10, 15].
所有得分的总和 5 + 10 + 15 = 30

示例 2:
输入:ops = [“5”,"-2",“4”,“C”,“D”,“9”,"+","+"]
输出:27
解释:
“5” - 记录加 5 ,记录现在是 [5]
“-2” - 记录加 -2 ,记录现在是 [5, -2]
“4” - 记录加 4 ,记录现在是 [5, -2, 4]
“C” - 使前一次得分的记录无效并将其移除,记录现在是 [5, -2]
“D” - 记录加 2 * -2 = -4 ,记录现在是 [5, -2, -4]
“9” - 记录加 9 ,记录现在是 [5, -2, -4, 9]
“+” - 记录加 -4 + 9 = 5 ,记录现在是 [5, -2, -4, 9, 5]
“+” - 记录加 9 + 5 = 14 ,记录现在是 [5, -2, -4, 9, 5, 14]
所有得分的总和 5 + -2 + -4 + 9 + 5 + 14 = 27

示例 3:
输入:ops = [“1”]
输出:1

解题思路:
初始化一个栈
(1)对于数字,直接入栈
(2)对于C,弹出栈顶元素
(3)对于D,弹出栈顶元素后乘以2,再将该值入栈
(4)对于+,先保存弹出的栈顶元素(pop()),然后弹出下一个元素(peek()),之后再将最近弹出的两个值相加,最后将后一个弹出的元素入栈,再将两者的和入栈。
(5)使用循环将栈中的所有元素相加

class Solution {
    public int calPoints(String[] ops) {
    int sum = 0;
    Stack<Integer> stack =new Stack<>();
    for(String op:ops){
     if( op.equals("C") ){
         stack.pop();
     }else if( op.equals("D")){
         stack.push( 2 * stack.peek() );
     }else if( op.equals("+")){
         int top= stack.pop();
         int temp=stack.peek();
         stack.push(top);
         stack.push(top + temp);
     }else{
         stack.push(Integer.parseInt(op));
     }
    }
    while(!stack.isEmpty()){
     sum += stack.pop();
    }
    return sum;   
    }
}
  1. 比较含退格的字符串(难度:简单)
    题目:给定 S 和 T 两个字符串,当它们分别被输入到空白的文本编辑器后,判断二者是否相等,并返回结果。 # 代表退格字符。
    注意:如果对空文本输入退格字符,文本继续为空。

示例 1:
输入:S = “ab#c”, T = “ad#c”
输出:true
解释:S 和 T 都会变成 “ac”。

示例 2:
输入:S = “ab##”, T = “c#d#”
输出:true
解释:S 和 T 都会变成 “”。

示例 3:
输入:S = “a##c”, T = “#a#c”
输出:true
解释:S 和 T 都会变成 “c”。

示例 4:
输入:S = “a#c”, T = “b”
输出:false
解释:S 会变成 “c”,但 T 仍然是 “b”。

解法1:
定义两个栈,分别将字母入栈,遇到#,弹出栈顶元素,,最后比较两个栈的长度是否一致,不一直接返回false,一致再逐个比较是否相等,不相等直接返回false

class Solution {
    public boolean backspaceCompare(String s, String t) {
    Deque<Character> stackS=new LinkedList<Character>();
    Deque<Character> stackT=new LinkedList<Character>();

  for(int i=0;i<s.length();i++){
      if(s.charAt(i) != '#'){
          stackS.push(s.charAt(i));
      }else if(s.charAt(i) == '#' && !stackS.isEmpty()){
          stackS.pop();
      }else if(s.charAt(i) == '#' && stackS.isEmpty()){
          continue;
      }
  }
   for(int j=0;j<t.length();j++){
      if(t.charAt(j) !='#'){
          stackT.push(t.charAt(j));
      }else if(t.charAt(j) == '#' && !stackT.isEmpty()){
          stackT.pop();
      }else if(t.charAt(j) == '#' && stackT.isEmpty()){
          continue;
      }
  }
    while(!stackS.isEmpty() || !stackT.isEmpty()){
   if(stackS.size() != stackT.size()){
       return false;
   }    
        Character ch1=stackS.pop();
        Character ch2=stackT.pop();
        if(ch1 != ch2){
        return false;
        }       
    }
   return true;
    }
}

解法2:
(1)定义两个指针S,T,分别指向字符串的末尾,
(2)定义变量,分别记录两个字符串中的#个数skipS,skipT
(3)先对S从后往前判断,有三种情况:
a:如果是#,将skipS++,并前移一位继续处理
b:如果不是#,且skipS>0,说明有字母可以抵消,skipS–,并前移一位继续处理
c:否则说明S字符串中没有可以抵消的字母了,必须在最后与字符串T进行比较,因此,结束S字符串的循环,进入T字符串的循环
(4)判断字符串S和字符串T经第三步的抵消后(可以间知道:S和T字符串的长度一致),剩余字符串是否相等,不相等直接返回false;如果经过第三步的抵消后,S或T中剩余的字符串长度不一致,直接返回false。

class Solution {
    public boolean backspaceCompare(String s, String t) {
    int i = s.length()-1 , j = t.length()-1;
    int skipS = 0 ,skipT = 0 ;
    while( i >= 0 || j >= 0){
        while(i >= 0){
            if(s.charAt(i) == '#' ){
                skipS++;
                i--;
            }else if(skipS > 0){
                skipS--;
                i--;
            }else{
                break;
            }
        }
        while(j >= 0){
            if(t.charAt(j) == '#'){
               skipT++;
               j--;
            }else if( skipT > 0){
                skipT--;
                j--;
            }else{
                break;
            }
        }
        if( i >= 0 && j >= 0){
            if(s.charAt(i) != t.charAt(j)){
                return false;
            }
        }else{
            if(i >= 0 ||  j >= 0){
                return false;
            }
        }
        i--;
        j--;
    }
   return true;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值