深入解析链表:解锁数据结构核心奥秘

一. 链表的定义

链表是一种线性数据结构,由一系列节点组成。每个节点包含两个部分:

  1. 数据域(Data):存储节点的数据。
  2. 指针域(Pointer):存储指向下一个节点的地址。

链表的第一个节点称为头节点(Head),最后一个节点的指针域指向空(NULL),表示链表的结束。

二. 链表的结构

1) 单向 / 双向

2) 带头 / 不带头

3)  循环 / 非循环

 链表种类丰富多样 重点掌握 单向不带头非循环 链表  可作为其他数据结构的子结构,如 哈希桶、图的邻接表等   笔试常考

  

三.  实现链表

 1) 节点类

定义链表节点类,每个节点包含数据和指向下一个节点的指针。

public class Node {
    int data;
    Node next;

    public Node(int data) {
        this.data = data;
        this.next = null;
    }
}

2) 链表类:

用于实现链表的功能 

public class MySingleList {
    private ListNode head;

    static class ListNode {
        int val;
        ListNode next;

        ListNode(int val) {
            this.val = val;
            this.next = null;
        }
    }
    public ListNode cur; // 临时头节点
}
 插入节点: 

                                                         插入节点  图示

// 在链表的末尾插入一个新节点
    void append(int data) {
        Node newNode = new Node(data);
        if (head == null) {
            head = newNode;
        } else {
            Node current = head;
            while (current.next != null) {
                current = current.next;
            }
            current.next = newNode;
        }
        usedSize++;
    }

    // 在链表的开头插入一个新节点
    void prepend(int data) {
        Node newNode = new Node(data);
        newNode.next = head;
        head = newNode;
        usedSize++;
    }

    // 在指定位置插入一个新节点
    void insertAt(int index, int data) {
        if (index < 0 || index > usedSize) {
            throw new IndexOutOfBoundsException("Index out of bounds");
        }
        Node newNode = new Node(data);
        if (index == 0) {
            newNode.next = head;
            head = newNode;
        } else {
            Node current = head;
            for (int i = 0; i < index - 1; i++) {
                current = current.next;
            }
            newNode.next = current.next;
            current.next = newNode;
        }
        usedSize++;
    }
 删除节点: 
// 删除指定位置的节点
    void deleteAt(int index) {
        if (index < 0 || index >= usedSize) {
            throw new IndexOutOfBoundsException("Index out of bounds");
        }
        if (index == 0) {
            head = head.next;
        } else {
            Node current = head;
            for (int i = 0; i < index - 1; i++) {
                current = current.next;
            }
            current.next = current.next.next;
        }
        usedSize--;
    }
 查找节点:
// 查询指定位置的节点
    Node getNodeAt(int index) {
        if (index < 0 || index >= usedSize) {
            throw new IndexOutOfBoundsException("Index out of bounds");
        }
        Node current = head;
        for (int i = 0; i < index; i++) {
            current = current.next;
        }
        return current;
    }

 四. 链表OJ 实战 ! 

 1)  开胃小菜  删除所有值域为val的节点

力扣链接: . - 力扣(LeetCode)

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while(head!= null && head.val == val){
            head = head.next;
        }
        ListNode cur = head;
        while(cur != null && cur.next != null){
            if(cur.next.val == val){
                cur.next = cur.next.next;
            }else{
            cur = cur.next;
            }
        }
        return head;
    } 
}

2)  反转链表 重要程度 五颗星 !!!

力扣链接: . - 力扣(LeetCode)

//    反转链表
    public ListNode reverseList(ListNode head) {
        // 定义两个指针,pre初始化为null,用于存储反转后的链表
        ListNode pre = null;
        // cur初始化为head,表示当前处理的节点
        ListNode cur = head;
    
        // 循环直到cur为null,表示已经处理完所有节点
        while(cur != null){
            // temp临时存储cur的下一个节点,因为接下来要修改cur.next
            ListNode temp = cur.next;

            // 将cur的next指针指向pre,实现反转
            cur.next = pre;

            // pre和cur都前进一步
            pre = cur; // pre移动到cur的位置
            cur = temp; // cur移动到下一个待处理的节点
        }

        // 当cur为null时,pre就是新链表的头节点
        return pre;
    }

 思路: 在遍历链表时逐步反转每个节点的指针方向 

3) 快慢指针算法 

快慢指针: 处理环形链表或数组非常有用

其实不单单是环形链表或者是数组,如果我们要研究的问题出现循环往复的情况时,均可考虑使用快慢指针的思想。

力扣链接: 876. 链表的中间结点 - 力扣(LeetCode)

public ListNode middleNode(ListNode head) {
        // 定义快慢指针,都初始化为头节点head
        ListNode fast = head;
        ListNode slow = head;

        // 循环条件,快指针fast及其next不为null
        while (fast != null && fast.next != null) {
            // 快指针fast每次向前移动两步
            fast = fast.next.next;
            // 慢指针slow每次向前移动一步
            slow = slow.next;
        }

        // 当快指针到达链表末尾时,慢指针正好到达中间节点
        return slow;
    }

 原理 : 因为fast的速度是 slow的速度的二倍, 而fast走完,slow 必然在中间位置.

 4) 合并两个有序链表:

