数据结构与算法分析学习笔记(五)队列

简介: 数据结构与算法分析学习笔记(五)队列

引言

队列在日常生活中时一个十分常见的名词,比如去食堂打饭时排队,排在最前面的总是最先取到餐。最晚到达这个队列往往排在队列的最后面。也就是先进先出。这种排队的特点同样也被引入到了计算机中,也就是消息队列,电商在搞大促销的时候,峰值会是平常的好几倍,系统可能会处理不到位,我们一边将系统扩容,一边准备消息队列,将超过系统处理能力的强求放在消息队列中,系统依次处理消息队列中的请求。这种"先进先出"也别引入到了数据结构中,也就是队列。

队列

由上面的讨论我们可以总结出队列的逻辑结构,数据结构中的队列,即是把一个一个的结点按照某种顺序排列,对其处理的过程和日常生活中的对了是一样的。队列规则为:

  • 排列顺序:  结点排成一队,后来的排在队尾。
  • 处理过程:  在队头处理事件; 队列中有元素一直处理,直至队空或发生中断事件。

队列的定义:

队列(Queue) 是只允许在一端进行操作而在另一端进行删除操作的线性表。删除端被称为"队头",插入端被称入"队尾"。image.png与栈的不同之处在于,队列是一种先进先出(First In First Out  FIFO)的线性表;与栈相同的是,队列也是一种重要的线性结构。根据队列的定义,队列的基本操作有下列这些:

  • 置空队列: 将已有队列置空
  • 判空操作:  若队列为空, 则返回的值为true, 否则为false.
  • 出队操作:  当队列非空时, 返回队列头元素的值, 修改队列头指针。
  • 入队操作: 将结点插入队尾,修改队尾指针
  • 取队头元素操作:  当队列非空时, 返回队列头元素的值,但不删除队列头元素。

栈使用top来指向栈顶元素,便于我们实现入栈出栈操作。那对于队列这种一端进一端出的数据结构,我们就需要两个变量来实现入队和出队操作,一个指向队头,一个指向队尾。我们依然用链式存储结构和顺序存储结构来分别实现队列。

顺序队列

对于顺序队列来说有两种类型,一种是无限制的队列, 自动扩容,对入队元素数量无限制。一种是限制容量的队列。对入队元素数量有限制。对于无限制的队列,我们可以仿照之前做线性表的思路,在原先的基础上进行扩展。在原先的基础上重新提供专属于队列的API,然后入队操作和出队操作在原先的基础上做包装。像下面这样:

public class SequenceQueue extends SequenceList{
    public void offer(Integer data) {
        add(data);
    }
    public Integer remove() {
        return remove(size() - 1);
    }
    public Integer peek() {
        if (size() > 0){
            return get(0);
        }
        return null;
    }
    public boolean empty(){
        return size() == 0;
    }
    // 清空操作 在SequenceList中实现
}
复制代码

那对于容量限制的队列来说就有一个假溢出的问题, 我们举一个例子来说明这个问题,假定限制数组的容量为10,我们每次出队就是让头指针迁移,假设现在头指针已经到了最后,也就是前面的元素已经都出队了,但是有新元素过来仍然不能出队。 即使前面的空间已经废弃掉了,这样队列就只能使用一次,相当鸡肋,所以这种设计存在不合理之处,我们当然不能让队列只使用一次,那我们该如何使用前面的废弃的空间呢? 有两种思路:

  • 队头到队尾的元素向废弃空间平移,但这种方式比较浪费时间。
  • 将存储区域看做一个首尾相接的环形区域,入队时判断队列已经满的时候,这个元素就放置在0位置上。

一般我们采用第二种方式来处理队列空间的重复使用问题,在结构上采用这种技巧来存储的队列被称为循环队列。因为循环队列的元素的空间可以被重复使用,除非队列真的全被占用,否则是不会发生上溢出。基本上除了一些简单的应用外,真正实用的顺序队列是循环队列。

其实在这里依然有两种设计, 第一种设计是如果队列全部被占用,那么拒绝入队, 第二种设计就是覆盖掉之前的元素。其实也可以两种设计都要,提供一个方法给调用者,根据参数的不同来采取不同的策略。其实这也是看源码的意义之一,理解设计者的设计思路。下面是我实现的一个简单循环队列:

public class CircularQueue {
    private int start; 
    private int end;
    private Integer[] elements;
    private int maxElements;
    private boolean full;
    public CircularQueue(int size) {
        elements = new Integer[size];
        maxElements = size;
    }
    public boolean offer(final Integer element){
        if(element == null){
            // 禁止空元素加入
        }
        // 如果队列已经满了,则
        if (isAtFullCapacity()){
            remove();
        }
        return false;
    }
    public Integer poll(){
        if (isEmpty()){
            // 或给提示
            return null;    
        }
        return remove();
    }
    /**
     * 移除元素
     */
    private Integer remove() {
        if (isEmpty()){
          // 给出提示
        }
        Integer element = elements[start];
        if (null != element){
            elements[start++] = null;
            // 说明已经全面出队
            if (start >= maxElements){
                start = 0;
            }
        }
        return element;
    }
    public boolean isEmpty(){
        return size() == maxElements;
    }
    public boolean isAtFullCapacity() {
        return size() == maxElements;
    }
    public Integer peek(){
        if (size() == 0){
            return null;
        }
        return elements[start];
    }
    // 如何计算size
    // 循环队列, end到达最后被清置0
    // 所以start完全可以大于end
    // 刚开始start == end
    // 在一种情况就是 刚超出被覆盖掉
    public int size(){
        int size = 0;
        // 说明
        if (end  < start){
            size = maxElements - start + end;
        }else if (end == start){
            size = this.full? maxElements:0;
        }else {
            size = end - start;
        }
        return size;
    }
    public boolean empty(){
        return  size() == maxElements;
    }
    public void clear(){
        full = false;
        start = 0;
        end = 0;
    }
}
复制代码

链式队列

队列是运算受限的线性表,线性表有两种方式进行存储一种是数组,一种是链表。用链表实现的队列我们一般称之为链队列。 Java中也封装了队列这种数据结构,Queue是接口,LinkedList是他的一个实现类,我们来大致的看一下LinkedList:image.pngDeque 是双向队列,像是栈和队列的集合体,普通队列只能一端进,一端出。而双向队列就可以做到一端进一端出。我们在设计数据结构也可以参考LinkedList的设计。我们再讨论一下队列的结构,顺序队列的相邻可以依靠数组的下标,那么对于链式队列来说就需要再有一个变量来维持各个结点的相邻了。所以链式队列的存储结构就像下面这样:image.png代码实现如下:

public class LinkedQueue {
    private Node first;
    private Node  last;
    private int size;
    public LinkedQueue() {
    }
    private class Node{
        private int data;
        private Node next;
        public Node(int data , Node next){
            this.data = data;
            this.next = next;
        }
        public int getData() {
            return data;
        }
        public Node getNext() {
            return next;
        }
    }
    public boolean empty(){
        return size == 0;
    }
    public void clear(){
        first = null;
        last = null;
        size = 0;
    }
    /**
     * 入队操作
     * @param data
     * @return
     */
    public boolean offer(Integer data) {
        Node l = last;
        Node node = new Node(data,null);
        if (l == null){
            first = node;
        }else {
            last.next = node;
        }
        last = node;
        size++;
        return true;
    }
    /**
     * 可扩展该方法, 当头结点为空时, 可返回null,而不是抛出异常
     * @return
     */
    public Integer remove() {
        Node f = first;
        if (f == null){
            // 队列为空 给出警告
        }
        first = first.next;
        f = null; // help GC
        size--;
        return first.data;
    }
    public Integer peek() {
         Node f = first;
        return f.data;
    }
}


相关文章
|
4月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
374 9
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
158 77
|
3天前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
16 4
|
16天前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
11天前
|
缓存 监控 算法
内网监控管理软件:PHP 语言队列算法揭秘
在数字化办公环境中,内网监控管理软件对企业的稳定运行和信息安全至关重要。本文深入介绍PHP中的队列算法及其在内网监控软件中的应用,包括监控数据收集、任务调度和日志记录等场景,通过代码示例展示其实现方法。队列算法可提高性能、保证数据顺序并实现异步处理,为企业提供高效的安全保障。
21 1
|
2月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
50 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
2月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
51 9
|
2月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
43 7
|
4月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
104 5
|
4月前
|
算法 安全 NoSQL
2024重生之回溯数据结构与算法系列学习之栈和队列精题汇总(10)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第3章之IKUN和I原达人之数据结构与算法系列学习栈与队列精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!