sisterAn / JavaScript-Algorithms

基础理论+JS框架应用+实践,从0到1构建整个前端算法体系

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

字节&leetcode155:最小栈(包含getMin函数的栈)

sisterAn opened this issue · comments

设计一个支持 pushpoptop 操作,并能在常数时间内检索到最小元素的栈。

  • push(x) —— 将元素 x 推入栈中。
  • pop() —— 删除栈顶的元素。
  • top() —— 获取栈顶元素。
  • getMin() —— 检索栈中的最小元素。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

附leetcode地址:leetcode

commented
class MinStack {
  constructor () {
    this.length = 0;
    this.content = [];
    this.mins = [];
  }
  push (val) {
    const curMin = this.mins[this.length - 1] !== undefined ? this.mins[this.length - 1] : Infinity;
    this.content[this.length++] = val;
    this.mins[this.length - 1] = Math.min(curMin, val);
  }
  pop () {
    return this.content[--this.length]
  }
  top () {
    return this.content[this.length - 1]
  }
  getMin () {
    return this.mins[this.length - 1];
  }
}
var MinStack = function() {
  this.stack = [];
  this.min = Number.MAX_SAFE_INTEGER;
};


MinStack.prototype.push = function(x) {
  if(this.min >= x){
      this.stack.push(x);
      this.min = x;
  } else {
    this.stack.push(x)
  }
};

MinStack.prototype.pop = function() {
  const min = this.stack.pop()
  if(min == this.min){
    this.min = Math.min(...this.stack)
  }
};

MinStack.prototype.top = function() {
  return this.stack[this.stack.length-1];
};

MinStack.prototype.getMin = function() {
  return this.min;
};

在常数时间内检索到最小元素的栈,即仅需保证 getMin 的时间复杂度为 O(1) 即可

var MinStack = function() {
    this.items = []
    this.min = null
};

// 进栈
MinStack.prototype.push = function(x) {
    if(!this.items.length) this.min = x 
    this.min = Math.min(x, this.min)
    this.items.push(x) 
};

// 出栈
MinStack.prototype.pop = function() {
    let num = this.items.pop() 
    this.min = Math.min(...this.items)
    return num
};

// 获取栈顶元素
MinStack.prototype.top = function() {
    if(!this.items.length) return null
    return this.items[this.items.length -1] 
};

// 检索栈中的最小元素
MinStack.prototype.getMin = function() {
    return this.min
};

时间复杂度:进栈O(1),出栈O(n),获取栈顶元素O(1),获取最小元素O(1)

空间复杂度:O(n)

leetcode

随便看了下 😂
@chenzesam 你没有考虑 push(0) 的情况。
@sisterAn 出栈也可以做到 O(1) 的,空间复杂度不变,见上面的那个老哥
如果 n 很大而且空间要求很严格的话,可以启用 mins 数组的压缩,不过感觉得不偿失

commented

@XGHeaven 可以举个 test case 吗?

commented

@chenzesam pop感觉不好使吧,只是改变length,但数组没有去除

commented

@Mr-jili 只是将栈的功能实现了, 不然使用数组的 pop 和 push, 那就不需要实现栈了. length 只是用来划的

@chenzesam 直接实例化之后跑这个
image

class MinStack {
    constructor() {
      this.min = undefined // 存放最小值
      this.secondMin = undefined // 存放第二小值
      this.stack = []
    }
    push(item) {
      this.stack.push(item)
      if (this.min === undefined) {
        this.min = item
      }
      if (item < this.min) {
        this.secondMin = this.min
        this.min = item
      }
    }
    pop() {
      const popItem = this.stack.pop()
      if (popItem === this.min) {
        this.min = this.secondMin
      }
      return popItem
    }
    top() {
      if (!this.stack.length) return null
      return this.stack[this.stack.length - 1]
    }
    getMin() {
      if (!this.stack.length) return null
      console.log('min:', this.min)
      return this.min
    }
  }

  const minStack = new MinStack();
  minStack.push(-2);
  minStack.push(0);
  minStack.push(-3);
  minStack.getMin();   // --> 返回 -3.
  minStack.pop();
  minStack.top();      // --> 返回 0.
  minStack.getMin();   // --> 返回 -2.

