MinStack 和MaxStack

leetcode链接
包含min函数的stack

分析

利用一个LinkedList 链表存储数据,类似于链stack, 还有数组stack 采用ArrayList存储
关于如何查找最小元素的情况

思路一 双stack

  1. stack 保存正常的元素情况
  2. min_stack 记录最小元素的顺序,
    上述min_stack存储的也是真正的元素情况,
    == 当加入一个元素时,有两种情况下要加入min_stack==
  • 第一次加入 元素,此时min_stack.isEmpty()==true;
  • 当min_stack.peek()>=x时

代码

Java stack当中是没有top(), 具有peek()

class MinStack {

    /** initialize your data structure here. */
    Stack<Integer> stack,minStack;
    //
    public MinStack() {
        stack =new Stack<>();
        minStack =new Stack<>();
    }
    
    public void push(int x) {
        // 如何使保持一样的情况也是要进入
        if(stack.isEmpty() || x<=minStack.peek())
        //  在这种情况下,只有minStack和stack 当中的数量是不一致的 ,如果当前要进入stack的元素要小于
            minStack.push(x);
        stack.push(x);
    }
    
    public void pop() {
        if(stack.isEmpty())   return;
        if(stack.peek().intValue()==minStack.peek().intValue())
            minStack.pop();
        stack.pop();
    }
    
    public int top() {
    
        return stack.peek();
    }
    
    public int getMin() {
        return minStack.peek();
    }
}

/**
 * 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();
 */

时间复杂度方面的情况

双stack的主要思想是

思路二:two stack

使用一个min, 记录stack中的最小值,但是也带来了问题,如果最小元素出stack后如何找到下一个最小的元素?
所以还是没有解决该问题
不容易想到用两个栈来存储数据,一个用于正常的存储栈数据,,另一个用于存储前一个栈的最小值。
一个stack存储真正的元素,另一min_stack记录最小元素的情况
MinStack 和MaxStack_第1张图片

终极思路

如何设计一个时间复杂度为O(1),空间复杂度为O(1), 只使用一个stack的情况,减少使用一个stack 的情况,
stack 不是存储真实压入的数据,minfile 是存储真实的最小数据情况
回到原来的思路: 采用一个stack记录数据,minfile记录最小值情况

push

假设要插入的是x , minfile 是最小数据情况

  • 如果stack为空,是第一次插入数据情况,则直接插入, stack.push(x), min = x;
  • 如果stack 不为空,
    - 判断 x 与min 的大小情况,
    - 如果 x>=min 的话, 对最小值没有影响,直接插入即可,也不用更新min
    - 如果 x

为何插入2*x-min

==为何要插入2*x-premin 然后 current_min=x ==
主要是为了pop()的stack的元素标志是否是弹出stack中最小的元素
如果stack.peek()小于min, 说明要出弹出的就是最小元素,执行pop()操作要解密update min
如果stack.peek()大于等于min, 说明要min 还是留在stack 里面,不用update min,直接pop()

pop

int y =stack.peek()

  • 如果y 大于或者等于 premin, 说明min 还在stack 中, 所以直接stack.pop() 后即可
  • 如果y 小于 min, 那么说明真在的y 是 min, 直接返回 min然后 更新 min , min = 2 * premin- y,

总结

如果stack.peek < min , 那么原来的数据其实就是min ,真正要压入的数据

说白了就是对数据进行一定程度对变换即可,加密和揭秘过程,如果是stack.peek()>min, 或者x>=min , 直接插入即可,

当x

**x 代表入stack, y 代表出stack, 即使 stack.peek()
使用这个两个元素与min, 进行一定程度对比较即可
如果 x 如果 y **

package data_struture;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;

/**
 * 155. Min Stack
 * Design a stack that supports push, pop, top, and retrieving the minimum element in constant time.
 *
 * push(x) -- Push element x onto stack.
 * pop() -- Removes the element on top of the stack.
 * top() -- Get the top element.
 * getMin() -- Retrieve the minimum element in the stack. 要求使用
 */
public class MinStack {
    //构造函数情况

    private int min;
    private Stack<Integer> stack;

