LeetCode 栈问题总结 + Java实现

文章目录

    • 栈特点
    • Java Stack
    • 最小栈
    • 单调递减栈
    • 二叉树中序遍历——栈解法

栈特点

  1. LIFO,Last In First Out
  2. 我们只能操纵栈顶元素,即pop和push方法的对象;
  3. 使用场景:
    • 逆序输出
    • 语法检查(符号成对出现)
    • 函数栈
    • 数学计算
  4. 用法小结:
    • 栈的核心是栈顶元素,一定先想好入栈的是什么元素,往往将中间过程的结果压栈,出栈即真正使用他们的时候;
    • 入栈可以快速入栈,但是出栈一定要一步一步出,每次出栈的元素一般都是需要使用的;
    • 栈是否为空常作为循环的条件,所以记住循环前以及循环即将结束是否需要压栈,以继续循环。
  5. 栈与递归
    • 递归算法可以用数据栈来进行非递归化
    • 借助数据栈而实现的非递归算法,理论上也可以被递归化
    • 也就是说,两者是可逆的,桥梁就是栈

Java Stack

  1. 仅有无参构造函数;
  2. 其他函数:
    • push(num)
    • pop()
    • empty() 或者 isempty() // 判断栈是否空, 返回值true:空;false:非空
    • peek() // 获取栈顶元素
    • search(num) // 判断num是否在栈中,返回值1:在;-1:不在

最小栈

对应题目:Leetcode155

  1. 最小栈不是全新的一个栈,而是一个额外的辅助栈

  2. 在栈中寻找最小值时间代价是O(n),所以增加一个辅助栈专门存最小值,将寻找最小值时间代价降为常数;

  3. 开始我的想法不是增加辅助栈,而是自己建一个数组做栈,再设置一个索引,专门用来存储最小值的索引,push()还好,但是pop()时要重新寻找最小值索引,代价为O(n);

  4. 后来在官方提示下,使用辅助栈,这也是最小栈的含义;

  5. 在原栈push时,最小栈也push,但要比较最小栈栈顶元素和push的元素,谁小就push谁入最小栈,最小栈栈顶总是存储着第i次push时的全栈最小元素(P.S. 由于第一次push时,最小栈栈顶没有元素,所以在生成最小栈时就push一个绝对大的数,保证第一个元素总是可以进入最小栈的,Java可以用Integer.MAX_VALUE);

  6. 在原栈pop时,最小栈跟着pop就可以;

  7. top()查询原栈栈顶,getMin()查询最小栈栈顶;

  8. 总结:以空间换时间,多加一个最小栈/辅助栈,所有操作保证常数时间内完成。

     import java.util.Stack;
     
     class MinStack {
         private Stack stack;
         private Stack minStack;
         /** initialize your data structure here. */
         public MinStack() {
             stack = new Stack();
             minStack = new Stack();
             minStack.push(Integer.MAX_VALUE);
         }
         
         public void push(int x) {
             stack.push(x);
             if(x

单调递减栈

对应题目:LeetCode42

  1. 如果不是按照标签写的话,我可能想不到用栈;

  2. 这里用到递减栈,其实也不是什么特别的栈,就是栈内元素大小按照push顺序递减;

  3. 因为短板效应,所以只存递减的;

  4. 按照柱子高度的数组height索引检索,高度比前一个低的,我们压栈,因为当前柱子上方可以存水,但是还要取决于后面的柱子;

  5. 遇到比栈顶高的柱子,说明栈内n个柱子可以存水了,只要栈内低于当前柱子的都要存水;

  6. 存多少是关键,我们一步步存,也就是先比较栈顶柱子的前一个栈内柱子与当前柱子,谁低谁决定水量,二者低的减去栈顶柱子高度,是可以上升的水高,水宽呢?应当是当前柱子-栈顶柱子的前一个栈内柱子-1,因为栈内的柱子不一定相连,但是可以肯定的是,我们压栈前算过水量,也就是栈内柱子间的水被填平了!!

  7. 当然上述循环依次进行到栈顶柱子比当前柱子高,或者栈空了;

  8. 当前柱子最后要压栈,因为它关系到后面柱子的水量;

  9. 遍历完所有柱子,栈非空也没事,因为能存进栈的都是递减的柱子,存不住水。

     import java.util.Stack;
     
     /**
      * Solution42
      */
     public class Solution {
     
         public int trap(int[] height) {
             int rain = 0;
             // 单调递减栈,按照push进去的顺序柱子高度依次递减,
             // 中间push一个元素大于前面已有n个元素时,push前先把这些pop掉
             Stack sk = new Stack();
             for(int i=0; iheight[i]?height[i]:preHgt)-height[j]);
                     }
                 }
                 // 入栈前先pop的都是比height[i]小的,pop它们相当于灌水,把低柱子填成平地了
                 sk.push(i);
             }
             return rain;
         }
     }
    

二叉树中序遍历——栈解法

对应题目:LeetCode94

  1. 可以使用递归(栈一般可以和递归解法互相转换)和莫里斯遍历(线索树),官方解释

  2. 这里只介绍栈解法:

    • 为什么用栈?
      • 用栈无非是符合LIFO,适合先存后输出;
      • 树结构决定我们可以由父节点找到子节点,但是反过来不可以,所以我们使用栈的好处就是可以在父节点向下遍历时,将父节点存起来;
    • 增么用栈?
      • 反向压栈,即利用LIFO特点;
      • 我们先在脑子里想像我们在只有root结点时,第一个输出的应该时最左节点,再遍历它的父节点,接下来是父节点的右子树,如果有的话,那么右子树的根节点是不是就是新的root(有点递归的意思了);
      • 所以我们要先从root,顺着左节点全部压栈,再输出,每pop出来一个就要判断其有无右子树;
      • 有右子树,就要将当前结点设成右子树的根节点,步骤转上一步,可以使用循环实现;
      • 循环结束条件是栈空。
  3. 代码

     import java.util.LinkedList;
     import java.util.List;
     import java.util.Stack;
     
     public class Solution94_2 {
         public List inorderTraversal(TreeNode root) {
             // 特殊情况
             if(root==null){
                 return new LinkedList();
             }
             // 存储遍历结果的链表
             List tra = new LinkedList();
             // 结点压栈
             Stack sk = new Stack();
             // 当前节点
             TreeNode cur = root;
             sk.push(cur); //先push进去一个,防止空栈不能进入循环
             while(!sk.isEmpty()){
                 // 先压栈,顺着左边结点一直压到叶子节点
                 while(cur.left != null){
                     cur = cur.left;
                     sk.push(cur);
                 }
                 // 用do while,保证每次大循环至少pop一个
                 // pop时每pop一个就要查看pop出的有无右节点
                 // pop一次对应遍历时的读取一次
                 // 注意空栈无法pop
                 do{
                     cur = sk.pop();
                     tra.add(cur.val);
                 }while (cur.right==null && !sk.isEmpty());
                 // 有可能是由于栈空了结束上面的循环
                 // 空栈才结束,说明没有右结点了
                 if(cur.right != null){
                      cur = cur.right;
                     sk.push(cur);
                 }
             }
             return tra;
         }
     }
    

你可能感兴趣的:(LeetCode,leetcode,栈,java)