• stack & queue及经典例题


    1. 用2个stack sort numbers

    给定一个stack1,里面的数是unsorted,只允许用额外一个stack2去sort这个stack。

    分析思路如下:

    • 一个global_min来放当前最小值(初始为stack1.pop()出来的值)
    • 然后依次和stack1.pop()出来的值进行比较,如global_min<stack1.pop(),则把stack1.pop()的值压入stack2,继续比较;如global_min>stack1.pop(),则把global_min压入stack2,global_min换成stack.pop()
    • 直到比完stack1最后一个值,再把第一轮最后的global_min压入stack1,此时的global_min为最小值
    • 把stack2中的数再压回stack1
    • 接着比下一轮时要注意,不是从stack1最底层开始比,要从stack1未sort好的部分开始比,即相当于每次压入global_min就在其后加一个挡板,之后的每一轮都从它之后的unsorted的数比较
    • 一直重复,直到『挡板』移到stack1最后一个数

    图示:

    * 如果stack1里有很多重复的元素,有没有更优化的方案?

     思路1:我们可以把和global_min重复的元素也放入stack2

     思路2:使用counter,记录和global_min重复的元素,但是并不压入stack2,最后global_min压入stack1的时候,根据counter,压入等量的数

    我们选择思路2:

    代码如下:

    注意这里的stack是用LinkedList(参考LinkedList)实现的,所以这里的

    peekFirst==peek

    offerFirst==push

    pollFirst==pop

    可以看成是deque,但是只用一边进出

    public class Solution {
      public void sort(LinkedList<Integer> s1) {
        LinkedList<Integer> s2 = new LinkedList<Integer>();
        // Write your solution here.
        int n=s1.size();
        sort_stack(s1,s2,n);
      }
      private void sort_stack(LinkedList<Integer> s1, LinkedList<Integer> s2, int size){
        int i=0;
        
        while(i<size-1){
          Integer global_max=s1.pollFirst();
          int counter=1;
              for(int j=0;j<size-i-1;j++){
                  if(s1.peekFirst()<global_max){
                      s2.offerFirst(s1.pollFirst());
                  }else if(s1.peekFirst()==global_max){
                      s1.pollFirst();
                      counter+=1; 
                  }else{
                      for(int k=1; k<=counter;k++){
                          s2.offerFirst(global_max);
                        } 
                      global_max=s1.pollFirst();
                      counter=1;
                  }   
              }
                for(int k=1; k<=counter;k++){
                    s1.offerFirst(global_max);
                } 
              while(!s2.isEmpty()){
                  s1.offerFirst(s2.pollFirst());
              }        
              i++;
          }
         }
    }

     2. 用两个stack实现一个queue

    enqueue: (in)stack1: 用来存新的元素

    dequeue: (out)stack2:用来pop:case1 如果stack2非空,直接pop —— O(1)

                 case2 如果stack2为空,先把所有的元素从stack1倒到stack2,再pop —— O(n)

    我们来分析一下armotized time complexity of dequeue:

    • 1st: n*stack1.pop()+n*s2.push()+1
    • 2nd: 1
    • 3rd: 1
    • .
    • .

    所以:(2n+1+1*(n-1))/n=3=O(1)

    (分子是underlying stack operations,分母是dequeue operations that we can support)

    代码如下:

    public class Solution {
        private Deque<Integer> stack1=new LinkedList<>();
        private Deque<Integer> stack2=new LinkedList<>();
      private void shuffle(){
        while(!stack1.isEmpty()){
          stack2.push(stack1.pop());
        }
      }
      public Integer poll() {
        if(stack2.isEmpty()){
          shuffle();
        }
        if(stack2.isEmpty()){
            return null;
         }
        return stack2.pop();
      }
      
      public void offer(int element) {
        stack1.push(element);
      }
      
      public Integer peek() {
       if(stack2.isEmpty()){
                shuffle();
        }
        if(stack2.isEmpty()){
              return null; 
         }
        return stack2.peek();
      }
      
      public int size() {
        return stack1.size()+stack2.size();
      }
      
      public boolean isEmpty() {
        if(stack2.isEmpty() && stack1.isEmpty()){
                return true;
        }
        return false;
      }
    }
    View Code

    *变种题1:两个queue实现一个stack

    Q:->xxxxxx->

    S:  ->xxxxxx

        <-

    push(): 直接往Q1放

    pop(): 把除了最后的元素都移到Q2,然后再Q1.pop()

    然后直接换一下reference,下次就又可以直接操作Q1

    此时Q2的作用相当于一个buffer

    *变种题2:一个queue实现一个stack

    此时我们要假设可以一直知道queue的size

    push():直接从q push

    pop(): 此时需要先把顶层前面的元素pop出来,然后从屁股push回去,直到把顶层元素压到最底层,pop出来

     3. 用stack实现min() function

    pop() push() top() min()

    stack1:is used to store input elements

    minstack:is used to store the min element so far in s1 (as its top elements)

    TC=O(1)

    SC=O(n)

    要注意的是,minstake里的数要和stack1的同步,比如stack1pop出一个数,如果这个数也再minstack的顶上,我们也要pop出

    public class Solution {
      public Solution() {
        // write your solution here
      }
      Deque<Integer> stack=new LinkedList<>();
      Deque<Integer> minstack=new LinkedList<>();
      public int pop() {
        if(stack.isEmpty()){
            return -1;
        }
        Integer element=stack.pop();
        if(element.equals(minstack.peek())){
              minstack.pop();
        }
        return element;
      }
      
      public void push(int element) {
        stack.push(element);
        if(minstack.isEmpty()||element<=minstack.peek()){
          minstack.push(element);
        }
      }
      
      public int top() {
        if(stack.isEmpty()){
            return -1;      
        }
            return stack.peek();
      }
      
      public int min() {
        if(stack.isEmpty() && minstack.isEmpty()){
         return -1; 
        }
        return minstack.peek();
      }
    }
    View Code

    4. 用2或3个stack实现deque

    deque:left.add() left.remove() right.add() right.remove()

    1)用两个stack:头对头

    ->     ||s1 s2||    ->

    <-                  <-

    left.add()=s1.push()——O(1)

    right.add()=s2.push()——O(1)

    left.remove():如果S1非空,则直接S1.pop()——O(1)

           如果S1为空,则把所有元素从S2移到S1,再S1.pop() ——O(n)

    同理,right.remove()

    那我们来算一下remove的amortized time

    worst case应该是 

    L.remove(); ——2n+1

    R.remove(); ——2(n-1)+1

    L.remove(); ——2(n-2)+!

    R.remove(); ——2(n-3)+!

    ……

    amotized time=(2n+!+2(n-1)+1+……+1)/n=n

    无法amortize成O(1)

    2)三个strck来提高 remove的TC

    S1和S2仍然作为左右手,而S3则作为一个buffer,来存放一半的元素

    例如deque=5678

    56||S1 S2||78

    如果,初始状态为:

    ||S1 S2||5678

    我们希望最后变成:

    56||S1 S2||78

    • 先把S2的一半7,8pop 出S2,压入S3:n/2 pop out of S2, n/2 push into S3
    • 再把剩的一半pop出S2,压入S1:n/2 pop out of S2, n/2 push into S1
    • 再把7,8压回S2:n/2 pop out of S3, n/2 push into S2

    n/2*6=3n。之后的n/2个操作,我们只需要pop对应边的元素就行了

    此时的amortized time=(3n+1*n/2)/(n/2)=7=O(1)

    总结:

    1. 什么时候需要往stack上考虑?

    当需要从左到右scan一个array/string时,如果要不断的回头看左边最新的元素师

    eg:逆波兰表达式:a*(b+c) -> abc+*

    public class Solution {
      public int evalRPN(String[] tokens) {
        // Write your solution here
        Deque<Integer> stack=new LinkedList<>();
            for(int i=0;i<tokens.length;i++){
             if(tokens[i]!="+" && tokens[i]!="-" && tokens[i]!="*" && tokens[i]!="/"){
                  Integer op = Integer.valueOf(Integer.parseInt(tokens[i]));  
                    stack.push(op);
              }else{
                       int op1=stack.pop(); 
                    int op2=stack.pop();
                    int res=0;
                    if(tokens[i]=="+"){
                        res=op1+op2;
                  }else if(tokens[i]=="-"){
                        res=op2-op1; //顺序
                  }else if(tokens[i]=="*"){
                     res=op2*op1; 
                  }else{
                        res=op2/op1; 
                  }
                stack.push(res);
              }
            }
            return stack.pop();
          }
    }

    2. 常见性质:S1的所有元素倒进S2, S2的顺序完全reverse

    S1->S2->S1顺序不变,amortized tc分摊到每一个move的元素时间为O(1)

  • 相关阅读:
    reids数据结构(四) ziplist
    redis 持久化机制
    redis数据结构(五) quik list
    redis数据结构(六) listpack
    python 聪明的尼姆游戏
    330 说一下Vue的$nextTick原理?
    330 为什么要使用 vue3, vue3 带来了什么好处?
    331 两数之和简单
    41 说一下Vue单页与多页的区别?
    42 怎样理解 Vue 的单向数据流?
  • 原文地址:https://www.cnblogs.com/x1mercy/p/8598675.html
Copyright © 2020-2023  润新知