数据结构(二) -- 队列

一,队列

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



二,队列ADT(AbstractDataType)

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

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

操作方法 功能描述
enqueue(x) 将元素x 加到队列末端
输入:一个对象
输出:无
dequeue() 若队列非空,则将队首元素移除,并将其返回否则,报错
输入:无
输出:对象

此外,与栈ADT 类似地,队列ADT 还支持如下的方法:

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

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


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

定义一个Queue 接口

/*
* 队列接口
*/
package dsa;
public interface Queue {
  public int getSize();//返回队列中元素数目
  public boolean isEmpty();//判断队列是否为空
  public Object front()throws ExceptionQueueEmpty;//取队首元素(但不删除)
  public void enqueue (Object obj)throws ExceptionQueueFull;//入队
  public Object dequeue()throws ExceptionQueueEmpty;//出队
  public void Traversal();//遍历
}

定义用到的两个异常:
一个Queue空异常

package dsa;
    public class ExceptionQueueEmpty extends RuntimeException {
    public ExceptionQueueEmpty (String err) {
        super(err);
    }
}

一个Queue满异常

package dsa;
    public class ExceptionQueueFull extends RuntimeException {
    public ExceptionQueueFull (String err) {
        super(err);
    }
}

循环数组
为了避免数组的整体移动,可以引入如下两个变量f 和r:

  • f:始终等于Q 的首元素在数组中的下标,即指向下次出队元素的位置
  • r:始终等于Q 的末元素的下标加一,即指向下次入队元素的位置

一开始,f = r = 0,此时队空。每次有对象入队时,将其存放于Q[r],然后r 加一,以指向下一单元。对称地,每次有对象出队之后,也将f 加一,指向新的队首元素。这样,对front()、enqueue()和dequeue()方法的每一次调用都只需常数时间。

然而,这还不够。细心的读者或许已经注意到,按照上述约定,在队列的生命期内,f 和r 始终在单调增加。因此,若队列数组的容量为N,则在经过N 次入队操作后,r 所指向的单元必然超出数组的范围;在经过N 次出队操作后,f 所指向的单元也会出现类似的问题。

解决上述问题的一种简便方法,就是在每次f 或r 加一后,都要以数组的长度做取模运算,以保证其所指单元的合法性。就其效果而言,这就相当于把数组的头和尾相联,构成一个环状结构。

具体的实现:

public class Queue_Array implements Queue {
    public static final int CAPACITY = 1000;// 数组的默认容量
    protected int capacity;// 数组的实际容量
    protected Object[] Q;// 对象数组
    protected int f = 0;// 队首元素的位置
    protected int r = 0;// 队尾元素的位置

    // 构造方法(空队列)

    public Queue_Array() {
        this(CAPACITY);
    }

    // 按指定容量创建对象
    public Queue_Array(int cap) {
        capacity = cap;
        Q = new Object[capacity];
    }

    // 查询当前队列的规模
    public int getSize() {
        return (capacity - f + r) % capacity;
    }

    // 判断队列是否为空
    public boolean isEmpty() {
        return (f == r);
    }

    // 入队
    public void enqueue(Object obj) throws ExceptionQueueFull {
        if (getSize() == capacity - 1)
            throw new ExceptionQueueFull("Queue overflow.");
        Q[r] = obj;
        r = (r + 1) % capacity;
    }

    // 出队
    public Object dequeue() {
        Object elem;
        if (isEmpty())
            throw new ExceptionQueueEmpty("意外:队列空");
        elem = Q[f];
        Q[f] = null;
        f = (f + 1) % capacity;
        return elem;
    }

    // 取(并不删除)队首元素
    public Object front() throws ExceptionQueueEmpty {
        if (isEmpty())
            throw new ExceptionQueueEmpty("意外:队列空");
        return Q[f];

    }

    // 遍历(不属于ADT)
    public void Traversal() {
        for (int i = f; i < r; i++)
            System.out.print(Q[i] + " ");
        System.out.println();
    }
}

四,队列应用实例

  • ** 循环分配器 **
    队列结构很适于用来实现循环分配器:按照环形次序反复循环,为共享某一资源的一群客户(比如共享一个CPU 的多个应用程序)做资源分配。
算法:RoundRobin
{
    e = Q.dequeue();
    Serve(e);
    Q.enqueue(e);
}
  • ** Josephus 环 (约瑟夫环)**
    孩提时的你是否玩过“烫手山芋”游戏:一群小孩围成一圈,有一个刚出锅的山芋在他们之间传递。其中一个孩子负责数数,每数一次,拿着山芋的孩子就把山芋转交给右边的邻居。一旦数到某个特定的数,拿着山芋的孩子就必须退出,然后重新数数。如此不断,最后剩下的那个孩子就是幸运者。
    通常,数数的规则总是从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));
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • 一、数据结构绪论 逻辑结构与物理结构逻辑结构:集合、线性(一对一)、树(一对多)、图(多对多)物理结构:顺序存储结...
    scarqin阅读 2,523评论 0 3
  • 课程介绍 先修课:概率统计,程序设计实习,集合论与图论 后续课:算法分析与设计,编译原理,操作系统,数据库概论,人...
    ShellyWhen阅读 2,252评论 0 3
  • 3.1❶若按教科书3.1.1节中图3.1(b)所示铁道进行车厢调度(注意:两侧铁道均为单向行驶道),则请回答: (...
    云时之间阅读 2,163评论 0 3
  • 本文内容取自于小甲鱼的数据结构与算法。http://www.jianshu.com/p/230e6fde9c75 ...
    阿阿阿阿毛阅读 2,867评论 0 7
  • 我喜欢你喜欢你的一切我不管我就是喜欢你我喜欢你我喜欢你!!!!!!!!!!! 不管我有多少情敌我就是喜欢你喜欢你喜...
    一只鸢阅读 88评论 0 0