数据结构与算法 - 双端队列

news2024/11/14 15:15:28

1. 概述

双端队列、队列、栈对比

定义特点
队列一端删除(头),另一端添加(尾)First In First Out
一端删除和添加(顶)Last In First Out
双端队列两端都可以删除、添加
优先级队列优先级高者先出队
延时队列根据延时时间确定优先级
并发非阻塞队列队列空或满时不阻塞
并发阻塞队列队列空时删除阻塞、队列满时添加阻塞

注1:Jav中LinkedList即为典型双端队列实现,不过它同时实现了Queue接口,也提供了栈的push、pop等方法。

注2:不同语言,操作双端队列的方法命名有所不同,参见下表

操作JavaJavaScriptC++LeetCode 641
尾部插入offerLastpushpush_backinsertLast
头部插入offerFirstunshiftpush_frontinsertFront
尾部移除pollLastpoppop_backdeleteLast
头部移除pollFirstshiftpop_frontdeleteFront
尾部获取peekLastat(-1)backgetRear
头部获取peekFirstat(0)frontgetFront


接口定义

package com.itheima.datastructure.deque;

/**
 * 双端队列接口
 * @param <E> 队列中元素类型
 */
public interface Deque<E> {

    boolean offerFirst(E e);

    boolean offerLast(E e);

    E pollFirst();

    E pollLast();

    E peekFirst();

    E peekLast();

    boolean isEmpty();

    boolean isFull();
}

2. 基于双向环形链表实现

package com.itheima.datastructure.Deque;

import java.util.Iterator;

public class LinkedListDeque<E> implements Deque<E>, Iterable<E> {
    static class Node<E> {
        Node<E> prev;
        E value;
        Node<E> next;

        public Node(Node<E> prev, E value, Node<E> next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }

    int capacity;
    int size;
    Node<E> sentinel = new Node<>(null,null, null);

    public LinkedListDeque(int capacity) {
        this.capacity = capacity;
        sentinel.next = sentinel;
        sentinel.prev = sentinel;
    }

    @Override
    public boolean offerFirst(E e) {
        if(isFull()) {
            return false;
        }
        // 处理4个指针
        Node<E> a = sentinel;
        Node<E> b = sentinel.next;
        Node<E> added = new Node<>(a, e, b);
        a.next = added;
        b.prev = added;
        size++;
        return true;
    }

    // a added b
    @Override
    public boolean offerLast(E e) {
        if(isFull()) {
            return false;
        }

        Node<E> b = sentinel;
        Node<E> a = sentinel.prev;
        Node<E> added = new Node<>(a, e, b);
        a.next = added;
        b.prev = added;
        size++;
        return true;
    }

    @Override
    public E pollFirst() {
        if(isEmpty()) {
            return null;
        }

        Node<E> a = sentinel;
        Node<E> removed = sentinel.next;
        Node<E> b = sentinel.next.next;
        a.next = b;
        b.prev = a;
        size--;
        return removed.value;
    }

    @Override
    public E pollLast() {
        if(isEmpty()) {
            return null;
        }

        Node<E> a = sentinel.prev.prev;
        Node<E> removed = sentinel.prev;
        Node<E> b = sentinel;
        a.next = b;
        b.prev = a;
        size--;
        return removed.value;
    }

    @Override
    public E peekFirst() {
        if(isEmpty()) {
            return null;
        }
        return sentinel.next.value;
    }

    @Override
    public E peekLast() {
        if(isEmpty()) {
            return null;
        }

        return sentinel.prev.value;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public boolean isFull() {
        return size == capacity;
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            Node<E> p = sentinel;
            @Override
            public boolean hasNext() {
                return p != sentinel;
            }

            @Override
            public E next() {
                E value = p.value;
                p = p.next;
                return value;
            }
        };
    }
}

3. 基于循环数组实现

package com.itheima.datastructure.Deque;

import java.util.Iterator;

public class ArrayDeque1<E> implements Deque<E>, Iterable<E>{
    E[] array;
    int head;
    int tail;

    public ArrayDeque1(int capacity) {
        array = (E[]) new Object[capacity + 1];  // 留一个空位置
    }
    
    static int inc(int i, int length) {
        if(i + 1 >= length) {
            return 0;
        }
        return i + 1;
    }
    
