数据结构-链表笔记

移除节点

203. 移除链表元素 - 力扣(LeetCode)

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 创建一个虚拟头节点,方便处理删除头节点的情况
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;

        // 初始化双指针
        ListNode pre = dummyHead; // pre指向当前节点的前一个节点
        ListNode cur = head;      // cur指向当前节点

        // 遍历链表
        while (cur != null) {
            // 如果当前节点的值等于要删除的值
            if (cur.val == val) {
                // 跳过当前节点,直接链接前一个节点到当前节点的下一个节点
                pre.next = cur.next;
            } else {
                // 如果当前节点的值不等于要删除的值,更新 pre 指针为当前节点
                pre = cur;
            }
            // 无论当前节点的值是否被删除,都移动 cur 指针到下一个节点
            cur = cur.next;
        }

        // 返回新的头节点,跳过虚拟头节点
        return dummyHead.next;
    }
}

设计链表

在链表类中实现这些功能:得到第 index 个节点的值,

在链表的第一个元素之前添加一个值为 val 的节点,

将值为 val 的节点追加到链表的最后一个元素,

在链表中的第 index 个节点之前添加值为 val  的节点,

删除链表中的第 index 个节点

707. 设计链表 - 力扣(LeetCode)

解法:单链表

class ListNode {
    int val; // 节点的值
    ListNode next; // 指向下一个节点的指针
    
    ListNode() {} // 默认构造函数
    ListNode(int val) { // 带值的构造函数
        this.val = val;
    }
}

class MyLinkedList {
    // size 存储链表元素的个数
    int size;
    // 虚拟头结点,便于处理头节点的插入和删除
    ListNode dummyHead;

    public MyLinkedList() {
        size = 0; // 初始化链表大小为 0
        dummyHead = new ListNode(0); // 创建虚拟头结点
    }
    
    // 获取链表中第 index 个节点的值,索引从 0 开始
    public int get(int index) {
        // 检查索引是否有效
        if (index < 0 || index >= size) {
            return -1; // 返回 -1 表示索引无效
        }

        ListNode curNode = dummyHead; // 从虚拟头节点开始
        // 遍历到目标索引的节点
        for (int i = 0; i <= index; i++) {
            curNode = curNode.next; // 移动到下一个节点
        }

        return curNode.val; // 返回目标节点的值
    }
    
    // 在链表头部添加一个新节点
    public void addAtHead(int val) {
        ListNode newNode = new ListNode(val); // 创建新节点
        newNode.next = dummyHead.next; // 新节点指向当前头节点
        dummyHead.next = newNode; // 虚拟头节点指向新节点
        size++; // 更新链表大小
    }
    
    // 在链表尾部添加一个新节点
    public void addAtTail(int val) {
        ListNode curNode = dummyHead; // 从虚拟头节点开始
        // 遍历到链表尾部
        while (curNode.next != null) {
            curNode = curNode.next; // 移动到下一个节点
        }
        ListNode newNode = new ListNode(val); // 创建新节点
        curNode.next = newNode; // 尾节点指向新节点
        size++; // 更新链表大小
    }
    
    // 在指定索引处添加一个新节点
    public void addAtIndex(int index, int val) {
        // 如果索引大于链表大小,无法添加
        if (index > size) {
            return;
        }
        // 如果索引小于 0,从头部插入
        if (index < 0) {
            index = 0;
        }
        ListNode newNode = new ListNode(val); // 创建新节点
        ListNode curNode = dummyHead; // 从虚拟头节点开始
        // 遍历到目标索引的前一个节点
        for (int i = 0; i < index; i++) {
            curNode = curNode.next; // 移动到下一个节点
        }
        // 插入新节点
        newNode.next = curNode.next; // 新节点指向当前位置的下一个节点
        curNode.next = newNode; // 前一个节点指向新节点
        size++; // 更新链表大小
    }
    
