实现具有两个堆栈的优先级堆栈

时间:2011-02-08 23:42:39

标签: java

这就是我认为它可能有用的方法:你有Stack 1根据优先级进行排序,每当一个新元素出现应该在堆栈的中间时,你会弹出Stack中的所有元素1到堆栈2,将元素添加到位,然后将堆栈2中的所有元素弹回堆栈1。

有人知道更有效的解决方案吗?

4 个答案:

答案 0 :(得分:4)

我能想到的唯一能让它更有效的方法是在插入后将两个堆栈之间的项目分开。

  1. 如果下一个操作是插入(而不是插入新的最高优先级项目),那么您在最后一次插入后将工作移动项目从堆栈2保存回到堆栈1只是为了移动它返回堆栈2进行下一次插入。

  2. 如果下一个操作是弹出或插入新的最高优先级项目,那么您将项目从堆栈2移回堆栈1 - 我们在原始解决方案中无论如何都要这样做,所以没有浪费努力。

  3. 编辑:

    我对这种替代方案的“复杂性”(休闲,而不是迂回)感到好奇,所以这里有一些用于视觉比较的未经测试的代码:

    注意:较大的值==较高的优先级

    原始impl:

    public void insert(int value) {
    
      // slide higher priority items on lower stack to higher stack temporarily
      while (! lowerStack.isEmpty() && lowerStack.peek() > value) {
        higherStack.push(lowerStack.pop());
      }
    
      assert lowerStack.isEmpty() || lowerStack.peek() <= value;
      assert higherStack.isEmpty() || higherStack.peek() >= value);
    
      lowerStack.push(value);
    
      // slide them back
      while (! higherStack.isEmpty()) {
        lowerStack.push(higherStack.pop());
      }
    
      assert higherStack.isEmpty();
    
    }
    
    public int pop() throws NoSuchElementException {
    
      assert higherStack.isEmpty();
      return lowerStack.pop();  // will throw if empty
    
    }
    

    替代impl:

    public void insert(int value) {
    
      // only one of the while loops below should really execute
    
      // slide any higher priority items on lower stack to higher stack
      while (! lowerStack.isEmpty() && lowerStack.peek() > value) {
        higherStack.push(lowerStack.pop());
      }
    
      // slide any lower priority items on higher stack to lower stack
      while (! higherStack.isEmpty() && higherStack.peek() < value) {
        lowerStack.push(higherStack.pop());
      }
    
      assert lowerStack.isEmpty() || lowerStack.peek() <= value;
      assert higherStack.isEmpty() || higherStack.peek() >= value);
    
      lowerStack.push(value);
    
    }
    
    
    public T pop() throws NoSuchElementException {
    
      // get to highest priority item
      while (! higherStack.isEmpty()) {
        lowerStack.push(higherStack.pop());
      }
    
      assert higherStack.isEmpty();
      return lowerStack.pop();  // will throw if empty
    
    }
    

答案 1 :(得分:3)

如果一个堆栈按顺序保留奇数优先级项目而另一个堆栈按顺序保留偶数优先级项目怎么办?

  • 在插入时,检查要插入的堆栈。您仍然可以使用其他堆栈的顶部暂时保留项目。

  • 在弹出窗口中,您可以比较哪个堆栈具有较高优先级的项目并将其弹出。

理论上,这不会大致减少要插入的操作次数(在堆栈之间滑动所需的项目数)吗?

编辑:

正如评论中所指出的,这种方法要求您有一种很好的方法来对两个堆栈之间的优先级值进行分级(您的优先级值实现了一个好的hashCode())。由于优先级值通常是线性顺序的,因此这种方法可能有效。

以下是一些可以使用的代码:

  • 原始方法== Temp Holding Stack == ~45行
  • 替代方法(其他答案)==拆分堆栈== ~50行
  • 奇数/偶数堆= = ~76行

典型运行:

...
Holding Stack Avg Moves = 1028.32 moves
Split Stack Avg Moves   = 747.85 moves
Odd/Even Stack Avg Moves    = 517.54 moves

代码:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Random;
import java.util.Stack;

public class PriorityStackProblem {

    private static final int NUMBER_OF_PASSES     = 100;
    private static final int MAX_START_STATE_SIZE = 100;
    private static final int MAX_OPERATIONS_SIZE  = 100;
    private static final int MAX_ITEM_VALUE       = 1000;

    public static abstract class MeasurableStack<T extends Comparable<? super T>> {

        protected int moves = 0;

        public abstract void load(final List<T> startState);
        public abstract void insert(final T value);
        public abstract T pop() throws NoSuchElementException;

        protected void move(
                final Stack<T> fromStack,
                final Stack<T> toStack) {
            toStack.push(fromStack.pop());
            moves++;
        }