    static int dec(int i, int length) {
        if(i - 1 < 0) {
            return length - 1;
        }
        return i - 1;
    }

    @Override
    public boolean offerFirst(E e) {
        if(isFull()) {
            return false;
        }
        // head先往前一个位置,再存放元素
        head = dec(head, array.length);
        array[head] = e;
        return true;
    }

    @Override
    public boolean offerLast(E e) {
        if(isFull()) {
            return false;
        }
        // 先存放元素到tail所在位置,再将tail往后一个位置
        array[tail] = e;
        tail = inc(tail, array.length);
        return true;
    }

    @Override
    public E pollFirst() {
        if(isEmpty()) {
            return null;
        }
        // 先获取head位置的元素,然后将其置空,并将head往后移一个位置
        E e = array[head];
        array[head] = null;
        head = inc(head, array.length);
        return e;
    }

    @Override
    public E pollLast() {
        if(isEmpty()) {
            return null;
        }
        // 先将tail的位置往前移一位,获取tail位置的元素并将其置空
        tail = dec(tail, array.length);
        E e = array[tail];
        array[tail] = null;
        return e;
    }

    @Override
    public E peekFirst() {
        if(isEmpty()) {
            return null;
        }
        return array[head];
    }

    @Override
    public E peekLast() {
        if(isEmpty()) {
            return null;
        }
        return array[tail - 1];
    }

    @Override
    public boolean isEmpty() {
        return head == tail;
    }

    @Override
    public boolean isFull() {
        if(tail > head) {
            return tail - head == array.length - 1;
        } else if(tail < head) {
            return head - tail == 1;
        } else {
            return false;
        }
    }

    @Override
    public Iterator<E> iterator() {
        return new Iterator<E>() {
            int p = head;
            @Override
            public boolean hasNext() {
                return p != tail;
            }

            @Override
            public E next() {
                E e = array[p];
                p = inc(p, array.length);
                return e;
            }
        };
    }
}

4. 习题

4.1 二叉树Z字层序遍历

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]

示例 2:

输入:root = [1]
输出:[[1]]

示例 3:

输入:root = []
输出:[]

提示:

  • 树中节点数目在范围 [0, 2000] 内
  • -100 <= Node.val <= 100

解法一:双端队列

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */
class Solution {
    public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }

        LinkedList<TreeNode> queue = new LinkedList<>();
        queue.offer(root); // 根节点入队
        boolean odd = true;  // 判断是否是奇数行
        int c1 = 1;  // 当前层的结点数

        while (!queue.isEmpty()) {
            int c2 = 0;
            LinkedList<Integer> deque = new LinkedList<>();  // 存储当前层
            for (int i = 0; i < c1; i++) {
                TreeNode n = queue.poll();
                if (odd) {
                    deque.offerLast(n.val);  // 尾插
                } else {
                    deque.offerFirst(n.val);  // 头插
                }

                if (n.left != null) {
                    queue.offer(n.left);
                    c2++;
                }
                if (n.right != null) {
                    queue.offer(n.right);
                    c2++;
                }
            }
            c1 = c2;
            odd = !odd;
            result.add(deque);
        }
        return result;
    }
}

4.2 设计双端循环队列

设计实现双端队列。

实现 MyCircularDeque 类:

  • MyCircularDeque(int k) :构造函数,双端队列最大为 k 。
  • boolean insertFront():将一个元素添加到双端队列头部。 如果操作成功返回 true ,否则返回 false 。
  • boolean insertLast() :将一个元素添加到双端队列尾部。如果操作成功返回 true ,否则返回 false 。
  • boolean deleteFront() :从双端队列头部删除一个元素。 如果操作成功返回 true ,否则返回 false 。
  • boolean deleteLast() :从双端队列尾部删除一个元素。如果操作成功返回 true ,否则返回 false 。
  • int getFront() ):从双端队列头部获得一个元素。如果双端队列为空,返回 -1 。
  • int getRear() :获得双端队列的最后一个元素。 如果双端队列为空,返回 -1 。
  • boolean isEmpty() :若双端队列为空,则返回 true ,否则返回 false  。
  • boolean isFull() :若双端队列满了,则返回 true ,否则返回 false 。