@chenzesam 直接实例化之后跑这个
image

push 没有return 一个返回值,就相当于return undefined

function MinStack() {
  let list = [];
  let min = null;
  this.push = val => {
    if (typeof val === 'number' && !Number.isNaN(val)) {
      list.push(val);
      min = min === null ? val : Math.min(min, val);
    } else if (Array.isArray(val)) {
      list.push(...val);
      min = min === null ? Math.min(...val) : Math.min(...[min, ...val]);
    }
    return list.length;
  };
  this.pop = () => {
    const last = list.pop();
    if (last === min) {
      min = Math.min(...list);
    }
    return last;
  };
  this.top = () => {
    return list[list.length - 1];
  };
  this.getMin = () => {
    return min;
  };
}
commented

关键点应该是常数时间内找到最小值,我就将最小值定义一个变量。
在添加和移除元素的时候 都会重置最小值

function MinStack() {
  let items = []
  let min
  this.push = function(item) {
    min = min && item > min ? min : item
    return items.push(item)
  }
  this.pop = function(){
    let item = items.pop()
    min = item > min ? min : Math.min(...items)
    return item
  }
  this.getMin = function() {
    return min
  }
  this.top = function(){
    return items[items.length - 1 ] 
  }
}
var MinStack = function() {
  this.stack = []
  this.minStack = []
};

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
  this.stack.push(x)

  if (!this.minStack.length || x <= this.minStack[ this.minStack.length - 1 ]) {
    this.minStack.push(x)
  }
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
  const delEle = this.stack.pop()

  if (delEle === this.minStack[ this.minStack.length - 1 ]) {
    this.minStack.pop()
  }
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
  return this.stack[ this.stack.length - 1 ]
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function() {
  return this.minStack[ this.minStack.length - 1 ]
};
// 用两个栈,一个用于存储所有原始数据,一个用于存储每组数据的最小值
/**
 * initialize your data structure here.
 */
var MinStack = function() {
  this.stack = []
  this.minStack = [Infinity]
};

/**
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
  this.stack.push(x)
  const minVal = Math.min(this.minStack[this.minStack.length - 1], x)
  this.minStack.push(minVal)
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
  this.stack.pop()
  this.minStack.pop()
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
  return this.stack[this.stack.length - 1]
};

/**
 * @return {number}
 */
MinStack.prototype.getMin = function() {
  return this.minStack[this.minStack.length - 1]
};
class MinStack {
  constructor() {
    this.min = Infinity;
    // Each element is also an array [currentValue,currentMin]
    this.elements = [];
  }
  get top() {
    return this.elements[this.size - 1][0];
  }
  get size() {
    return this.elements.length;
  }
  push(el) {
    if (this.min > el) {
      this.min = el;
      this.elements.push([el, el]);
    } else {
      this.elements.push([el, this.min]);
    }
  }
  pop() {
    const el = this.elements.pop();
    return el[0];
  }
  getMin() {
    return this.elements[this.size - 1][1];
  }
}

题目地址(155. 最小栈)

https://leetcode-cn.com/problems/min-stack/

题目描述

设计一个支持 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.
 

提示:

pop、top 和 getMin 操作总是在 非空栈 上调用。

前置知识

公司

  • amazon
  • bloomberg
  • google
  • snapchat
  • uber
  • zenefits
  • 阿里
  • 腾讯
  • 百度
  • 字节

两个栈

思路

我们使用两个栈:

  • 一个栈存放全部的元素,push,pop都是正常操作这个正常栈。
  • 另一个存放最小栈。 每次push,如果比最小栈的栈顶还小,我们就push进最小栈,否则不操作
  • 每次pop的时候,我们都判断其是否和最小栈栈顶元素相同,如果相同,那么我们pop掉最小栈的栈顶元素即可

