数据结构与算法——Java实现 27.双端队列

news2024/11/26 22:22:31

很多人觉得做一件事付出了10分的努力,却只得到5分的汇报。

其实剩下的五分,是在填补你过往的懒惰。

只有将过往的懒惰填满,

努力才会有正向结果

                                                                                        —— 24.10.3

一、概述

双端队列、队列、栈对比:

队列

        一端删除(头),一段添加(尾)        先进先出(FIFO)

        一端删除和添加        后进先出(LIFO)

双端队列

        队列的两端都可以添加、删除


二、双端队列——双向环形链表实现

双端队列要求队头队尾都可以添加和删除元素

所以为了使所有节点都方便计算,于是添加两个指针prevnext,一个指向每个节点的前一个元素,另一个指向一个每个节点的后一个元素,这样在队列头尾删除或添加元素时,只需要通过节点前后节点的指向,快捷删除

定义哨兵节点sentinel,根据哨兵节点前后指针的指向,进行添加和删除

接口

public interface Deque<E> {
    /*
        向队列头部添加元素
        Returns:bool类型,添加是否成功
     */
    boolean offerFirst(E e);

    /*
        向队列尾部添加元素
        Returns:bool类型,添加是否成功
    */
    boolean offerLast(E e);

    /*
        从队列的头部删除元素
        Returns:返回删除的元素
    */
    E pollFirst();

    /*
        从队列的尾部删除元素
        Returns:返回删除的元素
    */
    E pollLast();

    /*
        从队列的头部获取元素
        Returns:获取头部的元素
    */
    E peekFirst();

    /*
        从队列的尾部获取元素
        Returns:获取尾部的元素
    */
    E peekLast();

    /*
        判断队列是否为空
        Returns:判断是否为空
    */
    boolean isEmpty();

    /*
        判断队列是否为满
        Returns:判断是否为满
    */
    boolean isFull();
}

环形链表类

import java.util.Iterator;

/*
    基于双向环形链表实现双端队列
    Type parameters <E> —— 队列中元素类型
 */
public class LinkedListDeque<E> implements Deque<E>,Iterable<E>{
    // 容量
    int capacity;

    // 元素个数
    int size;

    // 哨兵
    Node<E> sentinel = new Node<>(null, null, null);

    // 首部添加
    @Override
    public boolean offerFirst(E e) {
        if (isFull()) {
            return false;
        }
        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;
    }

    // 尾部添加
    @Override
    public boolean offerLast(E e) {
        if (isFull()) {
            return false;
        }
        Node<E> a = sentinel.prev;
        Node<E> b = sentinel;
        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> remove = sentinel.next;
        Node<E> b = sentinel.next.next;
        a.next = b;
        b.prev = a;
        size--;
        return remove.value;
    }

    // 尾部删除
    @Override
    public E pollLast() {
        if(isEmpty()){
            return null;
        }
        Node<E> a = sentinel;
        Node<E> remove = sentinel.prev;
        Node<E> b = sentinel.prev.prev;
        b.next = a;
        a.prev = b;
        size--;
        return remove.value;
    }

    // 首部获取
    @Override
    public E peekFirst() {
        if (isEmpty()) {
            return null;
        }
        Node<E> node = sentinel.next;
        return node.value;
    }

    // 尾部获取
    @Override
    public E peekLast() {
        if (isEmpty()) {
            return null;
        }
        Node<E> node = sentinel.prev;
        return node.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.next;
            @Override
            public boolean hasNext() {
                return p!=sentinel;
            }

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

    // 节点类 prev指针指向上一个节点,next指针指向下一个节点
    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;
        }
    }

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

 测试类

import static org.junit.jupiter.api.Assertions.*;
import org.junit.Test;

import java.util.List;

public class TestLinkedListDeque {
    // 添加节点
    @Test
    public void offer(){
        LinkedListDeque<Integer> deque = new LinkedListDeque<>(5);
        deque.offerFirst(3);
        deque.offerLast(4);
        deque.offerFirst(2);
        deque.offerLast(5);
        deque.offerFirst(1);
        assertFalse(deque.offerLast(6));
        assertIterableEquals(List.of(1,2,3,4,5),deque);
    }

