【刷题 issue1】程序员代码面试指南 —— IT 名企算法与数据结构题目最优解

第一章 栈和队列

1.1 设计一个有 getMin 功能的栈

【题目】

实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。

【要求】

  1. pop、push、getMin 操作的时间复杂度都是 O(1)。
  2. 设计的栈类型可以使用现成的栈结构。

【难度】

士 ★☆☆☆

【题解】

数据结构采用两个栈:一个用来保存当前栈中的元素,记为 stackData;另一个栈用于保存每一步操作后栈中元素的最小值,记为 stackMin。实现方式有两种:

第一种方案:

  • 压入数据规则:(假设当前数据为 newNum,先将其压入 stackData,然后判断 stackMin 是否为空)
    • 如果为空,则 newNum 也压入 stackMin;
    • 如果不为空,则比较 newNum 和 stackMin 的栈顶元素 topNum 的大小:
      • 如果 newNum ≤ topNum,则 newNum 也压入 stackMin;
      • 如果 newNum > topNum,则 newNum 不压入 stackMin。
  • 弹出数据规则:
    • 先在 stackData 中弹出栈顶元素,记为 value。然后比较其与当前 stackMin 的栈顶元素 topNum 的大小。
    • 由压入规则可知,stackMin 中的元素从栈底到栈顶逐渐减小,topNum 既是 stackMin 的最小值,也是当前 stackData 中元素的最小值。因此,value 只可能大于或等于 topNum。
      • 当 value = topNum,stackMin 弹出栈顶元素;
      • 当 value > topNum,stackMin 不弹出栈顶元素。
    • 压入操作和弹出操作是对应的。
  • 查询当前栈中的最小值:
    • 由压入规则和弹出规则可知,stackMin 始终记录着 stackData 中的最小值。所以,最小值即为 stackMin 的栈顶元素。

第二种方案:

  • 压入数据规则:(假设当前数据为 newNum,先将其压入 stackData,然后判断 stackMin 是否为空)
    • 如果为空,则 newNum 也压入 stackMin;
    • 如果不为空,则比较 newNum 和 stackMin 的栈顶元素 topNum 的大小:
      • 如果 newNum ≤ topNum,则 newNum 也压入 stackMin;
      • 如果 newNum > topNum,则将 topNum 重复压入 stackdsadaMin。
  • 弹出数据规则:
    • 在 stackData 中弹出数据,同时弹出 stackMin 的栈顶元素。
    • 压入操作和弹出操作是对应的。
  • 查询当前栈中的最小值:
    • 由压入规则和弹出规则可知,stackMin 始终记录着 stackData 中的最小值。所以,最小值即为 stackMin 的栈顶元素。

【分析】

相同点:

  • 方案一和方案二都是用 stackMin 保存着 stackData 每一步的最小值;
  • 所有操作的时间复杂度都是 O(1),空间复杂度都是 O(n)。

不同点:

  • 方案一中 stackMin 压入时稍省空间,但是弹出时稍费时间;
  • 方案二中 stackMin 压入时稍费空间,但是弹出时稍省时间。

【实现】

  • MyStack.java
public interface MyStack {

    /**
     * 入栈
     *
     * @param newNum 压入堆栈
     * @throws Exception 栈为空
     */
    void push(int newNum) throws Exception;

    /**
     * 出栈
     *
     * @return 弹出堆栈
     * @throws Exception 栈为空
     */
    int pop() throws Exception;

    /**
     * 获得栈中最小元素
     *
     * @return 栈中最小元素
     * @throws Exception 栈为空
     */
    int getMin() throws Exception;

}
  • AbstractMyStack.java
import java.util.Stack;

public abstract class AbstractMyStack implements MyStack {

    protected Stack<Integer> stackData;
    protected Stack<Integer> stackMin;

    public AbstractMyStack() {
        stackData = new Stack<>();
        stackMin = new Stack<>();
    }

}
  • MyStack1.java
public class MyStack1 extends AbstractMyStack {

    @Override
    public void push(int newNum) throws Exception {
        if (this.stackMin.isEmpty() || newNum <= this.getMin()) {  // 不能写成 newNum < this.getMin()
            this.stackMin.push(newNum);
        }
        this.stackData.push(newNum);
    }

    @Override
    public int pop() throws Exception {
        if (this.stackData.isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        if (this.stackData.peek() == this.getMin()) {
            this.stackMin.pop();
        }
        return this.stackData.pop();
    }

    @Override
    public int getMin() throws Exception {
        if (this.stackData.isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        return this.stackMin.peek();
    }

}
  • MyStack2.java
public class MyStack2 extends AbstractMyStack {

    @Override
    public void push(int newNum) throws Exception {
        if (this.stackMin.isEmpty() || newNum <= this.getMin()) {
            this.stackMin.push(newNum);
        } else {
            this.stackMin.push(this.stackMin.peek());
        }
        this.stackData.push(newNum);
    }

    @Override
    public int pop() throws Exception {
        if (this.stackData.isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        this.stackMin.pop();
        return this.stackData.pop();
    }

    @Override
    public int getMin() throws Exception {
        if (this.stackData.isEmpty()) {
            throw new RuntimeException("栈为空");
        }
        return this.stackMin.peek();
    }

}
  • MyStackTest.java
public class MyStackTest {

    public static void main(String[] args) throws Exception {
        MyStack stack1 = new MyStack1();
        MyStack stack2 = new MyStack2();
        test(stack1);
        test(stack2);
    }

    private static void test(MyStack stack) throws Exception {
        if (stack == null) {
            throw new RuntimeException("参数不合法!");
        }
        stack.push(3);
        stack.getMin();
        stack.push(4);
        stack.push(5);
        stack.push(1);
        stack.push(2);
        stack.push(1);
        stack.getMin();
        stack.pop();
        stack.pop();
        stack.pop();
        stack.pop();
        stack.pop();
        stack.getMin();
        stack.pop();
    }

}
发布了147 篇原创文章 · 获赞 72 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/Pranuts_/article/details/100126913