        public void clearMoves() {
            moves = 0;
        }

        public int getMoves() {
            return moves;
        }

    }

    public static class TempHoldingStackApproach<T extends Comparable<? super T>>
    extends MeasurableStack<T> {

        private Stack<T> mainStack = new Stack<T>();
        private Stack<T> tempHoldingStack = new Stack<T>();

        @Override
        public void load(final List<T> startState) {
            mainStack.clear();
            tempHoldingStack.clear();
            clearMoves();
            T lastItem = null;
            for (final T item : startState) {
                mainStack.push(item);
                assert lastItem == null || item.compareTo(lastItem) >= 0;
                lastItem = item;
            }
        }

        @Override
        public void insert(final T value) {
            // slide higher priority items on main stack to temp stack
            while (!mainStack.isEmpty() && mainStack.peek().compareTo(value) > 0) {
                move(mainStack, tempHoldingStack);
            }

            assert mainStack.isEmpty() || mainStack.peek().compareTo(value) <= 0;
            assert tempHoldingStack.isEmpty() || tempHoldingStack.peek().compareTo(value) >= 0;

            mainStack.push(value);

            // slide them back
            while (!tempHoldingStack.isEmpty()) {
                move(tempHoldingStack, mainStack);
            }

            assert tempHoldingStack.isEmpty();
        }

        @Override
        public T pop() throws NoSuchElementException {
            assert tempHoldingStack.isEmpty();
            return mainStack.pop(); // will throw if empty
        }

    }

    public static class SplitStackApproach<T extends Comparable<? super T>>
    extends MeasurableStack<T> {

        private Stack<T> lowerStack = new Stack<T>();
        private Stack<T> higherStack = new Stack<T>();

        @Override
        public void load(final List<T> startState) {
            lowerStack.clear();
            higherStack.clear();
            clearMoves();
            T lastItem = null;
            for (final T item : startState) {
                lowerStack.push(item);
                assert lastItem == null || item.compareTo(lastItem) >= 0;
                lastItem = item;
            }
        }

        @Override
        public void insert(final T value) {
            // only one of the while loops below should really execute

            // slide any higher priority items on lower stack to higher stack
            while (!lowerStack.isEmpty() && lowerStack.peek().compareTo(value) > 0) {
                move(lowerStack, higherStack);
            }

            // slide any lower priority items on higher stack to lower stack
            while (!higherStack.isEmpty() && higherStack.peek().compareTo(value) < 0) {
                move(higherStack, lowerStack);
            }

            assert lowerStack.isEmpty() || lowerStack.peek().compareTo(value) <= 0;
            assert higherStack.isEmpty() || higherStack.peek().compareTo(value) >= 0;

            lowerStack.push(value);
        }

        @Override
        public T pop() throws NoSuchElementException {
            // get to highest priority item
            while (!higherStack.isEmpty()) {
                move(higherStack, lowerStack);
            }

            assert higherStack.isEmpty();
            return lowerStack.pop(); // will throw if empty
        }

    }

    public static class OddEvenStackApproach<T extends Comparable<? super T>>
    extends MeasurableStack<T> {

        private Stack<T> oddStack = new Stack<T>();
        private Stack<T> evenStack = new Stack<T>();

        @Override
        public void load(final List<T> startState) {
            oddStack.clear();
            evenStack.clear();
            clearMoves();
            for (final T item : startState) {
                if (item.hashCode() % 2 == 1) {
                    oddStack.push(item);
                } else {
                    evenStack.push(item);
                }
            }
        }

        @Override
        public void insert(final T value) {
            if (value.hashCode() % 2 == 1) {
                insert(value, oddStack, evenStack);
            } else {
                insert(value, evenStack, oddStack);
            }
        }

        @Override
        public T pop() throws NoSuchElementException {
            if (oddStack.size() <= 0) {
                return evenStack.pop();  // will throw if empty
            } else if (evenStack.size() <= 0) {
                return oddStack.pop();  // will throw if empty
            } else {
                final T oddTop = oddStack.peek();
                final T evenTop = evenStack.peek();
                if (oddTop.compareTo(evenTop) >= 0) {
                    return oddStack.pop();  // will throw if empty
                } else {
                    return evenStack.pop();  // will throw if empty
                }
            }
        }

        private void insert(
                final T value,
                final Stack<T> targetStack,
                final Stack<T> tempHoldingStack) {
            final int oldTargetStackSize = targetStack.size();
            final int oldHoldingStackSize = tempHoldingStack.size();

            int movedItems = 0;

            // slide any higher priority items on target stack to holding stack
            while (!targetStack.isEmpty() && targetStack.peek().compareTo(value) > 0) {
                move(targetStack, tempHoldingStack);
                movedItems++;
            }

            assert targetStack.isEmpty() || targetStack.peek().compareTo(value) <= 0;
            assert movedItems == 0 || tempHoldingStack.peek().compareTo(value) >= 0;

            targetStack.push(value);

            // slide items back
            while (movedItems > 0) {
                move(tempHoldingStack, targetStack);
                movedItems--;
            }

            assert targetStack.size() == oldTargetStackSize + 1;
            assert tempHoldingStack.size() == oldHoldingStackSize;
        }

    }