    // 移除节点
    @Test
    public void poll(){
        LinkedListDeque<Integer> deque = new LinkedListDeque<>(5);
        deque.offerFirst(3);
        deque.offerLast(4);
        deque.offerFirst(2);
        deque.offerLast(5);
        deque.offerFirst(1);

        assertEquals(1, deque.pollFirst());
        assertEquals(2, deque.pollFirst());
        assertEquals(3, deque.pollFirst());
        assertEquals(4, deque.pollFirst());
        assertEquals(5, deque.pollLast());
        assertNull(deque.pollLast());
        assertTrue(deque.isEmpty());
    }

}


三、双端队列——数组实现

基于循环数组实现

head:头指针        tail:尾指针

特点:tail停下来的位置不存储,会浪费一个位置

Type parameters:<E>——队列中元素类型

 接口

public interface Deque<E> {
    /*
        向队列头部添加元素
        Returns:bool类型,添加是否成功
     */
    boolean offerFirst(E e);

    /*
        向队列尾部添加元素
        Returns:bool类型,添加是否成功
    */
    boolean offerLast(E e);

    /*
        从队列的头部删除元素
        Returns:返回删除的元素
    */
    E pollFirst();

    /*
        从队列的尾部删除元素
        Returns:返回删除的元素
    */
    E pollLast();

    /*
        从队列的头部获取元素
        Returns:获取头部的元素
    */
    E peekFirst();

    /*
        从队列的尾部获取元素
        Returns:获取尾部的元素
    */
    E peekLast();

    /*
        判断队列是否为空
        Returns:判断是否为空
    */
    boolean isEmpty();

    /*
        判断队列是否为满
        Returns:判断是否为满
    */
    boolean isFull();
}

循环数组类

import java.util.Iterator;

public class ArrayDeque<E> implements Deque<E>,Iterable<E> {

    // 两个工具方法 1
    static int inc(int i,int length){
        if (i + 1 >= length){
            return 0;
        }
        return i + 1;
    }

    // 两个工具方法 2
    static int dec(int i,int length){
        if (i - 1 < 0){
            return length - 1;
        }
        return i - 1;
    }

    E[] array;
    int head;
    int tail;

    @SuppressWarnings("all")
    public ArrayDeque(int capacity){
        array = (E[]) new Object[capacity+1];
    }

    @Override
    public boolean offerFirst(E e) {
        if (isFull()){
            return false;
        }
        head = dec(head, array.length);
        array[head] = e;
        return true;
    }

    @Override
    public boolean offerLast(E e) {
        if (isFull()){
            return false;
        }
        array[tail] = e;
        tail = inc(tail, array.length);
        return true;
    }

    @Override
    public E pollFirst() {
        if (isEmpty()){
            return null;
        }
        E e = array[head];
        // 帮助垃圾回收
        array[head] = null;
        head = inc(head,array.length);
        return e;
    }

    @Override
    public E pollLast() {
        if (isEmpty()){
            return null;
        }
        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[dec(tail, array.length)];
    }

    @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;
            }
        };
    }
}

测试类

import org.junit.Test;

import java.util.List;

import static org.junit.jupiter.api.Assertions.*;

public class TestArrayDeque {
    @Test
    public void offer() {
        ArrayDeque<Integer> deque = new ArrayDeque<>(5);
        deque.offerFirst(1);
        deque.offerLast(2);
        deque.offerLast(3);
        deque.offerLast(4);
        deque.offerLast(5);
        assertFalse(deque.offerLast(3));
        assertIterableEquals(List.of(1,2,3,4,5), deque);
    }

