Loading

设计一个有getMin功能的栈 & 由两个栈组成的队列

设计一个有getMin功能的栈

题目:设计一个有getMin功能的栈

《程序员代码面试指南》第1题 P1 难度:士★☆☆☆

该题说是入门难度题,但实际上一上来想半天都想不出来o(╥﹏╥)o

随后去牛客网上在线编程,看到标签有个单调栈。好家伙,一上来就整个我不会的知识点

于是我去百度了一下,这篇CSDN博客讲的还不错(https://blog.csdn.net/lucky52529/article/details/89155694

结合单调栈的原理,我又想了一下,终于想出来了。

核心是需要2个栈,一个栈用来正常保存数据(stackData),另一个栈用来保存最小值(stackMin单调栈)。

我的思路是,push时:stackData正常保存数据,但是stackMin分两种情况:为空时,直接保存数据;不为空时,判断当前数据比stackMin的栈顶元素小(应该是小于等于),则入栈,否则不入栈。

pop时:stackData正常弹出数据,并与stackMin的栈顶元素作比较,相等则stackMin的栈顶元素出栈,否则不出栈。

getMin时直接调用stackMin的peek方法返回栈顶元素即可。

以下是我自己写的代码:

import java.util.*;

public class Main{
    private static Stack<Integer> stack = new Stack();
    private static Stack<Integer> minStack = new Stack();
    
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        Integer cnt = Integer.parseInt(scan.nextLine());
        for(int i=0; i<cnt; i++) {
            String line = scan.nextLine();
            if(line.startsWith("push")) {
                push(Integer.parseInt(line.substring(5)));
            } else if(line.startsWith("pop")) {
                pop();
            } else if(line.startsWith("getMin")) {
                System.out.println(getMin());
            }
        }
    }
    
    private static void push(Integer i) {
        stack.push(i);
        if(minStack.empty()) {
            minStack.push(i);
        } else if(i < minStack.peek()) {
            minStack.push(i);
        }
    }
    
    private static void pop() {
        Integer i = stack.pop();
        if(i == minStack.peek()) {
            minStack.pop();
        }
    }
    
    private static Integer getMin() {
        return minStack.peek();
    }
}

但是在看了标答后,发现还是有点小问题:

  1. push时,当前数据与stackMin的栈顶元素作比较时,如果相等也应该入栈(举例子,假如放3 2 1 1,按照我先前的思路则stackMin的数据为3 2 1。那么stackData的第一个1出栈后,stackMin就成了3 2,但是stackData的Min目前还是1,显然不对了)
  2. 调用pop()和getMin()方法最好加个判断是否为空,为空就抛异常(写代码还是得考虑全面一点嘛)

于是,牛客网上题解代码如下

import java.util.Stack;
import java.util.Scanner;
public class Main{
    private Stack<Integer> numStack;
    private Stack<Integer> minStack;
    public Main(){
        numStack = new Stack<Integer>();
        minStack = new Stack<Integer>();
    }
    public void push(int num){
        if(minStack.isEmpty()){
            minStack.push(num);
        }else{
            int min = Math.min(minStack.peek(),num);
            minStack.push(min);
        }
        numStack.push(num);
    }
    public int pop(){
        if(numStack.isEmpty()){
            throw new RuntimeException("stack is empty!");
        }
        minStack.pop();
        return numStack.pop();
    }
 
    public int getMin(){
        if(minStack.isEmpty()){
            throw new RuntimeException("stack is empty!");
        }
        return minStack.peek();
    }
    public static void main(String[] args){
        Main myClass = new Main();
        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        scanner.nextLine();
        String[] line;
        for(int i = 0; i < N; i++){
            line = scanner.nextLine().split(" ");
            if(line[0].equals("getMin")){
                System.out.println(myClass.getMin());
            }else if(line[0].equals("push")){
                myClass.push(Integer.valueOf(line[1]));
            }else{
                myClass.pop();
            }
        }
    }
}

这还只是第一种方法。书上还描述了第二种方法,具体原理参照书P3,这里不做过多赘述。

 

