首页 > 编程语言 >链表(双向链表)Java版

链表(双向链表)Java版

时间:2025-01-22 19:27:23浏览次数:3  
标签:Node Java next 链表 tail 双向 prev 节点

双向链表(有哨兵节点)

双向链表介绍

双向链表(Doubly Linked List)是一种链式存储结构,每个节点不仅包含数据,还包含两个指针,分别指向前驱节点和后继节点。它相比单向链表有更高的灵活性,因为可以从任意节点向前或向后遍历。

双向链表的特点

1,双向指针:每个节点包含两个指针,分别指向前驱节点和后继节点。
2,灵活遍历:既可以从头节点开始遍历,也可以从尾节点开始遍历。
3,插入和删除:在已知节点位置的情况下,插入和删除操作更高效,因为不需要像数组那样移动大量元素。
4,额外空间:因为需要两个指针,所以相比单向链表占用更多的内存。

应用场景

1,浏览器的前进和后退功能。
2,实现缓存(如 LRU 缓存)。
3,适用于需要双向遍历的场景。

代码解析

1,节点类:
包含 value、prev 和 next 属性。
prev 指向前驱节点,next 指向后继节点。

2,双向链表类:
使用 head 和 tail 分别指向链表的头节点和尾节点。
提供添加、删除和遍历功能。

3,核心方法:
addLast(int data):在链表尾部添加新节点。
removeFirst():从链表头部删除节点。
printForward() 和 printBackward():分别实现正向(从head.next开始)和反向遍历(从tail.prev开始)。

Java代码

import java.util.Iterator;

//双向链表(带哨兵)
public class DoublyLinkedListSentinel implements Iterable<Integer> {
    static class Node {
        Node prev;
        int value;
        Node next;

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

    private Node head;
    private Node tail;

    public DoublyLinkedListSentinel() {
        head = new Node(null, -1, null);
        tail = new Node(null, -1, null);
        head.next = tail;//初始化
        tail.prev = head;//初始化

    }

    private Node findNode(int index) {//根据索引位置找到节点
        int i = -1;
        for (Node p = head; p != tail; p = p.next, i++) {//p指向尾节点结束循环
            if (i == index) {
                return p;
            }
        }
        return null;
    }

    public void addFirst(int value) {//添加头节点
        insert(0,value);

    }
    public void removeFirst() {//删除头节点
        remove(0);

    }
    public void addLast(int value) {//添加尾节点
        //双向链表特色
        Node next = tail;
        Node prev = tail.prev;

        Node inserted = new Node(prev,value,next);
        prev.next = inserted;
        next.prev = inserted;
    }
    public void removeLast() {//删除尾节点
        //双向链表特色
        Node removed = tail.prev;
        if (removed == head) {
            throw illegalIndex(0);
        }
        Node prev = removed.prev;
        prev.next = tail;
        tail.prev = prev;
    }
    public void insert(int index,int value) {//添加节点
        Node prev = findNode(index - 1);
        if (prev == null) {//找不到前面节点抛出异常
            throw illegalIndex(index);
        }
        Node next = prev.next;
        Node inserted = new Node(prev,value,next);
        prev.next = inserted;
        next.prev = inserted;
    }
    public void remove(int index) {
        Node prev = findNode(index - 1);
        if (prev == null) {//如果前一个节点找不到抛出异常
            throw illegalIndex(index);//包含了待删节点是头哨兵的情况
        }
        Node removed = prev.next;//待删除节点
        if (removed == tail) {//如果待删除节点是尾哨兵抛出异常
            throw illegalIndex(index);
        }
        Node next = removed.next;

        prev.next = next;
        next.prev = prev;
    }
    private IllegalArgumentException illegalIndex(int index) {//异常信息

        return new IllegalArgumentException(
                String.format("索引:Index[%d],不合法 %n", index));
    }

