面试题精选:循环队列

简介: 面试题精选:循环队列

近期在面试找工作的小伙伴们很多啊,我周围就有好几个认识的朋友在找工作,于是我突发奇想在CSDN开了一个面试题精选的专栏,主要会关注一些算法题、设计题,次要会补充一些java面试相关的题(比较本博主是java出身)。其实在此之前已经写过一些相关的文章了,已经整理到专栏里的,后续会持续更新,希望对大家有所帮助,有兴趣的旁友可以关注下。

今天分享的面试题是循环队列,我对这道题记忆深刻,因为我在14年参加来校招面试的时候,二面面试官就问了这道题,当时我没有完全答上来(不过面试官居然给我过了),后来我当面试官的时候也拿这道题考过别人,也没遇到能彻底答出来的。不巧,前两天又在看别人文章的时候遇到了循环队列,索性就来自己写一下。

有时候虽然面试官表面上是考一道很简单的题,但背后却想着暗度陈仓、凭空……(对不起放错了)。比如这道循环队列,面试官一上来可能不会直接问循环队列,而是让你实现一个有界队列,如果你没一下子想到最优的方案,不要慌还有机会,一名合格的面试官会尝试引导你的思考,比如让你做做复杂度分析,分析下优缺点,最后编码实现下。就拿这题来说,首先考察了基本的数据结构队列,再考察算法复杂度分析的能力,还考察下你归纳总结的能力、表达能力,最后考察下编码能力。

有界队列:指存储有限个元素的队列,当队列满之后就不能往其中添加了。与之相对的有无界队列,无界队列可以无限的往其中添加元素,在实际使用中往往要注意避免OOM。

回到面试题上,给你一个有界队列的接口定义,你来实现一个有界队列,接口如下:

复制

public interface BoundedQueue {
    boolean add(int e);
    int peek();
    int poll();
    boolean isEmpty();
    boolean isFull();
}

我面过的一个候选人,当时他给出下面的实现方案。

复制

public class MyBoundedQueue implements BoundedQueue {
    private int tail = 0;
    private int[] arr;
    public MyBoundedQueue(int cap) {
        this.arr = new int[cap];
    }
    @Override
    public boolean add(int e) {
        if (isFull()) {
            return false;
        }
        arr[tail++] = e;
        return true;
    }
    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return arr[0];
    }
    @Override
    public int poll() {
        if (isEmpty()) {
            return -1;
        }
        int res = arr[0];
        for (int i = 0; i < tail-1; i++) {
            arr[i] = arr[i+1];
        }
        tail--;
        return res;
    }
    @Override
    public boolean isEmpty() {
        return tail == 0;
    }
    @Override
    public boolean isFull() {
        return tail == arr.length;
    }
}

看起来思路很清晰,是实现了有界队列的接口。他的思路是有个指针tail一直指向队尾第一个空位置,如果有插入就插到tail的位置,tail并往后移动。然后根据tail的具体位置来判断队列是满是空,入队如下图示。

我们来重点看下出队,他每次出队都是出第0位的元素,为了准确性必须保证arr[0]始终是队头,所以在每次出队的时候都将后面的所有元素往前移动一位,导致出队的时间复杂度变成了O(n),如下图所示。

有没有办法做到不用把后面的元素往前移动呢,其实有的,我们只需要用一个head指针指向队列的第一个可用元素前的空位即可,如下图。

如果队列里的元素不动,那我们就得让队头指针(head)动起来,每删除poll()一次,队头指针就往后移动一次,因为只需要移动指针,所以时间复杂度是O(1)。但是我们又有了新的问题,队头指针前面的那些空位置我们如何再利用起来?

这里我们就引出了循环队列,你把上图想象成一根软管,可以掰弯然后首尾相连,如下图。

当然这里我们使用数组实现了,只能做到逻辑上首尾相连,插入时如果插到了最后一格就直接跳回到第一格子。但如果你用链表实现的话,就很容易实现首尾相连了。

接下来就是难点了,上面的都比较容易理解,但好多人就是不知道如何判定队列是空是满(我当年面试的时候也是这种情况)。你有没有发现上文中我头指针和尾指针都是指向空的,不是说我喜欢这样,而是为了方便判断空和满。 注意看上文中的图,我只在头指针和尾指针之间的空格处存东西,所以当队列是空的时候,循环队列按顺时针方向,头指针在前尾指针在后,且二者相邻,如下图。

如果队列满,首尾指针是指向同一个位置,如下图。

所有的问题我们都通过图示的方式展示清楚了,下面我给出我用数组实现的代码。

复制

public class CircularBoundedQueue implements BoundedQueue {
    private int head = 0;
    private int tail = 1;
    private int[] arr;
    public CircularBoundedQueue(int cap) {
        this.arr = new int[cap+1]; 
    }
    @Override
    public boolean add(int e) {
        if (isFull()) {
            return false;
        }
        arr[tail] = e;
        tail = next(tail);
        return true;
    }
    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return arr[head+1];
    }
    @Override
    public int poll() {
        if (isEmpty()) {
            return -1;
        }
        return arr[head = next(head)];
    }
    @Override
    public boolean isEmpty() {
        return tail == next(head);
    }
    @Override
    public boolean isFull() {
        return head == tail;
    }
    private int next(int cur) {
        return (cur+1)%arr.length;
    }
}

用单链表实现的代码如下:

复制

