0
点赞
收藏
分享

微信扫一扫

3.7 队列的最大值问题


1. 前言

本文的一些图片, 资料 截取自编程之美

2. 问题描述

3.7 队列的最大值问题_数据

3. 问题分析

解法一 : 在队列添加元素的时候 如果添加的元素大于当前的最大元素, 则对最大元素进行更新,
在队列删除元素的时候, 如果删除元素即为最大的元素, 则遍历一次列表更新最大的元素

解法二 : 使用一个列表维护进队, 出队的顺序, 然后使用一个大根堆来维护最大的元素,
如果添加的元素大于当前的最大元素, 则添加元素重新整堆,
在队列删除元素的时候, 如果删除元素即为最大的元素, 则删除最大的元素重新整堆

解法三 : 利用两个栈来构造一个队列, 一个栈用作处理业务, 另一个栈用作存储入队的数据, 当数据入队的时候, 进入存储栈, 当数据出队的时候, 优先出业务栈中的数据, 如果其中没有元素了, 则将存储栈中的数据全部倒到业务栈中, 然后在出栈业务栈栈顶元素
其中这里的栈可以在O(1)的时间复杂度内, 找到最大值 [设计的很巧妙]

4. 代码

备注 : 这里的对于堆的处理, 并没有采用规范的处理方式

/**
 * file name : Test08FindMaxEleInQueue.java
 * created at : 2:52:32 PM May 28, 2015
 * created by 970655147
 */

package com.hx.test04;

public class Test08FindMaxEleInQueue {

    // 找出队列中的最大值
    public static void main(String []args) {

        // Queue Test
//      Queue01 que = new Queue01();
//      Queue02 que = new Queue02();
        Queue03 que = new Queue03();

        que.enqueue(1223);
        que.enqueue(34);
        que.enqueue(21);
        que.enqueue(1);
        que.enqueue(3);

        Log.log(que.getMaxVal());
        Log.log(que);


        que.dequeue();
        que.dequeue();
        que.dequeue();

        Log.log(que.getMaxVal());
        Log.log(que);



        // Stack Test
//      Stack stack = new Stack();
//      stack.push(33);
//      stack.push(234);
//      stack.push(3);
//      stack.push(322);
//      Log.log(stack.getMaxVal());
//      
//      stack.pop();
//      stack.pop();
//      Log.log(stack.getMaxVal());
//      Log.log(stack);

    }

    // 维护一个maxVal的值   来表示最大的值
    // 如果有其他元素入队  并且大于当前的maxVal  更新maxVal
    // 如果最大的元素 出队了, 则更新maxVal
    static class Queue01 {
        // data 维护队列的数据
        // maxVal 保存当前队列最大值
        Deque<Integer> data;
        int maxVal;

        // 初始化
        public Queue01() {
            data = new LinkedList<Integer>();
            maxVal = -1;
        }

        // 入队   如果该值大于maxVal   更新maxVal
        public void enqueue(Integer ele) {
            if(ele > maxVal) {
                maxVal = ele;
            }

            data.addLast(ele);
        }

        // 出队  如果该出队值为maxVal  则更新maxVal[遍历队列查找]
        public Integer dequeue() {
            Integer res = data.removeFirst();
            if(res == maxVal) {
                updateMaxVal();
            }
            return res;
        }

        // 遍历队列找出最大值
        private void updateMaxVal() {
            Integer max = Integer.MIN_VALUE;
            Iterator<Integer> it = data.iterator();
            while(it.hasNext()) {
                Integer ele = it.next();
                if(ele > max) {
                    max = ele;
                }
            }

            maxVal = max;
        }

        // 获取最大值
        public int getMaxVal() {
            return maxVal;
        }

        // for debug ...
        public String toString() {
            StringBuilder sb = new StringBuilder();
            Iterator<Integer> it = data.iterator();
            while(it.hasNext()) {
                Integer ele = it.next();
                sb.append(ele + " ");
            }

            return sb.toString();
        }

    }

    // 思想 : 维护一个队列  维护了队列的进出顺序, 并且维护了一个堆   来找最大的元素
    static class Queue02 {
        // 维护数据顺序的队列  以及维护最大元素的堆
        Deque<Integer> que;
        List<Integer> heap;

        // 初始化
        public Queue02() {
            que = new LinkedList<Integer>();
            heap = new ArrayList<Integer>();
        }

        // 入队操作   将数据添加到que, heap中   
        // 如果ele大于当前的最大元素 并进行整堆
        public void enqueue(Integer ele) {
            que.addLast(ele);
            heap.add(ele);

            if(ele > heap.get(0)) {
                buildMaxHeap(heap);
            }
        }

        // 出队操作   从que中移除队列头部的元素, 并移除heap中响应的元素
        // 如果移除的是最大的元素  进行整堆
        public Integer dequeue() {
            Integer res = que.removeFirst();
            Integer tmp = heap.get(0);
            heap.remove(res);

            if(res.equals(tmp) ) {
                buildMaxHeap(heap);
            }

            return res;
        }

        // 获取最大的元素   即堆顶
        public Integer getMaxVal() {
            return heap.get(0);
        }

        // 构建大根堆 
        private void buildMaxHeap(List<Integer> heap) {
            if(heap.size() > 1) {
                int now = heap.size()/2 - 1;
                // 将now所在的结点 置为该节点,左子节点,右子节点中最小的数的结点交换位置
                getMax(heap, now, getLeft(now));
                if((heap.size() & 1) == 1){
                    getMax(heap, now, getRight(now));
                }

                // 然后从now开始不断的整堆 知道now<0 结果堆顶为最大的元素
                for(int j=now-1; j>=0; j--){
                    getMax(heap, j, getLeft(j));
                    getMax(heap, j, getRight(j));
                }
            }
        }

