数据结构之队列

简介: 数据结构之队列

💕"

开朗些,勇敢些

"💕

作者:Mylvzi

文章主要内容:数据结构之队列及其面试题

一.队列

1.概念

 队列:只允许在一端进行数据的插入(队尾),在另一端进行数据的删除(队头)的特殊线性表,和栈不同的是,队列具有先进先出的特性(FIFO)

2.java中的队列

 Java中队列是一个接口,底层是通过链表实现的;既然是接口,就不能直接实例化对象,要利用实现queue接口的类去实例化对象

源码

二.队列的模拟实现

 队列的底层是是LinkdList,所以可以使用双向链表实现队列

/**
     * 实现队尾进,队头出
     */
    public void offer(int x) {
        ListNode node = new ListNode(x);
        if (rear == null) {
            front = rear = node;
            this.usedSize++;
            return;
        }
        rear.next = node;
        node.prev = rear;
        rear = node;
        this.usedSize++;
    }
    public int poll() {
        if (front == null) {
            throw new QueueEmptyException("队列为空,无法返回数据");
        }
        int ret = front.val;
        // 如果只有一个节点  要把front和rear都置空  否则在执行front.prev = null这句代码时会报错
        if (front.next == null) {
            front = null;
            rear = null;
            return ret;
        }
        front = front.next;
        front.prev = null;
        this.usedSize--;
        return ret;
    }
    public int peek() {
        if (front == null) {
            throw new QueueEmptyException("队列为空,无法返回数据");
        }
        int ret = front.val;
        return ret;
    }
    public int size() {
        return this.usedSize;
    }
    public boolean empty() {
        return this.usedSize==0;
    }
}

3.Deque

 Deque也是Java中和队列有关的数据结构,它是一种双端队列,即队头,队尾都可以实现数据插入与删除,deque 是 “double ended queue” 的简称。同时Deque也是Java中最常用的queue结构  

源码:

4.循环队列的实现

所谓循环队列就是front和rear一直在绕着圆走,和普通队列不一样的是,它可以重复利用空间

循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

一般的队列

循环队列

循环队列的难点在于如何判满,这里我们提供两种方法

循环队列的实现1:使用计数器

class MyCircularQueue {
    private int[] elem;
    private int front;
    private int rear;
    private int cnt = 0;
    public MyCircularQueue(int k) {
        this.elem = new int[k];
    }
    public boolean enQueue(int value) {
        // 插入数据
        if(isFull()) {
            return false;
        }
        this.elem[rear] = value;
        rear = (rear+1) % elem.length;
        cnt++;
        return true;
    }
    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        front = (front+1) % elem.length;
        cnt--;
        return true;
    }
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return this.elem[front];
    }
    public int Rear() {
               if (isEmpty()) {
            return -1;
        }
        int index = (rear == 0) ? (elem.length-1) : (rear-1);
        return this.elem[index];
    }
    public boolean isEmpty() {
        return cnt == 0;
    }
    public boolean isFull() {
        return cnt == this.elem.length;
    }
}

循环队列的实现2:预留一个空间

class MyCircularQueue {
    private int[] elem;
    private int front;
    private int rear;
    public MyCircularQueue(int k) {
        this.elem = new int[k+1];
    }
    public boolean enQueue(int value) {
        // 插入数据
        if(isFull()) {
            return false;
        }
        this.elem[rear] = value;
        rear = (rear+1) % elem.length;
        return true;
    }
    public boolean deQueue() {
        if(isEmpty()) {
            return false;
        }
        front = (front+1) % elem.length;
        return true;
    }
    public int Front() {
        if(isEmpty()) {
            return -1;
        }
        return this.elem[front];
    }
    public int Rear() {
                if(isEmpty()) {
            return -1;
        }
        int index = (rear == 0) ? (elem.length-1) : (rear-1);
        return this.elem[index];
    }
    public boolean isEmpty() {
        return rear == front;
    }
    public boolean isFull() {
        return (rear+1) % elem.length == front;
    }
}

注意:为了实现循环,rear和front的变化不能直接写成rear++或者front++,否则会发生越界

rear = (rear+1) % elem.length;

front = (front+1) % elem.length;

用队列实现栈

225. 用队列实现栈 - 力扣(LeetCode)

1.使用两个队列

 利用两个队列实现栈