    // 删除指定索引的节点
    public void deleteAtIndex(int index) {
        // 检查索引是否有效
        if (index < 0 || index >= size) {
            return; // 索引无效,不进行任何操作
        }
        size--; // 先减少链表大小
        ListNode curNode = dummyHead; // 从虚拟头节点开始
        // 遍历到目标索引的前一个节点
        for (int i = 0; i < index; i++) {
            curNode = curNode.next; // 移动到下一个节点
        }
        // 删除目标节点
        curNode.next = curNode.next.next; // 前一个节点指向目标节点的下一个节点
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

双指针

反转链表

206. 反转链表 - 力扣(LeetCode)

class Solution {
    // 反转链表的核心方法,传入链表的头节点 head
    public ListNode reverseList(ListNode head) {
        // 如果链表为空或链表只有一个节点,则不需要反转,直接返回原链表的头节点
        if (head == null || head.next == null) {
            return head;  // 终止条件:空链表或只有一个节点
        }

        // 初始化两个指针:pre 用来跟踪已经反转的部分,cur 用来跟踪未反转的部分
        ListNode pre = head;   // pre 指向当前节点,初始时为链表的头节点
        ListNode cur = head.next;   // cur 指向下一个节点,初始时为头节点的下一个节点

        // 将头节点的 next 设为 null,因为反转后原来的头节点会成为尾节点,尾节点的 next 应该是 null
        head.next = null;

        // 开始遍历链表,直到 cur 为空(即链表末尾)
        while (cur != null) {
            ListNode temp = cur.next;   // 暂存 cur 的下一个节点,防止链表断开后丢失后续节点
            cur.next = pre;   // 将当前节点 cur 的 next 指向 pre,从而实现反转
            pre = cur;   // pre 前进到 cur,表示反转后的部分已经包括当前节点
            cur = temp;   // cur 前进到下一个节点(即之前暂存的节点),继续反转
        }

        // 当 cur 为 null 时,pre 指向的是反转后的新头节点,返回该节点
        return pre;
    }
}

两两交换链表中的节点

24. 两两交换链表中的节点 - 力扣(LeetCode)

// 定义链表节点类
public class ListNode {
    int val;  // 节点的值
    ListNode next;  // 指向下一个节点的指针

    // 无参构造函数
    ListNode() {}

    // 带有节点值的构造函数
    ListNode(int val) { 
        this.val = val; 
    }

    // 带有节点值和下一个节点的构造函数
    ListNode(int val, ListNode next) { 
        this.val = val; 
        this.next = next; 
    }
}

// 解决方案类,包含交换链表节点对的方法
class Solution {
    public ListNode swapPairs(ListNode head) {
        // 定义一个虚拟头节点 dummyHead,用来简化链表头部的操作
        ListNode dummyHead = new ListNode(0);  // 虚拟头节点,值为0,指向实际链表的头节点
        dummyHead.next = head;  // 将虚拟头的 next 指向传入的 head,便于处理链表的开头

        // 定义临时指针 temp,用来遍历链表。初始指向 dummyHead,方便处理链表头部
        ListNode temp = dummyHead;
        
        // node1 和 node2 分别用于指向要交换的两个相邻节点
        ListNode node1;
        ListNode node2;

        // 只要 temp 后面有两个节点存在(即 temp.next 和 temp.next.next 都不为空),就可以继续交换
        while (temp.next != null && temp.next.next != null) {
            // node1 指向第一对中第一个节点
            node1 = temp.next;
            // node2 指向第一对中第二个节点
            node2 = temp.next.next;

            // 开始交换:将 temp 的 next 指向第二个节点(node2)
            temp.next = node2;
            // 将第一个节点 node1 的 next 指向第二个节点 node2 的下一个节点,即交换后第一个节点应该指向的节点
            node1.next = node2.next;
            // 将第二个节点 node2 的 next 指向第一个节点 node1,完成交换
            node2.next = node1;

            // 将 temp 指向 node1,继续处理下一对
            temp = node1;  // node1 是交换后的第二个节点,所以 temp 移动到这里准备处理下一对节点
        }

        // 返回新链表的头节点,即 dummyHead 的 next
        return dummyHead.next;  // dummyHead 是虚拟头节点,实际链表的头节点在 dummyHead.next
    }
}

删除链表的倒数第N个节点

代码随想录 (programmercarl.com)

// 定义链表节点类
public class ListNode {
    int val; // 节点的值
    ListNode next; // 指向下一个节点的指针

    // 无参构造函数
    ListNode() {}

    // 带有节点值的构造函数
    ListNode(int val) {
        this.val = val;
    }

    // 带有节点值和下一个节点的构造函数
    ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

class Solution {
    // 移除链表中倒数第 n 个节点
    public ListNode removeNthFromEnd(ListNode head, int n) {
        // 边界检查,如果链表为空,直接返回 null
        if (head == null) {
            return null;
        }

        // 定义一个虚拟头节点,dummyHead 用来处理链表头部的特殊情况(例如删除头节点)
        ListNode dummyHead = new ListNode(0);
        dummyHead.next = head; // dummyHead.next 指向链表头节点

        // 定义快指针 fast 和慢指针 slow,都初始化为 dummyHead
        ListNode fast = dummyHead;
        ListNode slow = dummyHead;

        // 快指针 fast 先前进 n+1 步,以便与慢指针 slow 之间的距离为 n
        for (int i = 0; i <= n; i++) {
            fast = fast.next;
        }

        // 快慢指针同时向前移动,直到 fast 到达链表末尾
        while (fast != null) {
            fast = fast.next;
            slow = slow.next;
        }

        // 此时慢指针 slow 指向待删除节点的前一个节点,执行删除操作
        slow.next = slow.next.next;

        // 返回新的链表头节点(dummyHead.next),此时 dummyHead.next 是链表的头节点
        return dummyHead.next;
    }
}

哈希表

判断是否存在同一个元素

链表相交

面试题 02.07. 链表相交 - 力扣(LeetCode)

public class Solution {
    // 定义一个方法来获取两个链表的交点节点
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        // 使用一个 HashSet 来存储链表 A 的所有节点
        Set<ListNode> hashset = new HashSet<ListNode>();

        // 创建一个指针 cur,用于遍历链表 A
        ListNode cur = headA;
        // 遍历链表 A,直到链表的末尾
        while(cur != null){
            // 将当前节点添加到 HashSet 中
            hashset.add(cur);
            // 移动到下一个节点
            cur = cur.next;
        }

        // 重新使用 cur 指针遍历链表 B
        cur = headB;
        // 遍历链表 B,直到链表的末尾
        while(cur != null){
            // 如果当前节点在 HashSet 中,说明找到了交点
            if(hashset.contains(cur)){
                // 返回交点节点
                return cur;
            }
            // 移动到下一个节点
            cur = cur.next;
        }
        // 如果没有交点,返回 null
        return null;
    }
}

环形链表

142. 环形链表 II - 力扣(LeetCode)

public class Solution {
    public ListNode detectCycle(ListNode head) {
        
        // 创建一个 HashSet 用来存储访问过的节点
        Set<ListNode> hashset = new HashSet<>();
        
        // 初始化当前节点为链表的头节点
        ListNode cur = head;
        
        // 循环遍历整个链表
        while (cur != null) {
            // 如果当前节点已经存在于 HashSet 中,说明链表有环,且该节点就是环的起点
            if (hashset.contains(cur)) {
                return cur;  // 返回环的起点节点
            } else {
                // 如果当前节点不在 HashSet 中,将其添加到集合中,表示已经访问过该节点
                hashset.add(cur);
            }
            // 移动当前指针到下一个节点
            cur = cur.next;
        }

        // 如果遍历完整个链表都没有找到重复的节点,说明链表无环,返回 null
        return null;
    }
}

总结

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

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

相关文章

C++杂项

作业&#xff1a; 将之前实现的顺序表、栈、队列都更改成模板类 顺序表 #include <iostream>using namespace std;template<typename T>class SeqList { private:T *ptr;int size; //总长度int len 0; //当前顺序表实际长度public://初始…

Vue3.X + SpringBoot小程序 | AI大模型项目 | 饮食陪伴官

gitee平台源码 github平台源码 饮食陪伴师是一个管理饮食的原生大模型小程序&#xff0c;优势&#xff1a; 精确营养监控&#xff1a;用户记录饮食后&#xff0c;我们会计算出食用的营养成分与分量&#xff0c;并反馈给用户。饮食建议有效&#xff1a;大模型经过我们训练具备大…

中建材信云智联项目总监张瑞洲受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 中建材信云智联科技有限公司数字化事业部项目总监张瑞洲先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“电厂智能安全管控项目范围管理实践分享”。大会将于10月26-27日在北…

工具介绍---效率高+实用

Visual Studio Code (VS Code) 功能特点&#xff1a; 智能代码提示&#xff1a;内置的智能代码提示功能可以自动完成函数、变量等的输入&#xff0c;提高代码编写速度。插件丰富&#xff1a;支持成千上万的扩展插件&#xff0c;例如代码片段、主题、Linting等&#xff0c;能够…

67.【C语言】枚举类型

1.定义 对于有限的情况,一一列举 如一周有7天,从周一到周日;光学三原色(Red Green Blue) 2.格式 enum 枚举类型名 {//枚举常量 }; 备注:enum为enumeration缩写 3.枚举成员变量的值 #include <stdio.h> enum color {Red,Green,Blue };int main() {printf("%d…

alpine安装docker踩坑记

文章目录 前言错误场景正确操作最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;最近使用alpine操作系统上docker遇到了一些错误&#xff0c;尝试解决之后就准备输出一篇博客&#xff0c;帮助有需要的后人能够少踩坑&#xff0c;因为淋过雨所以想给别人撑伞 错误场景 我…

基于Hive和Hadoop的电信流量分析系统

本项目是一个基于大数据技术的电信流量分析系统&#xff0c;旨在为用户提供全面的通信数据和深入的流量使用分析。系统采用 Hadoop 平台进行大规模数据存储和处理&#xff0c;利用 MapReduce 进行数据分析和处理&#xff0c;通过 Sqoop 实现数据的导入导出&#xff0c;以 Spark…

Excel实现省-市-区/县级联

数据准备 准备省份-城市映射数据&#xff0c;如下&#xff1a; 新建sheet页&#xff0c;命名为&#xff1a;省-市数据源&#xff0c;然后准备数据&#xff0c;如下所示&#xff1a; 准备城市-区|县映射数据&#xff0c;如下&#xff1a; 新建sheet页&#xff0c;命名为&#x…

遗传算法与深度学习实战(15)——差分进化详解与实现

遗传算法与深度学习实战&#xff08;15&#xff09;——差分进化详解与实现 0. 前言1. 差分进化1.1 基本原理1.2 差分进化基本流程 2. 使用差分进化逼近复杂和不连续函数小结系列链接 0. 前言 深度学习 (Deep learning, DL) 系统通常可以被简单的视为凸函数逼近器&#xff0c;…

[Linux]从零开始的网站搭建教程

一、谁适合本次教程 学习Linux已经有一阵子了&#xff0c;相信大家对LInux都有一定的认识。本次教程会教大家如何在Linux中搭建一个自己的网站并且实现内网访问。这里我们会演示在Windows中和在Linux中如何搭建自己的网站。当然&#xff0c;如果你没有Linux的基础&#xff0c;这…

python画图|自制渐变柱状图

在前述学习过程中&#xff0c;我们已经通过官网学习了如何绘制渐变的柱状图及其背景。 掌握一门技能的最佳检验方式就是通过实战&#xff0c;因此&#xff0c;本文尝试做一些渐变设计。 前述学习记录可查看链接&#xff1a; Python画图|渐变背景-CSDN博客 【1】柱状图渐变 …

ArcGIS共享数据的最佳方法(不丢可视化、标注等各类显示信息一样带)

今天我们介绍一下ArcGIS数据共享的几个小妙招 我们时常要把数据发给对方&#xff0c;特别是很多新手朋友要将shp发给对方时只是发送了shp后缀的文件&#xff0c;却把shp的必要组成文件dbf、shx等等给落下了。 还有很多朋友给图层做好了符号化标注&#xff0c;但是数据一发给别…

详解调用钉钉AI助理消息API发送钉钉消息卡片给指定单聊用户

文章目录 前言准备工作1、在钉钉开发者后台创建一个钉钉企业内部应用&#xff1b;2、创建并保存好应用的appKey和appSecret&#xff0c;后面用于获取调用API的请求token&#xff1b;3、了解AI助理主动发送消息API&#xff1a;4、应用中配置好所需权限&#xff1a;4.1、权限点4.…

OkHttp 详细使用步骤,以及异步请求和同步请求

&#x1f604;作者简介&#xff1a; 小曾同学.com,一个致力于测试开发的博主⛽️&#xff0c;主要职责&#xff1a;测试开发、CI/CD 如果文章知识点有错误的地方&#xff0c;还请大家指正&#xff0c;让我们一起学习&#xff0c;一起进步。 &#x1f60a; 座右铭&#xff1a;不…

python编程开发“人机猜拳”游戏

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;开发者-曼亿点 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 曼亿点 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a…

Arduino UNO R3自学笔记6 之 Arduino引脚(IO)功能介绍

注意&#xff1a;学习和写作过程中&#xff0c;部分资料搜集于互联网&#xff0c;如有侵权请联系删除。 前言&#xff1a;Ardunio UNO R3有很多引脚&#xff0c;接下来主要介绍它们都可以用做什么。 从上图不难看出开发板引脚也不是有多少&#xff0c;分类来看也就以下种类型&…

翻译:Recent Event Camera Innovations: A Survey

摘要 基于事件的视觉受到人类视觉系统的启发&#xff0c;提供了变革性的功能&#xff0c;例如低延迟、高动态范围和降低功耗。本文对事件相机进行了全面的调查&#xff0c;并追溯了事件相机的发展历程。它介绍了事件相机的基本原理&#xff0c;将其与传统的帧相机进行了比较&am…

大数据-154 Apache Druid 架构与原理详解 基础架构、架构演进

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

最大正方形 Python题解

最大正方形 题目描述 在一个 n m n\times m nm 的只包含 0 0 0 和 1 1 1 的矩阵里找出一个不包含 0 0 0 的最大正方形&#xff0c;输出边长。 输入格式 输入文件第一行为两个整数 n , m ( 1 ≤ n , m ≤ 100 ) n,m(1\leq n,m\leq 100) n,m(1≤n,m≤100)&#xff0c;接…

[Linux]开发环境搭建

RPM和YUM 安装JDK 安装Tomcat 安装IDEA 安装MySql