    private static List<Integer> generateStartState(final Random random) {
        final int size = random.nextInt(MAX_START_STATE_SIZE-1);
        final List<Integer> startState = new ArrayList<Integer>(size);
        for (int i = 0; i < size; i++) {
            startState.add(random.nextInt(MAX_ITEM_VALUE-1));
        }
        Collections.sort(startState);
        return startState;
    }

    private static List<Integer> generateOperations(
            final Random random,
            final int startStateSize) {
        final int size = random.nextInt(MAX_OPERATIONS_SIZE-1);
        final List<Integer> operations = new ArrayList<Integer>(size);
        int count = startStateSize;
        for (int i = 0; i < size; i++) {
            if (count > 0 && random.nextInt(2) == 0) {
                // null means pop
                operations.add(null);
                count--;
            } else {
                // non-null means insert the item
                operations.add(random.nextInt(MAX_ITEM_VALUE-1));
                count++;
            }
        }
        return operations;
    }

    private static <T extends Comparable<? super T>> int execute(
            final List<T> startState,
            final List<T> operations,
            final MeasurableStack<T> stack) {
        System.out.print(stack.getClass().getSimpleName()+" Moves:\t");
        stack.load(startState);
        for (final T item : operations) {
            if (item == null) {
                stack.pop();
            } else {
                stack.insert(item);
            }
            System.out.print(item+" ("+stack.getMoves()+") ");
        }
        System.out.println();
        return stack.getMoves();
    }

    private static float average(final int[] results) {
        int total = 0;
        for (final int moves : results) {
            total += moves;
        }
        return total / (float) results.length;
    }

    public static void main(final String[] args) {
        final Random random = new Random();

        final int[] holdingStackResults = new int[NUMBER_OF_PASSES];
        final int[] splitStackResults   = new int[NUMBER_OF_PASSES];
        final int[] oddEvenStackResults = new int[NUMBER_OF_PASSES];

        for (int pass = 0; pass < NUMBER_OF_PASSES; pass++) {

            final List<Integer> startState = generateStartState(random);
            final List<Integer> operations = generateOperations(random, startState.size());

            System.out.println("Start State ["+startState.size()+"]:\t"+startState);
            System.out.println("Operations ["+operations.size()+"]:\t"+operations);
            System.out.println();

            System.out.println("Moves: item (moves) item (moves) ...\t");

            holdingStackResults[pass] =
                execute(startState, operations, new TempHoldingStackApproach());

            splitStackResults[pass] =
                execute(startState, operations, new SplitStackApproach());

            oddEvenStackResults[pass] =
                execute(startState, operations, new OddEvenStackApproach());

            System.out.println();
            System.out.println(
                    "Holding: \t" + holdingStackResults[pass] + " total moves\n" +
                    "Split:   \t" + splitStackResults[pass]   + " total moves\n" +
                    "Odd/Even:\t" + oddEvenStackResults[pass] + " total moves");
            System.out.println("---");

        }

        System.out.println("Holding Stack Results:\t"+Arrays.toString(holdingStackResults));
        System.out.println("Split Stack Results:\t"+Arrays.toString(splitStackResults));
        System.out.println("Odd/Even Stack Results:\t"+Arrays.toString(oddEvenStackResults));
        System.out.println();

        final float holdingStackAverage = average(holdingStackResults);
        final float splitStackAverage   = average(splitStackResults);
        final float oddEvenStackAverage = average(oddEvenStackResults);

        System.out.println("Holding Stack Avg Moves\t\t= "  + holdingStackAverage+" moves");
        System.out.println("Split Stack Avg Moves\t\t= "  + splitStackAverage+" moves");
        System.out.println("Odd/Even Stack Avg Moves\t= " + oddEvenStackAverage+" moves");
        System.out.println();
    }
}

答案 2 :(得分:0)

如果是面试问题,也许你应该回答“ Stack根本没有优先权。它是优先队列。”:)

答案 3 :(得分:-1)

您不需要在内部使用堆栈只是因为您将堆栈作为外部接口(封装!)。只需插入您正在使用的任何集合的中间。