关键点

  • 往minstack中 push的判断条件。 应该是stack为空或者x小于等于minstack栈顶元素

代码

  • 语言支持:JS,C++,Java,Python

JavaScript Code:

/**
 * initialize your data structure here.
 */
var MinStack = function() {
    this.stack = []
    this.minStack = []
};

/** 
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
    this.stack.push(x)
    if (this.minStack.length == 0 ||  x <= this.minStack[this.minStack.length - 1]) {
        this.minStack.push(x)
    }
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
    const x = this.stack.pop()
    if (x !== void 0 &&  x === this.minStack[this.minStack.length - 1]) {
        this.minStack.pop()
    }
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
    return this.stack[this.stack.length - 1]
};

/**
 * @return {number}
 */
MinStack.prototype.min = function() {
    return this.minStack[this.minStack.length - 1]
};

/** 
 * Your MinStack object will be instantiated and called as such:
 * var obj = new MinStack()
 * obj.push(x)
 * obj.pop()
 * var param_3 = obj.top()
 * var param_4 = obj.min()
 */

C++ Code:

class MinStack {
    stack<int> data;
    stack<int> helper;
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        data.push(x);
        if(helper.empty() || helper.top() >= x)
        {
            helper.push(x);
        }
        
    }
    
    void pop() {
        int top = data.top();
        data.pop();
        if(top == helper.top())
        {
            helper.pop();
        }
        
    }
    
    int top() {
        return data.top();
    }
    
    int getMin() {
        return helper.top();
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

Java Code:

public class MinStack {

    // 数据栈
    private Stack<Integer> data;
    // 辅助栈
    private Stack<Integer> helper;

    /**
     * initialize your data structure here.
     */
    public MinStack() {
        data = new Stack<>();
        helper = new Stack<>();
    }
    
    public void push(int x) {
        // 辅助栈在必要的时候才增加
        data.add(x);
        if (helper.isEmpty() || helper.peek() >= x) {
            helper.add(x);
        }
    }

    public void pop() {
        // 关键 3:data 一定得 pop()
        if (!data.isEmpty()) {
            // 注意:声明成 int 类型,这里完成了自动拆箱,从 Integer 转成了 int,
            // 因此下面的比较可以使用 "==" 运算符
            int top = data.pop();
            if(top == helper.peek()){
                helper.pop();
            }
        }
    }

    public int top() {
        if(!data.isEmpty()){
            return data.peek();
        }
    }

    public int getMin() {
        if(!helper.isEmpty()){
            return helper.peek();
        }
    }
}

Python3 Code:

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.minstack = []

    def push(self, x: int) -> None:
        self.stack.append(x)
        if not self.minstack or x <= self.minstack[-1]:
            self.minstack.append(x)

    def pop(self) -> None:
        tmp = self.stack.pop()
        if tmp == self.minstack[-1]:
            self.minstack.pop()

    def top(self) -> int:
        return self.stack[-1]

    def min(self) -> int:
        return self.minstack[-1]


# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()

复杂度分析

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

一个栈

思路

符合直觉的方法是,每次对栈进行修改操作(push和pop)的时候更新最小值。 然后getMin只需要返回我们计算的最小值即可,
top也是直接返回栈顶元素即可。 这种做法每次修改栈都需要更新最小值,因此时间复杂度是O(n).

是否有更高效的算法呢?答案是有的。

我们每次入栈的时候,保存的不再是真正的数字,而是它与当前最小值的差(当前元素没有入栈的时候的最小值)。
这样我们pop和top的时候拿到栈顶元素再加上上一个最小值即可。
另外我们在push和pop的时候去更新min,这样getMin的时候就简单了,直接返回min。

注意上面加粗的“上一个”,不是“当前的最小值”

经过上面的分析,问题的关键转化为“如何求得上一个最小值”,解决这个的关键点在于利用min。

pop或者top的时候:

  • 如果栈顶元素小于0,说明栈顶是当前最小的元素,它出栈会对min造成影响,我们需要去更新min。
    上一个最小的是“min - 栈顶元素”,我们需要将上一个最小值更新为当前的最小值

因为栈顶元素入栈的时候的通过 栈顶元素 = 真实值 - 上一个最小的元素 得到的,
而真实值 = min, 因此可以得出上一个最小的元素 = 真实值 -栈顶元素

  • 如果栈顶元素大于0,说明它对最小值没有影响,上一个最小值就是上上个最小值。


关键点

  • 最小栈存储的不应该是真实值,而是真实值和min的差值
  • top的时候涉及到对数据的还原,这里千万注意是上一个最小值

代码

  • 语言支持:JS,C++,Java,Python

Javascript Code:

/*
 * @lc app=leetcode id=155 lang=javascript
 *
 * [155] Min Stack
 */
/**
 * initialize your data structure here.
 */
var MinStack = function() {
  this.stack = [];
  this.minV = Number.MAX_VALUE;
};

/**
 * @param {number} x
 * @return {void}
 */
MinStack.prototype.push = function(x) {
  // update 'min'
  const minV = this.minV;
  if (x < this.minV) {
    this.minV = x;
  }
  return this.stack.push(x - minV);
};

/**
 * @return {void}
 */
MinStack.prototype.pop = function() {
  const item = this.stack.pop();
  const minV = this.minV;

  if (item < 0) {
    this.minV = minV - item;
    return minV;
  }
  return item + minV;
};

/**
 * @return {number}
 */
MinStack.prototype.top = function() {
  const item = this.stack[this.stack.length - 1];
  const minV = this.minV;

  if (item < 0) {
    return minV;
  }
  return item + minV;
};

/**
 * @return {number}
 */
MinStack.prototype.min = function() {
  return this.minV;
};

/**
 * Your MinStack object will be instantiated and called as such:
 * var obj = new MinStack()
 * obj.push(x)
 * obj.pop()
 * var param_3 = obj.top()
 * var param_4 = obj.min()
 */

C++ Code:

class MinStack {
    stack<long> data;
    long min = INT_MAX;
public:
    /** initialize your data structure here. */
    MinStack() {
        
    }
    
    void push(int x) {
        data.push(x - min);
        if(x < min)
        {
            min = x;
        }
        
    }
    
    void pop() {
        long top = data.top();
        data.pop();
        // 更新最小值
        if(top < 0)
        {
            min -= top;
        }
        
    }
    
    int top() {
        long top = data.top();
        // 最小值为 min
        if (top < 0)
        {
            return min;
        }
        else{
            return min+top;
        }
    }
    
    int getMin() {
        return min;
    }
};

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack* obj = new MinStack();
 * obj->push(x);
 * obj->pop();
 * int param_3 = obj->top();
 * int param_4 = obj->getMin();
 */

Java Code:

class MinStack {
    long min;
    Stack<Long> stack;
    
    /** initialize your data structure here. */
    public MinStack() {
        stack = new Stack<>();
    }
    
    public void push(int x) {
        if (stack.isEmpty()) {
            stack.push(0L);
            min = x;
        }
        else {
            stack.push(x - min);
            if (x < min)
                min = x;
        }
    }
    
    public void pop() {
        long p = stack.pop();
        
        if (p < 0) {
            // if (p < 0), the popped value is the min
            // Recall p is added by this statement: stack.push(x - min);
            // So, p = x - old_min
            // old_min = x - p
            // again, if (p < 0), x is the min so:
            // old_min = min - p
            min = min - p;
        }
    }
    
    public int top() {
        long p = stack.peek();
        
        if (p < 0) {
            return (int) min;
        }
        else {
            // p = x - min
            // x = p + min
            return (int) (p + min);
        }
    }
    
    public int getMin() {
        return (int) min;    
    }
}

Python Code:

class MinStack:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.minV = float('inf')
        self.stack = []

    def push(self, x: int) -> None:
        self.stack.append(x - self.minV)
        if x < self.minV:
            self.minV = x

    def pop(self) -> None:
        if not self.stack:
            return
        tmp = self.stack.pop()
        if tmp < 0:
            self.minV -= tmp

    def top(self) -> int:
        if not self.stack:
            return
        tmp = self.stack[-1]
        if tmp < 0:
            return self.minV
        else:
            return self.minV + tmp

    def min(self) -> int:
        return self.minV



# Your MinStack object will be instantiated and called as such:
# obj = MinStack()
# obj.push(x)
# obj.pop()
# param_3 = obj.top()
# param_4 = obj.min()

复杂度分析

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

更多题解可以访问我的LeetCode题解仓库:https://github.com/azl397985856/leetcode 。 目前已经40K star啦。

关注公众号力扣加加,努力用清晰直白的语言还原解题思路,并且有大量图解,手把手教你识别套路,高效刷题。

class MinStack {

    private _stack: number[] = []
    private min: number | undefined;

    push (data: number) {
        if (this.min === undefined) {
            this.min = data;
        } else {
            this.min = Math.min(data, this.min);
        }
        this._stack.push(data);
    }

    pop () {
        this._stack.pop();
        if (this._stack.length <= 0) {
            this.min = undefined;
        } else {
            this.min = Math.min(...this._stack);
        }
    }

    top () {
        return this._stack[this._stack.length - 1];
    }

    getMin () {
        return this.min;
    }

}
commented

// if push item is a Array

class MinStack {
      constructor() {
          this.min = null;
          this.stack = [];
      }

    push(item) {
        if (Array.isArray(item)) {
            this.stack.push(...item.flat(Infinity));
            this.min === null ? this.min = Math.min(...item.flat(Infinity)) : this.min = Math.min(...[this.min,...item.flat(Infinity)])
        } else {
            this.stack.push(item)
            this.min === null ? this.min = item : this.min = Math.min(this.min, item)
        }
        
       return this.stack.length;
    }

    pop() {
        const _last = this.stack.length ? this.stack.pop() : null;

        if (this.min === _last) {
            this.min = Math.min(...this.stack);
        }

        return _last;
    }

    top() {
        const _top = this.stack.length ? this.stack[this.stack.length - 1] : null

        return _top;
    }

    getMin() {
        return this.min
    }
}
commented
class minStack {
    constructor (){
        this.stack = []
    }

    push (e) {
        this.stack.push(e)
    }

    pop () {
        this.stack.pop()
    }
    top () {
        return this.stack[this.stack.length -1]
    }
    getMin () {
        let sort = [...this.stack].sort((a,b) => a-b)
        return sort[0]
    }
}
lass MinStack {
    data: number[]
    constructor() {
        this.data = []
    }

    push(val: number): void {
        this.data.push(val)
        return null
    }

    pop(): void {
        this.data.pop()
        return null
    }

    top(): number {
        // let top = this.data.pop()
        // this.data.push(top)
        // return top
        return this.data[this.data.length-1]
    }

    getMin(): number {
        let min = this.data[0]
        for(let i=1;i<this.data.length;i++){
            let item = this.data[i]
            if(item<min) {
                min = item
            }
        }
        return min
    }
}
function MinStack(){
    this.stack = []
    this.stack2 = []
}
MinStack.prototype.push = function(val){
    this.stack.push(val)
    if(this.stack2.length==0||this.stack2[this.stack2.length-1]>val){
        this.stack2.push(val)
    }
}
MinStack.prototype.pop = function(){
    if(this.stack.pop()===this.stack2[this.stack2.length-1]){
        this.stack2.pop()
    }
}
MinStack.prototype.top = function(){
    return this.stack[this.stack.length-1]
}
MinStack.prototype.getMin = function(){
    return this.stack2[this.stack2.length-1]
}

let minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();  // 返回 -3.
minStack.pop();
minStack.top();      // 返回 0.
minStack.getMin();   //-2