    @Test
    public void poll() {
        ArrayDeque<Integer> deque = new ArrayDeque<>(5);
        assertTrue(deque.isEmpty());
        deque.offerFirst(1);
        deque.offerLast(2);
        deque.offerLast(3);
        deque.offerLast(4);
        deque.offerFirst(5);
        assertIterableEquals(List.of(5,1,2,3,4),deque);
        assertTrue(deque.isFull());

        assertEquals(5, deque.pollFirst());
        assertEquals(4, deque.pollLast());
        assertEquals(1 , deque.pollFirst());
        assertEquals(3 , deque.pollLast());
        assertEquals(2 , deque.pollFirst());
        assertNull(deque.pollLast());
        assertTrue(deque.isEmpty());
    }
}

垃圾回收机制

因为用泛型实现,所以都是引用类型,我们需要将索引位置的数组值在处理完后赋值为null

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

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

相关文章

计算机毕业设计 基于Python的个性化旅游线路推荐系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

C++基础补充(02)C++其他控制语句break continue goto等

文章目录 1. break2. continue 语句3. goto 语句goto的存在 4. 跳出多重循环4.1 goto 直接跳转4.2 C11及其后版本的 return 语句4.3 使用标志变量 在C中&#xff0c;控制语句用于管理程序的执行流程。常见有 break、continue 和 goto。 1. break break语句主要用于在循环或者s…

【GEE学习第一期】GEE介绍、注册及基本使用

【GEE学习第一期】GEE介绍、注册及基本使用 GEE基本介绍GEE架构编辑语言&#xff1a;JavaScript GEE注册GEE平台界面GEE编辑器API区详解 GEE环境配置本地Python环境配置在线Python环境配置 参考 GEE基本介绍 地球引擎代码编辑器&#xff08;Google Earth Engine, GEE&#xff…

oauth2授权码模式单点登录

文章目录 前言一、单点登录是什么&#xff1f;二、oauth2授权码模式单点登录流程1.流程图2. 代码相关2. 验证流程 总结 前言 oauth2 有四种模式,常用的为密码和授权码,剩下两种几乎不用 密码模式,很好理解,就是根据输入的用户名/密码进行登录认证的,最终返回一个合法token授权…

Coze:如何使用主页对话框?

你好&#xff0c;我是三桥君 我们今天要介绍的功能模块是“主页对话框”。 目录 访问官网 登录首页 基本功能 主页对话框 第一个功能&#xff1a;如何与自己收藏的机器人进行对话&#xff1f; 第二个功能&#xff1a;如何请求主页对话框的机器人帮助创建一个新的机器人&#x…

C++11 异步操作 std::future类

阅读导航 引言一、异步的概念二、应用场景1. 异步任务处理2. 并发控制3. 结果获取 三、使用示例1. 使用std::async关联异步任务&#x1f4bb;示例代码说明 2. 使用std::packaged_task和std::future配合&#xff08;1&#xff09;定义std::packaged_task&#xff08;2&#xff0…

游戏修改器Cheat Engine CE v7.5修改版下载安装详细方法

Cheat Engine是一个专注于游戏的修改器。它可以用来扫描游戏中的内存&#xff0c;并允许修改它们。它还附带了调试器、反汇编器、汇编器、变速器、作弊器生成、Direct3D操作工具、系统检查工具等。 具体安装方法如下&#xff1a; 地址&#xff1a;Cheat Engine 7.5.zip 解压文件…

Prompt 初级版:构建高效对话的基础指南

Prompt 初级版&#xff1a;构建高效对话的基础指南 文章目录 Prompt 初级版&#xff1a;构建高效对话的基础指南一 “标准”提示二 角色提示三 多范例提示四 组合提示五 规范化提示 本文介绍了提示词的基础概念与不同类型&#xff0c;帮助用户更好地理解如何在对话中构建有效的…

Java 计算器项目

更多有趣请关注公众号 计算器项目 代码仓库&#xff1a;https://gitee.com/wengxiulin/vs_code 项目图片 项目简介 这是一个用 Java 编写的简单计算器应用程序&#xff0c;具有基本的数学运算功能。该计算器支持加、减、乘、除等运算&#xff0c;并提供用户友好的图形界面…