    public MinStack() {
        stack = new Stack<>();
    }

    //压入stack
    public void push(int x) {
    //第一次处理整个元素当情况
        if (stack.isEmpty()) {
            min = x;
            stack.push(x);
        }
        //x
        if (x < min) {
            min = x;
            stack.push(2 * x - min);
        } else {
            stack.push(x);
        }

    }

    //弹出stack顶部的元素,删除
    public void pop() {
        if (stack.isEmpty()) {
            System.out.println("Stack is empty");
            return;
        }
        System.out.print("Top Most Element Removed: ");
        int y = stack.peek();
        //需要揭秘
        if (y < min) {
            System.out.println(min);
            min = 2 * min - y;

        } else {
            System.out.println(y);
            
        }
    }

    //返回stack顶部的元素情况,不删除
    public int top() {
        if (stack.isEmpty()) {
            System.out.println("Stack is empty ");
            return -1;
        }
        Integer y = stack.peek();
        System.out.println("Top Most Element is: ");
        if (y >= min) {
            return y;

        } else {
            return min;
        }
    }

    public int getMin() {
        if (stack.isEmpty()) {
            System.out.println("stack is empty");
            return -1;
        }
        return min;
    }


        public static void main(String[] args) {
        MinStack test =new MinStack();
        test.push(-2);
        test.push(0);
        test.push(-3);
        System.out.println(test.getMin());
        test.pop();
        System.out.println(test.top());
        System.out.println(test.stack.size());
        System.out.println(test.getMin());
    }
}

参考链接过程

LeetCode后面最佳解法

基本原理同上面是一样的情况,

只不过不是采用加密和解密不是采用上面这种解法, 对于每个元素

public class MinStack {
    long min;
    Stack<Long> stack;

    public MinStack(){
        stack=new Stack<>();
    }
    
    public void push(int x) {
        if (stack.isEmpty()){
            stack.push(0L);
            //自动类型转换
            min=x;
        }else{
        // 都是自动类型转换
            stack.push(x-min);//Could be negative if min value needs to change
            if (x<min) min=x;
        }
    }

    public void pop() {
        if (stack.isEmpty()) return;
        
        long pop=stack.pop();
        
        if (pop<0)  min=min-pop;//If negative, increase the min value
        
    }

    public int top() {
        long top=stack.peek();
        if (top>0){
            return (int)(top+min);
        }else{
           return (int)(min);
        }
    }

    public int getMin() {
        return (int)min;
    }
}
Comments: 52

MaxStack 设计

如何设计一个最大stack,
Max Stack

设计的API设计要求等概念

设计的时候主要包括下面几个api 即可,

  1. push
  2. pop() return the top and then remove
  3. peekMax() return the maximum
  4. popMax() return the maximum and remove , 如果有多个相同的最大值,只需要返回最后入stack的那个元素即可
  5. top() return the top element but do not remove

异同点的基本情况

与上面minstack 这道题目相比,就是多了popMax(), 并且getMin() 变换成了,

问题的关键就是如何popMax() 如何才是关键的因素情况, 如何做到将最大的元素弹出来,

popmax 的时候采用一个Stack 保存从numStack当中弹出的元素情况,找到最大max,然后再讲中间结果弹出numStack 即可

解法一:双stack

在中间结果如何保存

import java.util.Stack;

/**
 * @ClassName MaxStack
 * @Description TODO
 * @Author coderlau
 * @Date 2019/1/28 8:36 PM
 * @Version 1.0
 **/
public class MaxStack {
    Stack<Integer>  numStack,maxStack;

     // 时间复杂度为O(n),时间


    public MaxStack() {
        numStack = new Stack<>();
        maxStack = new Stack<>();
    }

    public void push(int x){
        //  时间复杂度
        if(numStack.isEmpty() || maxStack.peek()<=x) {
            maxStack.push(x);
        }

        numStack.push(x);
    }

    //O(1) 时间复杂度
public int pop(){
          if(numStack.peek().intValue()==maxStack.peek().intValue()){
             maxStack.pop();
          }
        return  numStack.pop();
    }

