基础数据结构------单链表

news2024/9/20 5:54:56

1、链表使用的解决方案

【链表的概述】

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。

【链表的分类】

链表有很多种不同的类型:单向链表,双向链表以及循环链表。 

由于N 个结点依次相链构成的链表的每个结点中只包含一个指针域,故又称单链表或线性链表。

在双向链表中,结点除含有数据域外,还有两个链域,一个存储直接后继结点地址,一般称之为右链域;一个存储直接前驱结点地址,一般称之为左链域。

循环链表的最后一个结点的指针是指向该循环链表的第一个结点或者表头结点,从而构成一个环形的链。

2、带头节点单向链表

定义头节点

private Node head = null;   // 头节点

 节点类

private static class Node {
        int value;   // 节点的值
        Node next;   // 下一个节点

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

为什么节点类用 private 修饰?

链表和节点之间存在整体和部分的关系,对外隐藏节点的属性和实现细节。链表类封装了节点类的属性。

为什么节点类用 static 关键字修饰?

static 可以在内部类变量和外部的成员变量没有关系时添加。

【采用头插法创建链表】

public void addFirst(int value) {
        // 链表无节点
        //head = new Node(value, null);
        // 链表有节点
        head = new Node(value, head);
    }

【遍历链表】

public void loop1 (Consumer<Integer> consumer) {
        Node p = head;
        while (p != null) {  //p当前节点为空时结束循环
            consumer.accept(p.value);
            p = p.next;     //节点位置后移
        }
    }
list.loop1(value -> System.out.println(value));//输出: 4 3 2 1
public void loop2(Consumer<Integer> consumer) {
        for (Node p = head; p != null; p = p.next) {
            consumer.accept(p.value);
        }
    }
list.loop2(value -> System.out.println(value));//输出: 4 3 2 1
@Override
    public Iterator<Integer> iterator() {
        // 匿名内部类 ----> 带名字内部类
        return new NodeIterator();
    }

    private class NodeIterator implements Iterator<Integer> {
        Node p = head;  // 新节点指向头节点

        @Override
        public boolean hasNext() { //判断有无下一个节点
            return p != null;
        }

        @Override
        public Integer next() {  // 当前节点的值和指向下一个节点的地址
            int value = p.value;
            p = p.next;
            return value;
        }
    }

 【采用尾插法创建链表】

// 查找最后一个节点
    private Node findLast() {
        //判断是否为空链表
        if (head == null){
            return null;
        }

        Node p;
        for (p = head; p.next != null; p = p.next) {

        }
        return p;
    }