由两个栈组成的队列

题目:由两个栈组成的队列

《程序员代码面试指南》第2题 P5 难度:尉☆☆

该题在做之前就无意中看到了题目,然后想了一会,大概有了思路。等到真正做的时候,思路就很清晰了。(以后还是尽量不提前看题目,现看现想)

牛客网简单难度,感觉比第1题入门难度还简单一点,毕竟只用到了的知识点(不像第一题还用单调栈)

主要思路是,一个栈用来push(stackPush),一个栈用来pop和peek(stackPop);

push时:正常将数据push到stackPush栈中

poppeek时:判断stackPop栈是否为空,为空则需要将stackPush栈中的元素全部pop出来并push到stackPop里。然后再pop或peek

但是我一开始想的是pop或peek后第一次push还要压回stackPush栈。看了解答后,发现完全是多此一举,还浪费时间。

主要原因是前一次stackPush到stackPop,只要stackPop不为空,反正之前压入的数据从上到下是按照时间先后排列的,stackPop再次pop还是比较早的数据。此时stackPush再push也不受影响。等stackPop中数据pop空了,可以再次把stackPush中的数据反过来压进去,也同样是按照先后顺序从上到下的排列。所以是不需要回压的。总而言之,核心在于必须在stackPop为空时才能将stackPush的数据压入stackPop,满足这个条件,pop出的数据的顺序是不可能乱的。

我自己的代码(有点小问题)如下:

import java.util.*;

public class Main {
    private static Stack<Integer> stack1 = new Stack();
    private static Stack<Integer> stack2 = new Stack();
    
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = Integer.parseInt(scan.nextLine());
        for(int i=0; i<n; i++){
            String line = scan.nextLine();
            if(line.startsWith("add")) {
                add(Integer.parseInt(line.substring(4)));
            } else if(line.startsWith("poll")) {
                poll();
            } else if(line.startsWith("peek")) {
                System.out.println(peek());
            }
        }
    }
    
    private static void add(Integer i) {
        while(!stack2.empty()) {
            stack1.push(stack2.pop());
        }
        stack1.push(i);
    }
    
    private static void poll() {
        while(!stack1.empty()) {
            stack2.push(stack1.pop());
        }
        stack2.pop();
    }
    
    private static Integer peek() {
        while(!stack1.empty()) {
            stack2.push(stack1.pop());
        }
        return stack2.peek();
    }
}

然后牛客网上题解代码如下:

import java.util.*;
public class Main{
    static Stack<Integer> stack1 = new Stack<Integer>();
    static Stack<Integer> stack2 = new Stack<Integer>();
    public static void add(int a){
        stack1.push(a);
        if(stack2.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
    }
    public static int poll(){
        if(stack2.isEmpty() && !stack1.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        if(stack2.isEmpty()) throw new RuntimeException("队列空");
        return stack2.pop();
    }
    public static int peek(){
        if(stack2.isEmpty() && !stack1.isEmpty()){
            while(!stack1.isEmpty()){
                stack2.push(stack1.pop());
            }
        }
        if(stack2.isEmpty()) throw new RuntimeException("队列空");
        return stack2.peek();
    }
    public static void main(String[] args){
        Scanner scan = new Scanner(System.in);
        int n = Integer.valueOf(scan.nextLine());
        for(;n>0;n--){
            String str = scan.nextLine();
            String[] splits = str.split(" ");
            if("add".equals(splits[0])){
                add(Integer.parseInt(splits[1]));
            }else if("peek".equals(splits[0])){
                System.out.println(peek());
            }else{
                poll();
            }
        }
    }
}

(评论说这里面重复代码可以抽取出来。看来牛客网上题解的代码可能也有小瑕疵。因此完美的解答还是参照书本吧,毕竟也花钱买了书的(#^.^#)

此外我自己的思路描述可能也不标准,详细的思路还是参照书上大佬的解释吧)

posted @ 2021-10-28 17:20  幻梦翱翔  阅读(63)  评论(0)    收藏  举报