    // 0(1) 时间复杂度
public int top(){
      return numStack.peek();
    }
    public int popMax(){

        int max =peekMax();
        Stack<Integer> tmp =new Stack<>();
        // stack 当中的中间结果情况

        while(top()!=max){
            tmp.push(numStack.pop());
        }
        // 当扎到最后一个元素的时候,还是没有pop()来的基本情况
        numStack.pop();
        while(!tmp.isEmpty()){
            numStack.push(tmp.pop());
        }
        return maxStack.pop();
    }
    public int peekMax(){
            return maxStack.peek();
    }

    public static void main(String[] args) {
        MaxStack stack = new MaxStack();
        stack.push(5);
        stack.push(1);
        stack.push(5);
        System.out.println(stack.top());
        System.out.println(stack.popMax());
        System.out.println(stack.top());
        System.out.println(stack.peekMax());
        System.out.println( stack.pop());
        System.out.println( stack.top());
       // stack.top();
    }
}

空间复杂度为0(N), 时间复杂度也为O(N),

排序树sortmap treeMap

MinStack 和MaxStack_第2张图片
因为主要的时间复杂度花费在如何查找MAX,并且如何查找最大值,进行排序,

然后一些基本的增删查改都要在treeMap 和双向循环链表之间同时进行,保证数据的一致性问题

为何要选择treeMap

MinStack 和MaxStack_第3张图片
Using structures like Array or Stack will never let us popMax quickly. We turn our attention to tree and linked-list structures that have a lower time complexity for removal, with the aim of making popMax faster than
O(N) time complexity.

Say we have a double linked list as our “stack”. This reduces the problem to finding which node to remove, since we can remove nodes in O(1)

We can use a TreeMap mapping values to a list of nodes to answer this question. TreeMap can find the largest value, insert values, and delete values, all in
O(logN)
O(logN) time.

Algorithm

Let’s store the stack as a double linked list dll, and store a map from value to a List of Node.

When we MaxStack.push(x), we add a node to our dll, and add or update our entry map.get(x).add(node).

When we MaxStack.pop(), we find the value val = dll.pop(), and remove the node from our map, deleting the entry if it was the last one.

When we MaxStack.popMax(), we use the map to find the relevant node to unlink, and return it’s value.

public class MaxStack {
    TreeMap<Integer, List<Node>> map;
    DoubleLinkedList dll;

    public MaxStack() {
        map = new TreeMap();
        dll = new DoubleLinkedList();
    }

    public void push(int x) {
        Node node = dll.add(x);
        if(!map.containsKey(x))
            map.put(x, new ArrayList<Node>());
        map.get(x).add(node);
    }

    public int pop() {
        int val = dll.pop();
        List<Node> L = map.get(val);
        L.remove(L.size() - 1);
        if (L.isEmpty()) map.remove(val);
        return val;
    }

    public int top() {
        return dll.peek();
    }

    public int peekMax() {
        return map.lastKey();
    }

    public int popMax() {
        int max = peekMax();
        List<Node> L = map.get(max);
        Node node = L.remove(L.size() - 1);
        dll.unlink(node);
        if (L.isEmpty()) map.remove(max);
        return max;
    }
}

class DoubleLinkedList {
    Node head, tail;

    public DoubleLinkedList() {
        head = new Node(0);
        tail = new Node(0);
        head.next = tail;
        tail.prev = head;
    }

    public Node add(int val) {
        Node x = new Node(val);
        x.next = tail;
        x.prev = tail.prev;
        tail.prev = tail.prev.next = x;
        return x;
    }

    public int pop() {
        return unlink(tail.prev).val;
    }

    public int peek() {
        return tail.prev.val;
    }
// 其实就是如何删除一个基本节点的情况
    public Node unlink(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
        return node;
    }
}
class Node {
    int val;
    Node prev, next;
    public Node(int v) {val = v;}
}

时间复杂度为O(Log(N)), 当数据量比较的时候优势很明显。

LeetCode 题解 | 716. 最大栈 - 知乎

Shared - LeetCode Playground

你可能感兴趣的:(stack,Leetcode)