    // 尾插法
    public void addLast(int value) {
        // 找到最后一个节点
        Node last = findLast();
        //空链表
        if (last == null){
            addFirst(value);
            return;
        }
        // 新节点
        last.next = new Node(value, null);
    }

【获取节点的索引】

public void getIndex () {
        int i = 0;
        for (Node p = head; p != null; p = p.next, i++) {
            System.out.println(p.value + "索引是:" + i);
        }
    }

【根据指定的索引获取对应节点】 

public int get (int index) {
        Node node = findNode(index);
        if (node == null) {
            // 抛出异常
            throw IllegalIndex(index);
        }
        return node.value;
   }
// 参数不合法异常
    private IllegalArgumentException IllegalIndex(int index) {
        return new IllegalArgumentException(
                String.format("index[%d] " + index + "不合法 %n"));
    }

【向指定索引位置插入节点】

public void insert(int index, int value) {
        //索引为0
        if (index == 0) {
            addFirst(value);
            return;
        }
        // 找到上一个节点
        Node prev = findNode(index - 1);
        //找不到节点
        if (prev == null) {
            throw IllegalIndex(index);
        }
        // 新节点在上一个节点之后
        prev.next = new Node(value, prev.next);
    }

【删除节点】

// 删除第一节点
    public void removeFirst() {
        if (head == null) {
            throw IllegalIndex(0);
        }
        head = head.next;
    }

// 删除指定索引的节点
    public void remove(int index) {
        // 索引为 0
        if (index == 0) {
            removeFirst();
            return;
        }
        Node prev = findNode(index - 1);
        if (prev == null) {
            throw IllegalIndex(index);
        }
        // 待删除的节点
        Node removed = prev.next;
        // 删除节点 是null
        if (removed == null) {
            throw IllegalIndex(index);
        }
        prev.next = removed.next;
    }

3、哨兵的单向链表

当链表为空时,执行addLast(int value)、insert(int index, int value)、remove(int index)和链表非空时,执行addLast(int value)、insert(int index, int value)、remove(int index)的逻辑不同。采用哨兵节点为了简化addLast(int value)、insert(int index, int value)、remove(int index)操作链表。

【哨兵节点】

private Node head = new Node(100, null);

【初始化节点对象】

private static class Node {
        int value;   // 节点的值
        Node next;   // 下一个节点

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

【链表的遍历】

// 遍历链表1: while  consumer 要执行的操作
    public void loop1(Consumer<Integer> consumer) {
        Node p = head.next;
        while (p != null) {  //p当前节点为空时结束循环
            consumer.accept(p.value);
            p = p.next;     //节点位置后移
        }
    }

//遍历链表2: fori形式 consumer 要执行的操作
    public void loop2(Consumer<Integer> consumer) {
        for (Node p = head.next; p != null; p = p.next) {
            consumer.accept(p.value);
        }
    }

//遍历3:迭代器
    @Override
    public Iterator<Integer> iterator() {
        // 匿名内部类 ----> 带名字内部类
        return new NodeIterator();
    }

    private class NodeIterator implements Iterator<Integer> {
        Node p = head.next;  // 新节点指向头节点

        @Override
        public boolean hasNext() { //判断有无下一个节点
            return p != null;
        }