思路 : 使用 虚拟节点 为了简化头节点的处理逻辑

比较大小 插入新链表

如果其中一个链表的节点全部被插入到新链表中,如果另一个链表还有剩余节点, 直 接将这些剩余节点链接到新链表的末尾。
记得最后返回虚拟节点的下一个节点.

力扣链接: . - 力扣(LeetCode)

public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode dummy = new ListNode(-1);//虚拟节点

        // 创建一个指针cur,用来遍历并构建新的合并后的链表,初始指向哑节点
        ListNode cur = dummy;
        
        // 遍历链表直到其中一个为空
        while(list1 != null && list2 != null){
            if(list1.val > list2.val){
                cur.next = list2;
                list2 = list2.next;
            }else{
                cur.next = list1;
                list1 = list1.next;
            }
            cur = cur.next;
        }

        // 如果其中一个链表先遍历完,将另一个链表的剩余部分连接到新链表的末尾
        cur.next = (list1 == null) ? list2:list1;
        return dummy.next; //返回虚拟节点的下一个节点
    }

 五. ArrayList和LinkedList的区别

  • 数组: 适用于需要快速访问和固定大小的情况。
  • 链表: 适用于频繁插入和删除、且大小动态变化的情况。

 六. 总结 

链表作为数据结构中的佼佼者,凸显了其在动态数据操作场景下的独特价值,特别是对于需要频繁执行插入和删除操作的应用来说,更是不可或缺。掌握链表的基础操作,包括但不限于节点的创建、插入、删除及遍历,以及进阶技巧如链表的反转、合并操作,以及利用快慢指针解决复杂链表问题,是深化链表理解和应用实践的关键步骤。在链表与数组的权衡选择上,认识到两者各自的强项——数组的快速随机访问与链表的高效动态修改能力,是根据具体需求制定数据结构策略的先决条件。总而言之,链表的灵活高效使其在众多计算领域内占有一席之地,而深入探索其特性和应用,则是对每一位技术探索者的智慧挑战与能力提升之旅。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序开发_准备工作

1 注册小程序 注册地址 https://mp.weixin.qq.com/wxopen/waregister?actionstep1&sourcempregister&token&langzh_CN 2 完善小程序信息 进入微信公众平台https://mp.weixin.qq.com/,登录账号 登录后,在首页完善小程序信息和小程序类目 完成后在左侧找到开发…

权威认可 | Smartbi连续5年入选“Gartner增强数据分析代表厂商”

近日&#xff0c;全球权威技术研究与咨询公司Gartner最新发布《2024 年中国数据、分析和人工智能技术成熟度曲线》&#xff0c;Smartbi以其卓越的增强数据分析及自助分析能力&#xff0c;再次入选代表厂商&#xff0c;这也是Smartbi连续5年入选增强数据分析及自助分析代表厂商&…

优选算法2

五、位运算 常见位运算总结 &&#xff1a;有0就是0&#xff1b; |&#xff1a;有1就是1 ^&#xff1a;相同为0&#xff0c;相异就是1/无进位相加 给定一个数n,确定它的二进制表示中的第x位是0还是1&#xff1a;二进制中权值最小的是第0位&#xff0c;所以int整型是从第0位到…

os实训课程模拟考试(选择题复习)

目录 一、操作系统的基本功能和设计目标 &#xff08;1&#xff09;基础知识 &#xff08;2&#xff09;题目与答案 1、操作系统是一组 B &#xff08;单选&#xff09; 2、以下哪项不是操作系统关心的主要问题&#xff1f;D &#xff08;单选&#xff09; 3、下列关于…

基于Ansys workbench进行发动机风扇非定常流固耦合计算

添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&…

MFC扩展库BCGControlBar Pro v35.0新版亮点 - 工具栏、菜单全新升级

BCGControlBar库拥有500多个经过全面设计、测试和充分记录的MFC扩展类。 我们的组件可以轻松地集成到您的应用程序中&#xff0c;并为您节省数百个开发和调试时间。 BCGControlBar专业版 v35.0已全新发布了&#xff0c;这个版本改进类Visual Studio 2022的视觉主题、增强对多个…

pytest中的极其重要固件(request)的理解

pytest 是一个非常流行的Python测试框架&#xff0c;它为开发人员提供了丰寴的测试工具和功能。 在pytest中&#xff0c;固件&#xff08;fixture&#xff09;是一种非常核心的概念&#xff0c;用于设置测试前的预条件&#xff0c;清理测试后的环境&#xff0c;或者提供测试过…

vxeTable反转表格

