队列

与栈一样,队列也是最基本的数据结构之一。 队列也是对象的一种容器,其中对象的插入和删除遵循“先进先出”(First-In-First-Out, FIFO)的原则--也就是说,每次删除的只能是最先插入的对象。 因此,我们可以想象成将对象追加在队列的后端,而从其前端摘除对象。 就这一性质而言,队列与栈堪称“孪生兄弟”。 比如,在银行等待接受服务时,顾客们就会排成一个队列--最先到达者优先得到服务。 再如,用球桶来乘放羽毛球时,所有的球也排成一个队列--你总是从一端将球取出,而从另一端把球放入。

队列ADT(AbstractDataType)

队列的抽象数据类型就是一个容器,其中的对象排成一个序列, 我们只能访问和取出排在最前端(Front)的对象, 只能在队列的尾部(Rear)插入新对象。正是按照这一规则, 才能保证最先被插入的对象首先被删除(FIFO)。

队列ADT 首先支持下面的两个基本方法:

操作方法 功能描述
enqueue(x) 将元素x 加到队列末端
输入:一个对象
输出:无
dequeue() 若队列非空,则将队首元素移除,并将其返回否则,报错
输入:无
输出:对象
size() 返回队列中当前包含的元素数目
输入:无
输出:非负整数
isEmpty() 检查队列是否为空
输入:无
输出:布尔标志
front() 若队列非空,则返回队首元素(但并不移除)否则,报错
输入:无
输出:队头对象

队列的应用十分广泛,无论是商店、剧院、机场还是银行、医院,凡是按照“先到的客户优先接受服务”原则的场合,都可以利用队列这一数据结构来编排相应的服务。

基于定长循环数组的队列的实现

面试中关于队列的常见问题

  • 使用队列表示栈
  • 对队列的前k个元素倒序
  • 使用队列生成从1到n的二进制数
  • 循环分配器

队列结构很适于用来实现循环分配器:按照环形次序反复循环, 为共享某一资源的一群客户(比如共享一个CPU 的多个应用程序) 做资源分配。

算法:RoundRobin
{
    e = Q.dequeue();
    Serve(e);
    Q.enqueue(e);
}
  • osephus 环 (约瑟夫环)

孩提时的你是否玩过“烫手山芋”游戏:一群小孩围成一圈, 有一个刚出锅的山芋在他们之间传递。其中一个孩子负责数数, 每数一次,拿着山芋的孩子就把山芋转交给右边的邻居。 一旦数到某个特定的数,拿着山芋的孩子就必须退出,然后重新数数。 如此不断,最后剩下的那个孩子就是幸运者。

通常,数数的规则总是从1 开始,数到k 时让拿着山芋的孩子出列, 然后重新从1 开始。Josephus问题可以表述为:n 个孩子玩这个游戏, 最后的幸运者是谁?

为了解答这个问题,我们可以利用队列结构来表示围成一圈的n个孩子。 一开始,假定对应于队列首节点的那个孩子拿着山芋。 然后,按照游戏的规则,把“土豆”向后传递到第k个孩子 (交替进行k次dequeue()和k次enqueue()操作), 并让她出队(dequeue())。如此不断迭代,直到队长 (getSize())为1。

/*
 * Josephus环
 */
import dsa.*;
import java.io.*;

//模拟Josephus环
public class Josephus {

    // 利用队列结构模拟Josophus环
    public static Object Josephus(Queue Q, int k) {
        if (Q.isEmpty())
            return null;
        while (Q.getSize() > 1) {// 不断迭代
            Q.Traversal();// 显示当前的环
            for (int i = 0; i < k; i++)
                // 将山芋向前传递k次
                Q.enqueue(Q.dequeue());
            Object e = Q.dequeue();// 拿着山芋的孩子退出
            System.out.println("\n\t" + e + "退出");
        }
        return Q.dequeue();// 最后剩下的那个孩子
    }

    // 将一组对象组织为一个队列
    public static Queue buildQueue(Object a[]) {
        Queue Q = new Queue_Array();
        for (int i = 0; i < a.length; i++)
            Q.enqueue(a[i]);
        return Q;
    }

    // 测试用main方法
    public static void main(String[] args) {
        String[] kid = { "Alice", "Bob", "Cindy", "Doug", "Ed", "Fred", "Gene",
                "Hope", "Irene", "Jack", "Kim", "Lance", "Mike", "Nancy",
                "Ollie" };
        System.out.println("最终的幸运者是" + Josephus(buildQueue(kid), 5));
    }
}