        @Override
        public Integer next() {  // 当前节点的值和指向下一个节点的地址
            int value = p.value;
            p = p.next;
            return value;
        }
    }

【查找最后一个节点】

private Node findLast() {
        Node p;
        for (p = head; p.next != null; p = p.next) {

        }
        return p;
    }

【根据索引查找指定的节点】

private Node findNode(int index) {
        int i = -1;
        for (Node p = head; p != null; p = p.next, i++) {
            if (i == index) {
                return p;
            }
        }
        return null;
    }

【从尾部插入节点】

public void addLast(int value) {
        // 找到最后一个节点
        Node last = findLast();
        // 新节点
        last.next = new Node(value, null);
    }

【获取索引指定的节点】

public int get(int index) {
        Node node = findNode(index);
        if (node == null) {
            // 抛出异常
            throw IllegalIndex(index);
        }
        return node.value;
    }

【获取链表的节点索引】

public void getIndex() {
        int i = 0;
        for (Node p = head.next; p != null; p = p.next, i++) {
            System.out.println(p.value + "索引是:" + i);
        }
    }

【向指定位置插入节点】

public void insert(int index, int value) {
        // 找到上一个节点
        Node prev = findNode(index - 1);
        //找不到节点
        if (prev == null) {
            throw IllegalIndex(index);
        }
        // 新节点在上一个节点之后
        prev.next = new Node(value, prev.next);
    }

【参数不合法异常】

private IllegalArgumentException IllegalIndex(int index) {
        return new IllegalArgumentException(
                String.format("index[%d] 不合法 %n" ));
    }

【删除节点】

public void remove(int index) {
        Node prev = findNode(index - 1);
        if (prev == null) {
            throw IllegalIndex(index);
        }
        // 待删除的节点
        Node removed = prev.next;
        // 删除节点 是null
        if (removed == null) {
            throw IllegalIndex(index);
        }
        prev.next = removed.next;
    }

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

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

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

相关文章

从MySQL小表驱动大表说起

刚刚开始学习MySQL的时候&#xff0c;连表查询直接使用left join或者更粗暴点&#xff0c;两个表直接查询&#xff0c;where过滤条件&#xff0c;组装笛卡尔积&#xff0c;最终出现自己想要的结果。 当然&#xff0c;现在left join也是会用的&#xff0c;毕竟嘛&#xff0c;方便…

如何实现多存储文件传输,镭速提供多存储文件传输解决方案

目前的文件传输系统中&#xff0c;大多数采用的文件传输系统只支持单个的存储。随着科技的发展&#xff0c;存储的类型越来越多&#xff0c;构建的越来越复杂&#xff0c;业务要求越来越多样化&#xff0c;只支持单个存储的文件传输系统是无法满足现有的需求。 为实现高自由度…

Java基础(十九):集合框架

Java基础系列文章 Java基础(一)&#xff1a;语言概述 Java基础(二)&#xff1a;原码、反码、补码及进制之间的运算 Java基础(三)&#xff1a;数据类型与进制 Java基础(四)&#xff1a;逻辑运算符和位运算符 Java基础(五)&#xff1a;流程控制语句 Java基础(六)&#xff1…

vue3新的组件

1.Fragment - 在Vue2中: 组件必须有一个根标签 - 在Vue3中: 组件可以没有根标签, 内部会将多个标签包含在一个Fragment虚拟元素中 - 好处: 减少标签层级, 减小内存占用 没有写根标签&#xff0c;也没有报错&#xff0c;如果是在v2中&#xff0c;我们还需要用一个div来包裹它 …

springboot web项目统一时区方案

背景 springboot项目国际化中&#xff0c;会遇到用户选择的时间和最终存到数据库的时间不一致&#xff0c;可能就是项目开发和部署时的时区没有处理好&#xff0c;导致时间转换出现了问题。 先了解时区都有哪些&#xff1a; 1.GMT&#xff1a;Greenwich Mean Time 格林威治…

移动端适配方法:rem+vw

1.百分比设置:几乎不用 因为各种属性百分比参照物(自身/父元素/...需要去查文档)很难统计固定,所以不用百分比进行适配 2.rem单位动态html的font-size 使用rem,因为rem参考html的fz,只需要在不同的屏幕上设置不同的html的fz即可,其他地方全用rem rem的fz尺寸 媒体查询 编写…

推荐系统召回之userCF

基于用户的协同过滤算法userCF 1.1 相似度计算 通过计算用户之间的相似度。这里的相似度指的是两个用户的兴趣相似度。 假设对于用户u uu和v vv&#xff0c;N ( u ) N(u)N(u)指的是用户u uu喜欢的物品集合&#xff0c;N ( v ) N(v)N(v)指的是用户v vv喜欢的物品集合&#xff0…

体验 Kubernetes Cluster API

体验 Kubernetes Cluster API 什么是 Kubernetes Cluster API安装 Kind增加 ulimit 和 inotify创建 Kind 集群安装 clusterctl CLI 工具初始化管理集群创建一个工作负载集群访问工作负载集群部署一个 CNI 解决方案安装 MetalLB部署 nginx 示例清理(参考)capi-quickstart.yaml 文…

C++的类和对象(2)

类和对象 1.类对象模型1.1. 如何计算类对象的大小1.2. 类的存储模式讨论1.3. 类对象的空间符合结构体对齐规则 2. this指针2.1. this指针的引出2.2. this指针的特性2.3.面试题2.4. C语言和C实现栈的对比 1.类对象模型 1.1. 如何计算类对象的大小 class A { public: void Prin…

类加载与卸载

加载过程 其中验证,准备,解析合称链接 加载通过类的完全限定名,查找此类字节码文件,利用字节码文件创建Class对象. 验证确保Class文件符合当前虚拟机的要求,不会危害到虚拟机自身安全. 准备进行内存分配,为static修饰的类变量分配内存,并设置初始值(0或null).不包含final修饰…

用python脚本从Cadence导出xdc约束文件

用python脚本从Cadence导出xdc约束文件 概述转换方法先导出csv文件修改CSV文件 CSV转XDC检查输出XDC文件csv2xdc源代码下载 概述 在Cadence设计完成带有FPGA芯片的原理图的时候&#xff0c;往往需要将FPGA管脚和网络对应关系导入vivado设计软件中&#xff0c;对于大规模FPGA管…

springboot+vue准妈妈孕期交流平台(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的准妈妈孕期交流平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;…

C++引用篇

文章目录 一、引用概念及示例二、引用做函数参数二、引用做函数的返回值四、常引用五、引用和指针的区别 一、引用概念及示例 c语言指针存变量地址&#xff0c;然后通过解引用可以访问或者改变变量&#xff0c;且也可以改变指针变量里面存的地址 修改变量这样还需要对指针变量…

Faster RCNN系列3——RPN的真值详解与损失值计算

Faster RCNN系列&#xff1a; Faster RCNN系列1——Anchor生成过程 Faster RCNN系列2——RPN的真值与预测值概述 Faster RCNN系列3——RPN的真值详解与损失值计算 Faster RCNN系列4——生成Proposal与RoI Faster RCNN系列5——RoI Pooling与全连接层 目录 一、RPN真值详解二、…

手把手教你实现el-table实现跨表格禁用选项,以及禁用选择后,对应的全选按钮也要禁用任何操作

哈喽 大家好啊 今天我要实现不能跨表格选择&#xff0c;如果我选择了其中一个表格的选项后&#xff0c;那么其他的表格选项则被禁用 然后我选择了其中一个表格行&#xff0c;我其他的表格选项则应该被禁用 实现代码&#xff1a; 其中关键属性&#xff1a; selectable仅对 typ…

如何保障企业网络安全

随着信息技术的迅速发展&#xff0c;网络已经渗透到了我们生活的方方面面。企业对网络的依赖程度也越来越高&#xff0c;网络安全问题已经成为了企业面临的一个重要挑战。那么&#xff0c;在这个风险重重的网络世界里&#xff0c;我们如何充分利用现有技术保障企业网络安全呢&a…

智能指针——C++

智能指针相较于普通指针的区别&#xff0c;就是智能指针可以不用主动释放内存空间&#xff0c;系统会自动释放&#xff0c;避免了内存泄漏。 1、unique_ptr&#xff1a;独占指针 需包含的头文件&#xff1a;#include <memory> unique_ptr 三种定义方式 先定义一个类 …

learn_C_deep_5 (温故知新、sigend char a = -128的深度理解、unsigned int类型的写法规范)

目录 温故知新 理解"unsigned int a -10;" 如何理解大小端 大小端的概念 大小端是如何影响数据存储的 sigend char a -128的深度理解 10000000为什么是-128&#xff0c;而不是-0 代码练习 unsigned int类型的写法规范 温故知新 理解"unsigned int a…

python数据结构与算法-动态规划(最长公共子序列)

一、最长公共子序列问题 1、问题概念 一个序列的子序列是在该序列中删去若干元素后得 到的序列。 例如&#xff1a;"ABCD”和“BDF”都是“ABCDEFG”的子序列。 最长公共子序列(LCS) 问题: 给定两个序列X和Y&#xff0c;求X和Y长度最大的公共子字列。 例:X"ABBCBDE”…

【ABAQUS Python二次开发】 debug : ini解析ERROR:没有实例属性‘__getintem__’

我的主页&#xff1a; 技术邻&#xff1a;小铭的ABAQUS学习的技术邻主页博客园 : HF_SO4的主页哔哩哔哩&#xff1a;小铭的ABAQUS学习的个人空间csdn&#xff1a;qgm1702 博客园文章链接&#xff1a; https://www.cnblogs.com/aksoam/p/17287136.html abaqus python 搭配ini…