zoukankan      html  css  js  c++  java
  • 【老鸟学算法】包含 min函数的栈设计——java实现

    要求:
    1. 定义栈的数据结构,要求添加一个 min函数,能够得到栈的最小元素。
    2. 要求函数 min、push 以及 pop 的时间复杂度都是 O(1)。

    这是考验“栈”数据结构设计。众所周知,栈是一种“后进先出”的线性数据结构,其push和pop的操作都是在栈顶实现的,时间复杂度为O(1),不难设计。关键是min函数,要求时间复杂度为O(1),略有难度。

    难点在于,我们一方面入栈的时候,要比较得到当前栈最小值,另一方面出栈的时候,要考虑出栈元素是否为当前栈最小值。这样,就不能简单的使用一个存储空间存放当前的最小值了。

    一开始我的想法是在栈内创建两个数组,一个是当做栈操作的存储数组,一个是放从大到小排序的值数组。这个“从大到小排序的值数组”不仅消耗空间,而且由于要排序,导致push操作的时间复杂度变成O(n)。

    后来,偷偷搜了下,发现July同学的解法很好:自定义栈内,另建一个栈,用于存储最小值的数组下标。即,入栈时,通过最小值索引栈,得到当前最小值下标,取数组对应值,如果比入栈的当前值大,则将当前值入栈下标,压入到索引栈中;出栈时,若当前出栈值的索引为索引栈的栈顶元素,则索引栈也进行栈顶元素出栈操作。

    其它push、pop操作都比较传统,也很简单,不赘述了。

    java实现,自然而然想到泛型的使用了。这里的泛型也是有要求的:必须实现Compareable接口。

    接下来就是源码了。这里保留原先min笨方法的实现,注释掉了,大家可以对比看看。

    import java.util.Stack;
    /**
     * 自定义栈
     * @author oh_Maxy
     */
    public class MyStack<E>
    {
    
        private int size = 16;//默认规模
    
        private int nextSize = 16;//超过范围,扩展操作
    
        private Object[] valArr;//存储数据结构
    
        //private Object[] minArr;//存储顺序排序//笨办法
    
        private int index = -1;//栈顶下标
    
        private Stack<Integer> minStack;//存储最小值下标的栈//参考July的方法
    
        /**
         * 构造器
         */
        public MyStack()
        {
            valArr = new Object[size];
            //minArr = new Object[size];//笨办法
            minStack = new Stack<Integer>();
        }
    
        /**
         * 入栈
         * @param value
         */
        public void push(E value)
        {
            //不能为空
            if (null == value)
            {
                throw new NullPointerException("Pushed value can not be null!");
            }
    
            //入参必须为可比较的
            if (!(value instanceof Comparable))
            {
                throw new UnsupportedOperationException("The value must be Comparable!");
            }
    
            //栈满了,重构栈
            if (index >= size)
            {
                resetStack();
            }
    
            //入栈
            valArr[++index] = value;
    
            //最小值插入排序
            //putMinObj(value);//笨办法
            //最小值的索引入栈
            checkMinObj(value);
        }
    
        /**
         * 笨办法
         * 将push的值插入minArr中
         * @param value
         */
        /*private void putMinObj(E value)
        {
            Comparable valueComp = (Comparable) value;
            int i = index;
            for (; i > 0; i--)
            {
                if (valueComp.compareTo(minArr[i - 1]) < 0)
                {
                    minArr[i] = valueComp;
                    break;
                }
                minArr[i] = minArr[i - 1];
            }
    
            //push的值为最大值,放在首位
            if (i == 0)
            {
                minArr[0] = valueComp;
            }
        }*/
    
        /**
         * 参考July的最小索引入栈思想
         * @param value
         */
        @SuppressWarnings("unchecked")
        private void checkMinObj(E value)
        {
            //第一个参数,直接为最小参数
            if (index == 0)
            {
                minStack.push(index);
                return;
            }
    
            //入参的值
            Comparable valueComp = (Comparable) value;
    
            //当前最小值
            Comparable valueMinComp = (Comparable) valArr[minStack.peek()];
    
            //新入栈的值小于当前最小值,则当前索引入栈
            if (valueComp.compareTo(valueMinComp) < 0)
            {
                minStack.push(index);
            }
        }
    
        /**
         * 出栈
         */
        @SuppressWarnings("unchecked")
        public E pop()
        {
            //栈为空,报错
            if (index < 0)
            {
                throw new NullPointerException("The statck is empty!");
            }
    
            //若是最小值出栈(比较下标判断),需要将其索引出栈
            if (index == minStack.peek())
            {
                minStack.pop();
            }
    
            //出栈
            return (E) valArr[index--];
    
        }
    
        /**
         * 最小值
         */
        @SuppressWarnings("unchecked")
        public E getMin()
        {
            //return (E) minArr[index];//笨办法
            //若栈空,则报错空指针
            if (index < 0)
            {
                throw new NullPointerException("The statck is empty,no minValue returned!");
            }
    
            return (E) valArr[minStack.peek()];
        }
    
        /**
         * 重构栈
         */
        private void resetStack()
        {
            size += nextSize;
            Object[] newArr = new Object[size];
    
            System.arraycopy(valArr, 0, newArr, 0, size - nextSize);
    
            valArr = newArr;
        }
    
        //测试桩
        public static void main(String[] args)
        {
            //栈初始化//也可以直接使用Integer等实现了Comparable接口的封装类
            MyStack<MyValue> stack = new MyStack<MyValue>();
    
            //初始化几个值
            MyValue v1 = new MyValue(1);
            MyValue v2 = new MyValue(2);
            MyValue v3 = new MyValue(3);
            MyValue v4 = new MyValue(4);
            MyValue v5 = new MyValue(5);
    
            //入栈
            stack.push(v5);
            stack.push(v4);
            stack.push(v3);
            stack.push(v2);
            stack.push(v1);
            stack.push(v4);
            stack.push(v1);
    
            //最小值
            System.out.println("min: " + stack.getMin());
            //出栈
            System.out.println("pop: " + stack.pop());
            System.out.println("min: " + stack.getMin());
            System.out.println("pop: " + stack.pop());
            System.out.println("min: " + stack.getMin());
            System.out.println("pop: " + stack.pop());
            System.out.println("min: " + stack.getMin());
            System.out.println("pop: " + stack.pop());
            System.out.println("min: " + stack.getMin());
            System.out.println("pop: " + stack.pop());
            //最小值
            System.out.println("min: " + stack.getMin());
            System.out.println("min: " + stack.getMin());
            System.out.println("pop: " + stack.pop());
            System.out.println("min: " + stack.getMin());
            System.out.println("pop: " + stack.pop());
        }
    }
    
    
    /**
     * 自定义数值类
     */
    class MyValue
        implements Comparable<MyValue>
    {
    
        private int value;
    
        public MyValue(int value)
        {
            this.value = value;
        }
    
        public int getValue()
        {
            return value;
        }
    
        public int compareTo(MyValue obj)
        {
            //不能与null比较
            if (null == obj)
            {
                throw new NullPointerException("Can not compare to null!");
            }
    
            return this.value - obj.getValue();
        }
    
        @Override
        public String toString()
        {
            return "" + value;
        }
    }


    除了数据结构的实现,关于这个类的设计思想,也有两点值得关注的地方:
    1. 泛型的使用;
    2. 运行时异常的使用;

    欢迎斧正。


  • 相关阅读:
    UVa 116 单向TSP(多段图最短路)
    POJ 1328 Radar Installation(贪心)
    POJ 1260 Pearls
    POJ 1836 Alignment
    POJ 3267 The Cow Lexicon
    UVa 1620 懒惰的苏珊(逆序数)
    POJ 1018 Communication System(DP)
    UVa 1347 旅行
    UVa 437 巴比伦塔
    UVa 1025 城市里的间谍
  • 原文地址:https://www.cnblogs.com/riskyer/p/3313271.html
Copyright © 2011-2022 走看看