如果是入栈,入到不为空的队列之中

如果是出栈,先找到不为空的队列,将队尾之前的所有元素出到另一个队列之中,最后再出队尾元素,实现后进先出

如果是返回栈顶元素,和出栈的思路一样,栈顶就是不为空队列的队尾

栈为空:当两个队列都为空时,栈为空

/**
 * 用队列实现栈  使用两个队列实现栈
 * 队列:先进先出  栈:后进先出
 * 要明白,一个队列无法实现栈,他们有本质上的区别
 * 入栈:入到不为空的队列之中  如果两个都为空,入到第一个队列之中
 * 出栈:出栈顶元素,后进先出  对应的是队列中的队尾元素  将队尾元素之前的所有元素转移到另一个队列之中,再返回队尾元素
 */
class MyStack2 {
    private Queue<Integer> que1;
    private Queue<Integer> que2;
    public MyStack2() {
        this.que1 = new LinkedList<>();
        this.que2 = new LinkedList<>();
    }
    public void push(int x) {
        // 入栈到不为空的队列之中
        if(!que1.isEmpty()) {
            que1.offer(x);
        } else if (!que2.isEmpty()) {
            que2.offer(x);
        }else {
            que1.offer(x);
        }
    }
    public int pop() {
        // 出栈
        // 为空  直接返回
        if (empty()) {
            return -1;
        }
        if (!que1.isEmpty()) {
            int size = que1.size();
            for (int i = 0; i < size - 1; i++) {
                // 将que1队尾元素前面的所有元素转移到que2中
                que2.offer(que1.poll());
            }
            return que1.poll();
        } else {
            int size = que2.size();
            for (int i = 0; i < size - 1; i++) {
                // 将que2队尾元素前面的所有元素转移到que1中
                que1.offer(que2.poll());
            }
            return que2.poll();
        }
    }
    public int top() {
        // 为空  直接返回
        if (empty()) {
            return -1;
        }
        int tmp = -1;
        if (!que1.isEmpty()) {
            int size = que1.size();
            for (int i = 0; i < size; i++) {
                // 将que1队尾元素前面的所有元素转移到que2中
                tmp = que1.poll();
                que2.offer(tmp);
            }
            return tmp;
        } else {
            int size = que2.size();
            for (int i = 0; i < size - 1; i++) {
                // 将que2队尾元素前面的所有元素转移到que1中
                tmp = que2.poll();
                que1.offer(tmp);
            }
            return tmp;
        }
    }
    public boolean empty() {
        // 当两个队列都为空时,栈就为空
        return que1.isEmpty() && que2.isEmpty();
    }
}

2.使用一个队列

/**
 * 思路2:使用一个队列实现栈
 * 栈:后进先出   队列:先进后出
 * 保证队头的元素时最后一个入栈的
 * 新入一个元素的时候将之前所有元素都poll出去,再重新入队,那么新元素就是队头了
 * 这样就实现了后进先出
 */
class MyStack2 {
    Queue<Integer> queue;
    public MyStack2() {
        this.queue = new LinkedList<>();
    }
    public void push(int x) {
        int n = queue.size();
        queue.offer(x);
        for (int i = 0; i < n; i++) {
            // 重新入队
            queue.offer(queue.poll());
//            int ret = queue.poll();
//            queue.offer(ret);
        }
    }
    public int pop() {
        return queue.poll();
    }
    public int top() {
        return queue.peek();
    }
    public boolean empty() {
        return queue.isEmpty();
    }
}

可以带入数据尝试一下  之所以需要%N,是因为rear有可能处于下标为0的位置

目录
相关文章
|
11月前
|
C语言
【数据结构】栈和队列(c语言实现)(附源码)
本文介绍了栈和队列两种数据结构。栈是一种只能在一端进行插入和删除操作的线性表,遵循“先进后出”原则;队列则在一端插入、另一端删除,遵循“先进先出”原则。文章详细讲解了栈和队列的结构定义、方法声明及实现,并提供了完整的代码示例。栈和队列在实际应用中非常广泛,如二叉树的层序遍历和快速排序的非递归实现等。
892 9
|
6月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
168 1
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
9月前
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
344 77
|
8月前
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
172 11
|
8月前
|
DataX
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
9月前
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
248 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
9月前
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
143 9
|
9月前
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
197 7
|
11月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
292 5