文章目录 前言 前言 如果遇到列为动态值&#xff0c;行相对固定的情况&#xff0c;这种时候就需要用到行列反转&#xff0c;这里我以vxeTable表格为例。 直接上代码 <vxe-gridref"tableRefRight":auto-resize"true":columns"dataColumn":dat…

springboot中使用springboot cache

前言&#xff1a;SpringBoot中使用Cache缓存可以提高对缓存的开发效率 此图片是SpringBootCache常用注解 Springboot Cache中常用注解 第一步&#xff1a;引入依赖 <!--缓存--><dependency><groupId>org.springframework.boot</groupId><artifactId…

【ai】trition:tritonclient yolov4:ubuntu18.04部署python client成功

X:\05_trition_yolov4_clients\01-python server代码在115上,client本想在windows上, 【ai】trition:tritonclient.utils.shared_memory 仅支持linux 看起来要分离。 【ai】tx2 nx:ubuntu18.04 yolov4-triton-tensorrt 成功部署server 运行 client代码远程部署在ubuntu18.0…

分布式锁及其实现与应用场景

分布式锁及其实现与应用场景 分布式锁是一种用于在分布式系统中协调多个进程或线程对共享资源进行访问的机制。它的主要目的是确保在同一时间只有一个进程或线程可以访问特定资源&#xff0c;从而避免数据竞争和不一致问题。分布式锁通常用于集群环境中&#xff0c;例如微服务…

二次封装 el-dialog 实现 全屏和最小化 功能

效果 封装后的组件 <template><el-dialog v-model"dialogVisible" :show-close"false" :fullscreen"fullscreen" draggable overflow><template #header"{ close }"><div><span style"font-weight: b…

Python operator模块这么用,效率杠杠的!

目录 1、基础操作符应用 🐍 1.1 加载operator模块 1.2 使用itemgetter进行排序 1.3 attrgetter与方法调用 2、高级功能探索 🔍 2.1 methodcaller的妙用 2.2 操作符重载与定制 3、结合lambda表达式 ✨ 3.1 lambda与operator模块协同工作 3.2 实战案例分析 4、结合…

nginx架构基本数据结构配置模块请求详解

nginx源码的目录结构&#xff1a; . ├── auto 自动检测系统环境以及编译相关的脚本 │ ├── cc 关于编译器相关的编译选项的检测脚本 │ ├── lib nginx编译所需要的一些库的检测脚本 │ ├── os 与平台相关的一些系统参…

【sqlmap命令学习及测试dvwa_SQL_Injection】

文章目录 1.sqlmap命令及 不同级别探索 能否注入命令option1.1 low等级1.2 Medium等级1. 3 High等级 2. 注入流程2.1 数据库2.2 指定数据库表名2.3 指定表的 字段名2.4 内容2.5 当前用户信息2.6 用户密码2.7 其他 1.sqlmap命令及 不同级别探索 能否注入 命令option sqlmap -u…

石家庄高校大学智能制造实验室数字孪生可视化系统平台项目验收

智能制造作为未来制造业的发展方向&#xff0c;已成为各国竞相发展的重点领域。石家庄高校大学智能制造实验室积极响应国家发展战略&#xff0c;结合自身优势&#xff0c;决定引进数字孪生技术&#xff0c;构建一个集教学、科研、生产于一体的可视化系统平台。 数字孪生可视化…

influxdb时序数据库使用

influxdb时序数据库使用 1.1.免费无云influx申请1.2.Telegraf安装1.3.influxdb安装mac安装Redhat && Centos安装docker安装Kubernetes安装windows安装 1.4.influx CLI 安装1.5.influx命令行界面1.5.influx配置项权限认证配置管理 API 令牌 InfluxDB 是一个开源分布式时…

kali Linux基本命令(超全)_kali linux命令

一、系统信息 arch 显示机器的处理器架构(1) uname -m 显示机器的处理器架构(2) uname -r 显示正在使用的内核版本 dmidecode -q 显示硬件系统部件- (SMBIOS / DMI) hdparm -i /dev/hda 罗列一个磁盘的架构特性 hdparm -tT /dev/sda 在磁盘上执行测试性读取操作 cat /proc/cpu…

【从0实现React18】 (六) 完成commit提交流程并初步实现react-dom包,完成首屏渲染测试

前面&#xff0c;我们提到 React 更新流程有四个阶段&#xff1a; 触发更新&#xff08;Update Trigger&#xff09;调度阶段&#xff08;Schedule Phase&#xff09;协调阶段&#xff08;Reconciliation Phase&#xff09;提交阶段&#xff08;Commit Phase&#xff09; 之前…

安防监控视频平台LntonAIServer视频监控管理平台裸土检测算法技术核心和应用场景

LntonAIServer裸土检测算法是一种基于人工智能技术的创新解决方案&#xff0c;旨在实现对裸土地表的自动识别。以下是对该算法的详细分析&#xff1a; 技术基础&#xff1a; 1、该算法利用深度学习和计算机视觉技术&#xff0c;通过捕捉视频或图像中的关键信息&#xff0c;如…