示例 1:

输入
["MyCircularDeque", "insertLast", "insertLast", "insertFront", "insertFront", "getRear", "isFull", "deleteLast", "insertFront", "getFront"]
[[3], [1], [2], [3], [4], [], [], [], [4], []]
输出
[null, true, true, true, false, 2, true, true, true, 4]

解释
MyCircularDeque circularDeque = new MycircularDeque(3); // 设置容量大小为3
circularDeque.insertLast(1);			        // 返回 true
circularDeque.insertLast(2);			        // 返回 true
circularDeque.insertFront(3);			        // 返回 true
circularDeque.insertFront(4);			        // 已经满了,返回 false
circularDeque.getRear();  				// 返回 2
circularDeque.isFull();				        // 返回 true
circularDeque.deleteLast();			        // 返回 true
circularDeque.insertFront(4);			        // 返回 true
circularDeque.getFront();				// 返回 4

提示:

  • 1 <= k <= 1000
  • 0 <= value <= 1000
  • insertFrontinsertLastdeleteFrontdeleteLastgetFrontgetRearisEmptyisFull  调用次数不大于 2000 次
class MyCircularDeque {  
    static class Node {  
        Node prev;  
        int value;  
        Node next;  

        public Node(Node prev, int value, Node next) {  
            this.prev = prev;  
            this.value = value;  
            this.next = next;  
        }  
    }  

    int capacity;  
    int size = 0;  
    Node sentinel;  

    public MyCircularDeque(int k) {  
        this.capacity = k;  
        sentinel = new Node(null, 0, null); // Node中应该有一个默认值,这里使用0  
        sentinel.next = sentinel;  
        sentinel.prev = sentinel;  
    }  
    
    public boolean insertFront(int value) {  
        if (isFull()) {  
            return false;  
        }  
        Node added = new Node(sentinel, value, sentinel.next);  
        sentinel.next.prev = added;  
        sentinel.next = added;  
        size++;  
        return true;  
    }  
    
    public boolean insertLast(int value) {  
        if (isFull()) {  
            return false;  
        }  
        Node added = new Node(sentinel.prev, value, sentinel);  
        sentinel.prev.next = added;  
        sentinel.prev = added;  
        size++;  
        return true;  
    }  
    
    public boolean deleteFront() {  
        if (isEmpty()) {  
            return false; // 应该返回 false,而不是 null  
        }  
        Node front = sentinel.next;  
        sentinel.next = front.next;  
        front.next.prev = sentinel;  
        size--;  
        return true;  
    }  
    
    public boolean deleteLast() {  
        if (isEmpty()) {  
            return false; // 应该返回 false,而不是 null  
        }  
        Node rear = sentinel.prev;  
        sentinel.prev = rear.prev;  
        rear.prev.next = sentinel;  
        size--;  
        return true;  
    }  
    
    public int getFront() {  
        if (isEmpty()) {  
            return -1; // 返回-1表示队列为空  
        }  
        return sentinel.next.value;  
    }  
    
    public int getRear() {  
        if (isEmpty()) {  
            return -1; // 返回-1表示队列为空  
        }  
        return sentinel.prev.value;  
    }  
    
    public boolean isEmpty() {  
        return size == 0;  
    }  
    
    public boolean isFull() {  
        return size == capacity;  
    }  
}  

/**  
 * Your MyCircularDeque object will be instantiated and called as such:  
 * MyCircularDeque obj = new MyCircularDeque(k);  
 * boolean param_1 = obj.insertFront(value);  
 * boolean param_2 = obj.insertLast(value);  
 * boolean param_3 = obj.deleteFront();  
 * boolean param_4 = obj.deleteLast();  
 * int param_5 = obj.getFront();  
 * int param_6 = obj.getRear();  
 * boolean param_7 = obj.isEmpty();  
 * boolean param_8 = obj.isFull();  
 */

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1974364.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java整合腾讯云发送短信实战Demo

简介 在现代应用开发中&#xff0c;短信服务是非常重要的功能之一。它可以用于用户验证、通知等各种场景。本文将介绍如何使用Java整合腾讯云短信服务&#xff0c;并提供一个完整的实战示例代码。 环境准备 在开始之前&#xff0c;确保你已经完成以下准备工作&#xff1a; 注…