public class CircularBoundedQueue2 implements BoundedQueue {
    private class Node {
        int value;
        Node next;
    }
    private Node head;
    private Node tail;
    public CircularBoundedQueue2(int cap) {
        Node p = new Node();
        head = p;
        for (int i = 0; i < cap; i++) {
            Node newNode = new Node();
            p.next = newNode;
            p = p.next;
        }
        p.next = head; // 链表最末尾节点next指向头部 
        tail = head.next;
    }
    @Override
    public boolean add(int e) {
        if (isFull()) {
            return false;
        }
        tail.value = e;
        tail = tail.next;
        return true;
    }
    @Override
    public int peek() {
        if (isEmpty()) {
            return -1;
        }
        return head.next.value;
    }
    @Override
    public int poll() {
        if (isEmpty()) {
            return -1;
        }
        head = head.next;
        return head.value;
    }
    @Override
    public boolean isEmpty() {
        return tail == head.next;
    }
    @Override
    public boolean isFull() {
        return head == tail;
    }
}


目录
相关文章
|
前端开发 Java 测试技术
基于Spring boot的图书馆图书借阅管理系统的设计与实现
基于Spring boot的图书馆图书借阅管理系统的设计与实现
4075 0
|
6月前
|
安全 应用服务中间件 网络安全
在Linux环境部署Flask应用并启用SSL/TLS安全协议
至此,你的Flask应用应该能够通过安全的HTTPS协议提供服务了。记得定期更新SSL证书,Certbot可以帮你自动更新证书。可以设定cronjob以实现这一点。
392 10
|
7月前
|
关系型数据库 测试技术 API
NestJS中TypeORM的使用
本文介绍了在 NestJS 中使用 TypeORM 实现数据库的 CRUD 操作,涵盖依赖安装、模块配置、实体定义、服务逻辑、控制器路由及 API 测试等步骤。
235 4
|
9月前
|
人工智能 自然语言处理 Java
效率飙升!3 款免费 AI 神器,让代码编写快到飞起
在快节奏的软件开发中,效率至关重要。本文推荐三款免费AI工具助力开发者:ChatCode基于自然语言生成高质量代码框架;CodeChecker实时检查语法与风格问题,提升代码规范性;飞算JavaAI通过一键生成完整工程代码,大幅缩短开发周期。这些工具从不同角度优化开发流程,让开发者事半功倍。
|
Web App开发 大数据 应用服务中间件
什么是 HTTP Range请求(范围请求)
HTTP Range 请求是一种非常有用的 HTTP 功能,允许客户端请求资源的特定部分,从而提高传输效率和用户体验。通过合理使用 Range 请求,可以实现断点续传、视频流播放和按需加载等功能。了解并掌握 HTTP Range 请求的工作原理和应用场景,对开发高效的网络应用至关重要。
1533 16
|
7月前
|
机器学习/深度学习 数据采集 数据可视化
基于YOLOv8的PCB缺陷检测识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
本项目基于YOLOv8实现PCB缺陷检测,提供一站式解决方案。包含完整训练代码、标注数据集、预训练权重及PyQt5图形界面,支持图片、文件夹、视频和摄像头四种检测模式。项目开箱即用,适合科研、工业与毕业设计。核心功能涵盖模型训练、推理部署、结果保存等,检测类型包括缺孔、鼠咬缺口、开路、短路、飞线和杂铜。项目具备高性能检测、友好界面、灵活扩展及多输入源支持等优势,未来可优化模型轻量化、多尺度检测及报告生成等功能。
基于YOLOv8的PCB缺陷检测识别项目|完整源码数据集+PyQt5界面+完整训练流程+开箱即用!
|
12月前
|
传感器 监控 物联网
智慧家居环境监测与控制系统研发与应用的目标分析
- **背景**:随着物联网技术的发展和智能家居市场的快速增长,人们对居住环境的舒适性、安全性及能源使用效率的要求日益提高。 - **目的**:通过研发和应用智慧家居环境监测与控制系统,实现住宅环境中温度、湿度、空气质量等关键参数的有效管理和自动化调节。
689 21
|
11月前
|
机器学习/深度学习 资源调度 数据可视化
YOLOv11改进策略【注意力机制篇】| 引入Shuffle Attention注意力模块,增强特征图的语义表示
YOLOv11改进策略【注意力机制篇】| 引入Shuffle Attention注意力模块,增强特征图的语义表示
541 1
YOLOv11改进策略【注意力机制篇】| 引入Shuffle Attention注意力模块,增强特征图的语义表示
|
机器学习/深度学习 监控 算法
车辆违停检测:基于计算机视觉与深度学习的自动化解决方案
随着智能交通技术的发展,传统人工交通执法方式已难以满足现代城市需求,尤其是在违法停车监控与处罚方面。本文介绍了一种基于计算机视觉和深度学习的车辆违停检测系统,该系统能自动监测、识别并报警违法停车行为,大幅提高交通管理效率,降低人力成本。通过使用YOLO算法进行车辆检测,结合区域分析判断车辆是否处于禁停区,实现了从车辆识别到违停判定的全流程自动化。此系统不仅提升了交通管理的智能化水平,也为维护城市交通秩序提供了技术支持。
|
机器学习/深度学习 计算机视觉 Python
【YOLOv11改进 - 注意力机制】EMA(Efficient Multi-Scale Attention):基于跨空间学习的高效多尺度注意力
【YOLOv11改进 - 注意力机制】EMA(Efficient Multi-Scale Attention):基于跨空间学习的高效多尺度注意力.EMA(Efficient Multi-Scale Attention)模块是一种高效多尺度注意力机制,旨在提高计算机视觉任务中的特征表示效果。该模块通过结合通道和空间信息、采用多尺度并行子网络结构以及优化坐标注意力机制,实现了更高效和有效的特征表示。EMA模块在图像分类和目标检测任务中表现出色,使用CIFAR-100、ImageNet-1k、MS COCO和VisDrone2019等数据集进行了广泛测试。
【YOLOv11改进 - 注意力机制】EMA(Efficient Multi-Scale Attention):基于跨空间学习的高效多尺度注意力