【算法刷题】实现一个能够获取栈中最小元素的栈

一、题目和要求

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

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

二、思路分析

  要实现能够查看栈中的最小值,我们需要记录下当前栈中数据的最小的数据,因为栈是涉及到出栈操作的,如果仅用一个值来记录最小值,当栈中的元素出栈以后,那个最小值就有可能失效,所以我首先想到的是在getMin方法中将所有的栈遍历一遍,但是栈没有遍历的方法,如果要遍历就需要用一个新的栈进行辅助,而且在这种方法需要遍历栈中的元素,不满足复杂度是O(1)的要求。
  另一种思路就是除了保存数据的栈,我们再新建一个保存最小值的栈,在这个栈中对应于数据栈保存对应的栈中的最小值,这样就可以轻松的获得栈中的最小值,而且也满足复杂度O(1)的要求

三、实现方案

方案一:

实现方法解析:

  创建一个存放最小值的栈minStack,在入栈的时候,将push的数据和当前最小值栈minStack中的栈顶元素进行比较,如果minStack为空或者push的值小于等于栈顶元素,就将push的数据入栈,否则,就对应的放一个和minStack栈顶元素相同的值,用来和dataStack中的数据相对应,在出栈的时候,我们就同时将minStack和dataStack出栈,这样两个栈中的数量是相同的,可以达到效果。示例代码如下:

示例代码

  • 首先我们创建一个抽象类AbstractSolution,用来编写不同方案相同的代码
    public static abstract class AbstractSolution {
        protected Stack<Integer> dataStack;

        protected Stack<Integer> minStack;

        public AbstractSolution() {
            this.dataStack = new Stack<>();
            this.minStack = new Stack<>();
        }

        public Integer getMin() {
            if (minStack.isEmpty()) {
                throw new RuntimeException("stack is empty. ");
            }
            return minStack.peek();
        }

        public boolean isEmpty() {
            return this.dataStack.isEmpty();
        }

        public String getAllData() {
            return dataStack.toString();
        }

        public String getAllMinData() {
            return minStack.toString();
        }

        public abstract void push(Integer data);

        public abstract Integer pop();
    }
  • 编写一个统一的测试方法,用来测试数据
    private void testSolution(AbstractSolution solution) {
        for (Integer integer : Arrays.asList(3, 4, 5, 1, 2, 1)) {
            solution.push(integer);
        }
        while (!solution.isEmpty()) {
            // 用栈的toString() 方法打印出来的栈是栈顶元素在最后面,它是按照入栈顺序打印的
            System.out.println(
                " ,currentData=" + solution.getAllData() + "  -> minData= " + solution.getMin() + ",minStack="
                    + solution.getAllMinData());
            System.out.print("popData: " + solution.pop());
        }
    }
  • 编写方案一的代码和测试方法
    public static class Solution1 extends AbstractSolution {

        public Solution1() {
            super();
        }

        public void push(Integer data) {
            Integer newMinData = minStack.isEmpty() ? data : data <= getMin() ? data : getMin();
            this.minStack.push(newMinData);
            this.dataStack.push(data);
        }

        public Integer pop() {
            if (dataStack.isEmpty()) {
                throw new RuntimeException("stack is empty. ");
            }
            this.minStack.pop();
            return this.dataStack.pop();
        }
    }
  • 测试方法:
    @Test
    public void testSolution1() {
        testSolution(new Solution1());
    }

测试结果

 ,currentData=[3, 4, 5, 1, 2, 1]  -> minData= 1,minStack=[3, 3, 3, 1, 1, 1]
popData: 1 ,currentData=[3, 4, 5, 1, 2]  -> minData= 1,minStack=[3, 3, 3, 1, 1]
popData: 2 ,currentData=[3, 4, 5, 1]  -> minData= 1,minStack=[3, 3, 3, 1]
popData: 1 ,currentData=[3, 4, 5]  -> minData= 3,minStack=[3, 3, 3]
popData: 5 ,currentData=[3, 4]  -> minData= 3,minStack=[3, 3]
popData: 4 ,currentData=[3]  -> minData= 3,minStack=[3]
popData: 3

方案二:

实现方法解析

  方案一种,minStack中的数据量和dataStack中是相同的,虽然在复杂度上面能满足要求,但我们仍然可以想办法减少minStack中的数量,达到减少空间复杂度的目的,具体思路如下:
  方案一中,我们在push的时候,如果push的数据大于minStack的栈顶元素,我们也是放了一个相同的栈顶元素进去,这样就造成了数据重复,我们可以从这里着手改进。如果在放入的时候,如果push的元素大于当前minStack中的栈顶元素,我们不放进去要怎么样呢?这样的结果就是两个栈的数量对不上,在pop元素的时候,就不能简单的将minStack中的元素也pop出来,需要将数据栈中pop出来的元素和当前minStack中的元素进行对比,从我们入minStack的逻辑可以看出,我们只放置了和push元素相等或者小于的值,也就是说,无论minStack是否为空,我们都会放入和push元素相同的值,这一点非常重要,从这个逻辑中我们可以发现,只要在出栈的时候,如果minStack中的栈顶元素和dataStack中的相同,我们就可以将minStack出栈一次,否则我们就不需要操作。代码如下:

示例代码

  • 方案二的代码
    public static class Solution2 extends AbstractSolution {

        public Solution2() {
            super();
        }

        public void push(Integer data) {
            if (this.minStack.isEmpty()) {
                this.minStack.push(data);
            } else if (data.compareTo(getMin()) <= 0) {
                this.minStack.push(data);
            }
            this.dataStack.push(data);
        }

        public Integer pop() {
            if (dataStack.isEmpty()) {
                throw new RuntimeException("stack is empty. ");
            }
            Integer data = dataStack.pop();
            // 为何是等于的时候会出栈?因为我们入栈的时候,放了小于和等于的值
            if (data.equals(getMin())) {
                this.minStack.pop();
            }
            return data;
        }
    }
  • 测试方法
    @Test
    public void testSolution2() {
        testSolution(new Solution2());
    }

测试结果:

 ,currentData=[3, 4, 5, 1, 2, 1]  -> minData= 1,minStack=[3, 1, 1]
popData: 1 ,currentData=[3, 4, 5, 1, 2]  -> minData= 1,minStack=[3, 1]
popData: 2 ,currentData=[3, 4, 5, 1]  -> minData= 1,minStack=[3, 1]
popData: 1 ,currentData=[3, 4, 5]  -> minData= 3,minStack=[3]
popData: 5 ,currentData=[3, 4]  -> minData= 3,minStack=[3]
popData: 4 ,currentData=[3]  -> minData= 3,minStack=[3]
popData: 3

四、总结

为了方便参考,整体代码也贴出来:

/**
 * 【题目】
 * 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作。
 * 【要求】
 * 1.pop、push、getMin 操作的时间复杂度都是O(1)。
 * 2.设计的栈类型可以使用现成的栈结构。
 */
public class StackGetMin {

    @Test
    public void testSolution1() {
        testSolution(new Solution1());
    }

    @Test
    public void testSolution2() {
        testSolution(new Solution2());
    }

    private void testSolution(AbstractSolution solution) {
        for (Integer integer : Arrays.asList(3, 4, 5, 1, 2, 1)) {
            solution.push(integer);
        }
        while (!solution.isEmpty()) {
            // 用栈的toString() 方法打印出来的栈是栈顶元素在最后面,它是按照入栈顺序打印的
            System.out.println(
                " ,currentData=" + solution.getAllData() + "  -> minData= " + solution.getMin() + ",minStack="
                    + solution.getAllMinData());
            System.out.print("popData: " + solution.pop());
        }
    }

    public static class Solution1 extends AbstractSolution {

        public Solution1() {
            super();
        }

        public void push(Integer data) {
            Integer newMinData = minStack.isEmpty() ? data : data <= getMin() ? data : getMin();
            this.minStack.push(newMinData);
            this.dataStack.push(data);
        }

        public Integer pop() {
            if (dataStack.isEmpty()) {
                throw new RuntimeException("stack is empty. ");
            }
            this.minStack.pop();
            return this.dataStack.pop();
        }
    }

    public static class Solution2 extends AbstractSolution {

        public Solution2() {
            super();
        }

        public void push(Integer data) {
            if (this.minStack.isEmpty()) {
                this.minStack.push(data);
            } else if (data.compareTo(getMin()) <= 0) {
                this.minStack.push(data);
            }
            this.dataStack.push(data);
        }

        public Integer pop() {
            if (dataStack.isEmpty()) {
                throw new RuntimeException("stack is empty. ");
            }
            Integer data = dataStack.pop();
            // 为何是等于的时候会出栈?因为我们入栈的时候,放了小于和等于的值
            if (data.equals(getMin())) {
                this.minStack.pop();
            }
            return data;
        }
    }

    public static abstract class AbstractSolution {
        protected Stack<Integer> dataStack;

        protected Stack<Integer> minStack;

        public AbstractSolution() {
            this.dataStack = new Stack<>();
            this.minStack = new Stack<>();
        }

        public Integer getMin() {
            if (minStack.isEmpty()) {
                throw new RuntimeException("stack is empty. ");
            }
            return minStack.peek();
        }

        public boolean isEmpty() {
            return this.dataStack.isEmpty();
        }

        public String getAllData() {
            return dataStack.toString();
        }

        public String getAllMinData() {
            return minStack.toString();
        }

        public abstract void push(Integer data);

        public abstract Integer pop();
    }

}


后记
  个人总结,欢迎转载、评论、批评指正

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值