YOLOv10有效涨点专栏目录

试读篇 YOLOv10改进 | Conv篇 | YOLOv10引入AKConv&#xff08;既轻量又提点&#xff09; YOLOv10改进 | 注意力篇 | YOLOv10改进CBAM注意力机制 手把手教你使用YOLOv10训练自己数据集&#xff08;含环境搭建 、数据集查找、模型训练、测试&#xff09; 卷积篇 &#xff08…

蚓链总结数字化营销线上线下融合发展趋势

蚓链数字化营销平台系统工具通过实战、实践总结数字化营销线上线下融合发展趋势如下&#xff1a; 1. 全渠道营销的深化&#xff1a;随着媒体形式的不断丰富和移动互联网技术的发展&#xff0c;全渠道营销能力将越发重要。企业需要在更多的线上和线下渠道进行布局&#xff0c;实…

优化招聘流程:2024年十大HR工具盘点

本文中提到的工具有 &#xff1a;Moka、腾讯企点、泛微E-office、美洽人事、Workday、ADP Workforce Now、SAP SuccessFactors、金蝶云苍穹、BambooHR、钉钉。 在处理日常人力资源任务时&#xff0c;经常感觉到工作效率不够高&#xff0c;招聘、绩效管理或员工福利这些环节总是…

网络协议 从入门到精通系列讲解 - 总目录

&#x1f466; 作者介绍&#xff1a;Bazinga bingo&#xff0c;专注网络协议基础学习以及协议应用硬核干货分享&#xff0c;潜心修炼&#xff0c;虚心学习&#xff0c;立志将每个网络协议最简单的方式分享大家。 &#x1f4d5; 本文收录于《网络协议 eNSP Wireshark》专栏&am…

图欧学习资源网创站以来的更新日志(截止至2022.5.6)不完全统计

一、网站创立和初步发展 2021年10月28日创建【TUO图欧视频备份站】&#xff0c;当时只有【单词视频】文件夹. 小学、初中、高中、大学、四六级、专四、专八、专升本、考研、考博、托福、雅思、托业、GRE、SAT、GMAT、MBA、新概念等 数量&#xff1a;500个文件&#xff0c;60个…

如何优雅地重装系统:使用Ventoy实现系统安装All In One

在日常工作和学习中&#xff0c;我们偶尔会面临需要在一台电脑上测试或使用多个操作系统的场景。传统的解决方案是制作多个启动盘或者使用PE&#xff0c;不仅耗时而且管理不便。 那么如何高效地在同一USB盘上实现多个系统镜像引导的统一集成呢&#xff1f; Ventoy提供了一个创…

基于双存档模型的多模态多目标进化算法(MMOHEA)求解无人机三维路径规划(MATLAB代码)

一、无人机多目标优化模型 无人机三维路径规划是无人机在执行任务过程中的非常关键的环节&#xff0c;无人机三维路径规划的主要目的是在满足任务需求和自主飞行约束的基础上&#xff0c;计算出发点和目标点之间的最佳航路。 1.1路径成本 无人机三维路径规划的首要目标是寻找…

远程链接另一个主机共享文件

一、打开本地电脑&#xff0c;win键&#xff0c;搜索mstsc&#xff0c;打开远程桌面链接 二、 在对话窗口输入另一台远程主机的ip地址 三、点击显示选项 四、点击本地资源&#xff0c;点击详细信息 五、选择要给另一台远程主机共享的文件夹所在磁盘&#xff0c;点击确定 六、点…

爬虫中常见的加密算法Base64伪加密,MD5加密【DES/AES/RSA/SHA/HMAC】及其代码实现(二)

前文爬虫中常见的加密算法Base64伪加密&#xff0c;MD5加密【DES/AES/RSA/SHA/HMAC】及其代码实现(一&#xff09;-CSDN博客 目录 . AES算法 1. 算法简介 2. JavaScript 实现 3.Python 实现# RC4# 算法简介 JavaScript 实现# Python 实现# Rabbit# 算法简介 JavaSc…

基于改进拥挤距离的多模态多目标优化差分进化(MMODE-ICD)求解无人机三维路径规划(MATLAB代码)