    public Iterator<Integer> iterator() {//迭代器
        return new Iterator<Integer>() {
            Node p = head.next;
            @Override
            public boolean hasNext() {
                return p != tail;
            }

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

    public void showList() {//正向遍历链表
        System.out.print("遍历链表: ");
        forEach( i -> {
            System.out.print(i + " ");
        });

    }
}

标签:Node,Java,next,链表,tail,双向,prev,节点
From: https://blog.csdn.net/dwfwhh/article/details/145308557

相关文章

  • 基于java web的社区人员流动管理系统的设计与实现-毕业设计源码19467
    目 录1绪论1.1研究背景与意义1.2国内外研究现状1.3论文结构与章节安排2 系统分析2.1可行性分析2.1.1技术可行性分析2.1.2经济可行性分析2.1.3法律可行性分析2.2系统功能分析2.2.1功能性分析2.2.2非功能性分析2.3系统用例分析2.4系......
  • Anthropic 计划为 Claude 发布「双向」语音模式;商汤「日日新」实时音视频对话服务开放
      开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(Real-TimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编辑......
  • JavaSE基础笔记
    Java基础笔记一、流程控制(一)Scanner输入1、next()读取到空白就会自动将其去掉,next()不能得到带有空格的字符串hasNext()可以判断是否还有输入的数据packagecom.TEST.Test01;importjava.util.Scanner;publicclassTest01{publicstaticvoidmain(String[......
  • java 中的匿名内部类
    在Java中,匿名内部类是一种没有名称的内部类,通常用于简化代码,尤其是在实现接口或继承类时,只需要一个简单的实现。匿名内部类允许你在创建类的同时实例化它,通常用于简化代码的书写。工作原理匿名内部类是在你需要使用接口或抽象类的实现时定义和实例化的。它们在使用时定义在方法......
  • 【转】[JavaScript] import 和 export 的用法
    转自:kimi.ai在JavaScript中,import和export是ES6(ECMAScript2015)引入的模块化语法,用于在不同的文件或模块之间共享代码。它们使得代码更加模块化、可维护,并且可以避免全局变量的污染。以下是import和export的基本用法和一些常见场景。1. export 的用法export用于......
  • Java 变量和数据类型
    目录变量数据类型数据类型分类基本数据类型包装类装箱和拆箱手动拆装箱:自动拆装箱:包装类的特点总结变量的定义格式注变量变量:常量是固定不变的数据,那么在程序中可以变化的量称为变量。数学中,可以使用字母代替数字运算,例如x=1+5或者6=x+5。程序中,可以使用字母保存数字......
  • Java 并发
    目录线程多线程原理多线程的常用方法Thread类创建线程四种方式继承Thread类实现Runnable接口实现Callable接口FutureTask使用匿名内部类方式Thread和Runnable的区别Runnable和Callable的区别线程的run()和start()有什么区别?线程安全线程安全线程同步同步代码块同......
  • java流之Stream
    java流之Stream流:在现实中有移动,传播,延绵不绝,其有难寻其源,难觅其踪,变约莫测等特点。stream:是jdk8中新增的api成员,是对容器对象功能的增强,借助于同样是新出现的Lambda表达式,提供了方便、简洁、高效、链式等方式处理集合容器,以获取自己需要的结果。java中的流stream与现实中的流......
  • Javase--基础语法上
    Javase--基础语法注释packageMistiest.com.cnblogs.comment;/***这里介绍注释的基本语法,这个是文本注释,一般写在方法的最前面*/publicclasscomment{publicstaticvoidmain(String[]args){//这是单行注释/*这是多行注释......
  • JavaScript 自定义获取当前日期和时间的函数
    JavaScript自定义获取当前日期和时间的函数 /***获取当前的日期和时间*格式为yyyy-MM-ddHH:mm:ss.SSS*/functiongetNowDateTime(){varnow=newDate,year=now.getFullYear(),month=now.getMonth()+1......