CSP-J模拟赛三补题报告

前言 挂了110pts( ⇑ \Uparrow ⇑ \hspace{14em} 有史以来最大傻逼 T1&#xff1a; 100 p t s \color{green}100pts 100pts T2: 100 p t s → 80 p t s \color{green}100pts\color{yellow}\rightarrow\color{red}80pts 100pts→80pts T3: 100 p t s → 10 p t s \color{gre…

java 的三种IO模型(BIO、NIO、AIO)

java 的三种IO模型&#xff08;BIO、NIO、AIO&#xff09; 一、BIO 阻塞式 IO&#xff08;Blocking IO&#xff09;1.1、BIO 工作机制1.2、BIO 实现单发单收1.3、BIO 实现多发多收1.4、BIO 实现客户端服务端多对一1.5、BIO 模式下的端口转发思想 二、NIO 同步非阻塞式 IO&#…

【Godot4.3】模拟平面图形绕轴或点在空间旋转

概述 平面图形&#xff0c;除了常规的线性变换&#xff1a;平移、缩放、旋转、斜切之外。还可以模仿在三维空间旋转、透视等等。 矩形绕纵对称轴旋转实点的轨迹 绕对称旋转是个特殊情况&#xff0c;轨迹是圆也是为了便于理解。更实际的情况应该是椭圆。非对称轴旋转的情况轨…

Java 之深入理解 String、StringBuilder、StringBuffer

前言 由于发现 String、StringBuilder、StringBuffer 面试的时候会经常问到&#xff0c;这里就顺便总结一下&#xff1a;本文重点会以这三个字符串类的性能、线程安全、存储结构这三个方面进行分析 ✨上期回顾&#xff1a;Java 哈希表 ✨目录 前言 String 介绍 String 的不可变…

2024/10/3 408数据结构大题打卡

最短路径复习&#xff1a; bfs&#xff1a;只能解决无权图

【LeetCode每日一题】——17.电话号码的字母组合

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 回溯 二【题目难度】 中等 三【题目编号】 17.电话号码的字母组合 四【题目描述】 给定一个…

redis 5的安装及启动(window)

最近看大模型的时候发现入手redis的同学没有练手的&#xff0c;而且大部分redis的文章要钱才能看&#xff0c;在这里我把路径和环境配置&#xff0c;启动给大家说一下 下载 redis5的获取链接在下面&#xff08;为什么是redis5&#xff0c;因为上个模型用的就是redis5&#xff…

pipe函数的例子

代码&#xff1a; #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<sys/types.h> int main(void) {int result -1;int fd[2],nbytes;pid_t pid;char string[80]"ni hao, pipe!";char readbuff…

linux信号 | 学习信号四步走 | 一篇文章教你理解信号如何保存

前言&#xff1a; 本节内容是信号的保存。 学习信号&#xff0c; 我们首先了解了信号的概念&#xff0c; 然后学习了信号的产生方式。 现在就开始讲解信号在时间窗口内是如何保存在进程内部的。 ps&#xff1a;本节内容需要了解信号的概念&#xff0c; 希望友友们了解一些信号…

实用技能分享!推荐最适合论文写作的5款ai工具

在当今学术研究和教育领域&#xff0c;AI工具的应用已经变得越来越普遍。这些工具不仅能够提高写作效率&#xff0c;还能帮助生成高质量的文稿。对于教师而言&#xff0c;选择合适的AI工具可以显著提升论文写作的效率和质量。本文将重点推荐五款最适合教师论文写作的AI工具&…

Linux聊天集群开发之环境准备

一.windows下远程操作Linux 第一步&#xff1a;在Linux终端下配置openssh&#xff0c;输入netstate -tanp,查看ssh服务是否启动&#xff0c;默认端口22.。 注&#xff1a;如果openssh服务&#xff0c;则需下载。输入命令ps -e|grep ssh, 查看如否配有&#xff0c; ssh-agent …