一、无人机多目标优化模型 无人机三维路径规划是无人机在执行任务过程中的非常关键的环节&#xff0c;无人机三维路径规划的主要目的是在满足任务需求和自主飞行约束的基础上&#xff0c;计算出发点和目标点之间的最佳航路。 1.1路径成本 无人机三维路径规划的首要目标是寻找…

3D生物打印咋实现?重组弹性蛋白来助力!

Human-Recombinant-Elastin-Based Bioinks for 3D Bioprinting of Vascularized Soft Tissues是发表于《ADVANCED MATERIALS》上的一篇文章&#xff0c;介绍了一种基于重组人原弹性蛋白的生物墨水&#xff0c;用于3D生物打印复杂软组织。该生物墨水由GelMA和MeTro组成&#xff…

人工智能幻觉:记忆能找到答案吗?

欢迎来到雲闪世界。探索记忆机制如何减轻大型语言模型中的幻觉。 幻觉是事实&#xff0c;不是错误&#xff1b;错误的是基于幻觉的判断。——伯特兰罗素 大型语言模型 (LLM)表现出色&#xff0c;但仍受到幻觉的困扰。特别是对于敏感应用来说&#xff0c;这不是一个小问题&#…

文献阅读:细胞分辨率全脑图谱的交互式框架

文献介绍 文献题目&#xff1a; An interactive framework for whole-brain maps at cellular resolution 研究团队&#xff1a; Daniel Frth&#xff08;瑞典卡罗林斯卡学院&#xff09;、Konstantinos Meletis&#xff08;瑞典卡罗林斯卡学院&#xff09; 发表时间&#xff…

最准的期货指标源码 九稳量化系统 趋势指标公式源码 文华财经指标公式源码 标趋势跟踪 全网最火指标公式

8. “世界上没有任何系统或技术交易方法能永保胜利&#xff0c;将来也不会有。”——这句话提醒投资者不要迷信任何交易系统或方法&#xff0c;要保持灵活和适应市场变化的能力。 9. “巨额利润来源于长期的积少成多或者稳坐不动。”——这句话强调了长期稳健交易的重要性&…

最佳HR软件指南:11款高效管理工具

文章介绍了11款人力资源管理工具&#xff1a;Moka、友人才、北森HRSaaS、同鑫eHR、i人事、红海eHR、BambooHR、Skuad、Hibob、OrangeHRM、Verint。 在选择人力资源管理软件时&#xff0c;选错不仅浪费时间和金钱&#xff0c;还会影响团队的工作效率和员工满意度。本文总结了11款…

24年军队文职选岗报名保姆级流程!

24年技能岗已经开始陆陆续续报名了&#xff0c;不知道具体流程的宝子别着急&#xff0c;给大家总结了详细的报名步骤&#xff0c;直接照着选就行&#xff01; ✅详细步骤&#xff1a; 1、在军 队人才网找到工作动态&#xff0c;把岗位表用Excel表格方式下载出来。 2、列出自身条…

不用再重装系统_手把手教你将系统迁移到新硬盘

最近有网友问我怎么将系统迁移到新加的硬盘上面&#xff0c;我们可以使用DiskGenius软件&#xff0c;可以方便地将系统从一个硬盘迁移到另外一个硬盘上&#xff0c;或者更常见的是将系统从硬盘迁移到SSD固态硬盘、U盘等。下面小编就教大家详细的步骤。 系统怎么迁移到新硬盘准备…

python学习之旅(基础篇看这篇足够了!!!)

目录 前言 1.输入输出 1.1 输入 1.2 输出 2. 变量与常量 2.1 变量 2.2 常量 2.3 赋值 2.4格式化输出 3. 数据类型 4. 四则运算 5.“真与假” 5.1 布尔数 5.2 比较运算和逻辑运算 5.3 布尔表达式 6.判断语句 6.1 基本的if语句 6.2 if-else语句 6.3 if-elif-el…

程序员保持健康的 10 个技巧

长时间坐在电脑前&#xff0c;整天甚至通宵编程、处理 bug 和面对 dealine 的压力。作为一名软件工程师绝对不是一个非常健康的职业。 我经常去欧洲和美国会见许多开发人员。我经常注意到的是&#xff1a;许多开发人员把自己当成机器。他们已经完全放弃了感受身体的感觉&#…