        // 如果array[child]>array[parent] 则交换他们两个的数据
        public static void getMax(List<Integer> array, int parent, int child){
            if(array.get(parent) < array.get(child) ){
                _swap(array, parent, child);
            }
        }

        // 交换arr的两个元素
        private static void _swap(List<Integer> arr, int idx, int i) {
            int tmp = arr.get(idx);
            arr.set(idx, arr.get(i));
            arr.set(i, tmp);
        }

        // 获取左孩子的索引
        public static int getLeft(int now){
            return 2*now + 1;
        }

        // 获取右孩子的索引
        public static int getRight(int now){
            return 2*(now+1);
        }

        // Debug
        public String toString() {
            StringBuilder sb = new StringBuilder();
            Iterator<Integer> it = que.iterator();
            while(it.hasNext()) {
                Integer ele = it.next();
                sb.append(ele + " ");
            }

            return sb.toString();
        }

    }

    // 思想 : 因为下面的Stack获取最大值是线性的   所以现在用两个Stack来构造一个Queue
    static class Queue03 {
        // bus 表示业务队列  控制出队   如果bus为空   则从store向bus传输数据
        // store 表示存储队列  控制入队
        Stack bus;
        Stack store;

        // 初始化
        public Queue03() {
            bus = new Stack();
            store = new Stack();
        }

        // 入队
        public void enqueue(int ele) {
            store.push(ele);
        }

        // 出队  如果bus队列为空的话  将数据转从store转移到bus中
        public int dequeue() {
            if(bus.isEmpty() ) {
                while(!store.isEmpty() ) {
                    bus.push(store.pop() );
                }
            }

            return bus.pop();
        }

        // 获取两个数的较大者
        public int getMax(int x, int y) {
            return x > y ? x : y;
        }

        // 获取整个队列的最大值  即获取两个Stack中最大值较大的那个
        public int getMaxVal() {
            return getMax(bus.getMaxVal(), store.getMaxVal() );
        }

        // for debug ...
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("business stack : ");
            sb.append(bus.toString() );
            sb.append("store stack : ");
            sb.append(store.toString() );

            return sb.toString();
        }

    }

    // item 存放栈中的数据, link2NextMaxItem 如果其元素为-1  则表示该元素之前的最大的元素值为   向左数最靠近该项的大于-1的索引对应的值
                                    // 否则如果该元素大于-1   则说明当前元素之前的所有元素 最大值为该索引对应的元素值
    // sp 表示下一个添加元素的位置, maxItemIdx 表示当前栈中最大元素的索引
    static class Stack {
        final int CAP = 10;

        int[] item;
        Deque<Integer> link2NextMaxItem;
        int sp, maxItemIdx;

        // 初始化
        public Stack() {
            sp = 0;
            maxItemIdx = -1;
            item = new int[CAP];
            link2NextMaxItem = new LinkedList<Integer>();
        }

        // 向栈中中添加一个元素  如果超过了CAP  则忽略该元素
        // 如果该元素  大于当前最大元素  则设置当前sp之前的最大的元素索引为maxItemIdx   然后更新maxItemIdx为sp
        // 否则  设置link2NextMaxItem的该项为-1[表示该项之前的所有数据最大的值为  左数最靠近该项的大于0的的索引的数]
        public void push(int ele) {
            if(isFull() ) {
                return ;
            }

            item[sp] = ele;
            if(ele > getMaxVal() ) {
                link2NextMaxItem.push(maxItemIdx);
                maxItemIdx = sp;
            }

            sp ++;
        }

        // 将栈顶的值出栈
        // 如果sp退到了maxItemIdx  这时说明已经将maxItemIdx对应的值pop出去了
        // 这时获取接下来的最大的元素索引
        public int pop() {
            if(isEmpty() ) {
                return -1 ;
            }

            sp --;
            int res = item[sp];
            if(sp == maxItemIdx ) {
                maxItemIdx = link2NextMaxItem.pop();
            }

            return res;
        }

        // 判断当前Stack是否为空          因为sp为下一个添加的元素的位置    所以如果其为0的话表示Stack为空
        public boolean isEmpty() {
            return sp == 0;
        }

        // 判断当前Stack是否满了          因为sp为下一个添加的元素的位置    所以如果其为CAP的话表示Stack满了
        public boolean isFull() {
            return sp == CAP;
        }

        // 获取最大值   如果maxItemIdx退到了-1  则返回minValue, 否则 返回maxItemIdx对应的值
        private int getMaxVal() {
            if(maxItemIdx >= 0) {
                return item[maxItemIdx];
            } else {
                return Integer.MIN_VALUE;
            }
        }

        // Debug
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("bottom -> top : ");
            for(int i=0; i<sp; i++) {
                sb.append(item[i] + " ");
            }
            sb.append("\r\n");

            return sb.toString();
        }   
    }

}

5. 运行结果

3.7 队列的最大值问题_编程之美_02

6. 总结

对于第一种解法, 每次在最大元素出队的时候, 都需要遍历一次队列, 重新更新最大元素
对于第二种解法, 需要使用额外的空间来存储队列中的元素
对于第三种解法, 非常巧妙的使用了一两个栈来构建一个队列, 尤其是这里的栈的构造非常巧妙, Stack中使用了一个栈来维护当前最大元素之前的最大元素, 这样当当前最大元素出栈的时候, 就可以轻松的获取到第二大的元素[当前最大元素] 了

注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!


举报

相关推荐

0 条评论