数据结构之队列

💕"

开朗些,勇敢些

"💕

作者:Mylvzi

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

一.队列

1.概念

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

2.java中的队列

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

源码

二.队列的模拟实现

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

java 复制代码
    /**
     * 实现队尾进,队头出
     */

    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:使用计数器

java 复制代码
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:预留一个空间

java 复制代码
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.使用两个队列

利用两个队列实现栈

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

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

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

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

java 复制代码
/**
 * 用队列实现栈  使用两个队列实现栈
 * 队列:先进先出  栈:后进先出
 * 要明白,一个队列无法实现栈,他们有本质上的区别
 * 入栈:入到不为空的队列之中  如果两个都为空,入到第一个队列之中
 * 出栈:出栈顶元素,后进先出  对应的是队列中的队尾元素  将队尾元素之前的所有元素转移到另一个队列之中,再返回队尾元素
 */
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.使用一个队列

java 复制代码
/**
 * 思路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的位置

相关推荐
武昌库里写JAVA39 分钟前
【Java】Java面试题笔试
c语言·开发语言·数据结构·算法·二维数组
一休哥助手2 小时前
Redis 五种数据类型及底层数据结构详解
数据结构·数据库·redis
苏宸啊2 小时前
顺序表及其代码实现
数据结构·算法
lin zaixi()2 小时前
贪心思想之——最大子段和问题
数据结构·算法
夜雨翦春韭2 小时前
【代码随想录Day30】贪心算法Part04
java·数据结构·算法·leetcode·贪心算法
一直学习永不止步3 小时前
LeetCode题练习与总结:H 指数--274
java·数据结构·算法·leetcode·数组·排序·计数排序
Amor风信子3 小时前
华为OD机试真题---跳房子II
java·数据结构·算法
Ljubim.te4 小时前
软件设计师——数据结构
数据结构·笔记
_GR5 小时前
每日OJ题_牛客_牛牛冲钻五_模拟_C++_Java
java·数据结构·c++·算法·动态规划
无限大.5 小时前
c语言实例
c语言·数据结构·算法