Alg

《数据结构与算法经典问题解析》--第五章

队列

Posted by Reborn on November 10, 2018

队列

队列同样是一种存储数据的线性表,只允许在队尾插入元素,在队首删除元素,是一种先进先出(FIFO)的线性表。

  • 专有名词概念:

    • 入队(EnQueue):在队列中插入一个元素
    • 出队(DeQueue):在队列中删除一个元素
    • 下溢(underflow):对一个空队列执行出队操作
    • 溢出(overflow):对一个满队列执行入队操作
  • 应用

    • 操作系统对于相同优先级的任务进行的顺序调度任务
    • 显示世界中的队列
    • 多道程序设计
    • 异步数据传输
  • 实现

    • 基于简单循环数组的实现方法
    • 基于动态循环数组的实现方法
    • 基于链表的实现方法

    采用循环数组可以有效减少空间的浪费。

    元素个数=rear-front+n(rear不等于front时)

//循环数组实现队列
public class ArrayQueue {
    private int front;
    private int rear;
    private int capacity;
    private int[] array;
    private ArrayQueue(int size) {
        capacity = size;
        front = -1;
        rear = -1;
        array = new int[size];
    }
    public static ArrayQueue createQueue(int size) {
        return new ArrayQueue[size];
    }
    public boolean isEmpty() {
        return front==-1;
    }
    public boolean isFull() {
        return (rear+1)%capacity == front;
    }
    public int getQueueSize() {
        return (capacity-front+rear+1)%capacity;
    }
    public void enQueue(int data) {
        if(isFull()){
            throw new QueueOverflowException("Queue Overflow");
        } else {
            rear = (rear+1)%capacity;
            array[rear] = data;
            if(front == -1) {
                front = rear;
            }
        }
    }
    public int deQueue() {
		int data = null;
        if(isEmpty()) {
            throw new EmptyQueueException("Queue Empty");
        } else {
            data = array[front];
            if(front==rear) {
                front =rear - 1;
            } else {
                front = (front+1)%capacity;
            }
        }
        return data;
    }
}
//基于链表实现的队列
public class LLQueue {
	private LLNode frontNode;
	private LLNode rearNode;
    private LLQueue() {
        this.frontNode = null;
        this.rearNode = null;
    }
    
    public static LLQueue createQueue() {
        return new LLQueue();
    }
    public boolean isEmpty() {
        return frontNode == null;
    }
    public void enQueue(int data) {
        LLNode newNode = new LLNode(data);
        if(rearNode != null) {
            rearNode.setNext(newNode);
        }
        rearNode = newNode;
        if(frontNode == null) {
            frontNode = rearNode;
        }
    }
    public int deQueue() {
        int data = null;
        if(isEmpty()) {
            throw new EmptyQueueException("Queue Empty");
        } else {
            data = frontNode.getData();//从队头删除元素
            frontNode = frontNode.getNext();
        }
        return data;
    }
}

问题

  1. 逆置队列元素?

    通过一个栈作为缓存

    public class QueueReversal {
        public static Queue reverseQueue(Queue queue) {
            Stack stack = new LLStack();
            while(!queue.isEmpty()) {
                stack.push(queue.deQueue());//栈中存储队头删除的元素
            }
            while(!stack.isEmpty()) {
                queue.enQueue(stack.pop());
            }
            return queue;
        }
    }
    
  2. 两个栈实现一个队列?

    入队:将一个元素压进第一个栈S1中。

    出队:当S2为空时,将S1中元素放进S2中,然后将S2的栈顶元素弹出;当S2不为空时,直接将S2的栈顶元素弹出。

  3. 两个队列实现一个栈?

    入栈:当Q1为空时,对Q2执行入队操作;当Q1不为空,对Q1执行入队操作

    出栈:当Q1为非空时,从Q1移n-1个元素到Q2中,对Q1最后一个元素执行出队操作;当Q2为非空时,从Q2移n-1个元素到Q1中,对Q2最后一个元素执行出队操作。

  4. 给定一个整数栈,检查栈中每队相邻数字是否是连续的,如果栈中元素为奇数,则忽略栈顶元素。

    public static boolean checkStackPairwiseOrder(Stack<Integer> s) {
        Queue<Integer> q = new LinkedList<Integer>();
        boolean pairwiseOrdered = true;
        while(!s.isEmpty()) {
            q.add(s.pop());
        }
        while(!q.isEmpty()) {
            s.push(q.remove());//逆置了整个栈中元素
        }
        //判断相邻元素是否连续的
        while(!s.isEmpty()) {
            int n = s.pop();
            q.add(n);
            if(!s.isEmpty()) {
                int m = s.pop();
                q.add(m);
                if(Math.abs(n-m)!=1) {
                    pairwiseOrdered = false;
                }
            }
        }
        //恢复栈中数字和顺序
        while(!q.isEmpty()) {
            s.push(q.remove());
        }
        return pairwiseOrdered;
    }
    
  5. 给定一个证书队列,把队列中前半部分和后半部分的元素相互交叉,完成队列中元素的重新排列。

    可以采用一个栈或队列作为中数据转换

    public void interLeavingQueue(Queue<Integer> q) {
        if(q.size() % 2 != 0)
            throw new IllegalArgumentException();
        Stack<Integer> s = new ArrayStack<Integer>();
        int halfSize = q.size() / 2;
        //倒置队列前半部分的元素
        for(int i = 0; i < halfSize; i++) {
            s.push(q.dequeue());
        }
        while(!s.isEmpty()) {
            q.enqueue(s.pop());//前半部分倒过来,并放置到后半部分位置
        }
        for(int i = 0; i < halfSize; i++) {
            q.enqueue(q.dequeue());//恢复位置
        }
        for(int i = 0; i < halfSize; i++) {
            s.push(q.dequeue());//将倒过来的前半部分一次放入栈中
        }
        //交叉排列数据
        while(!s.isEmpty()) {
            q.enqueue(s.pop());
            q.enqueue(q.dequeue());
        }
    }