链表mark

什么是链表,链表是一种通过指针串联在一起的线性结构,每一个节点由两部分组成,一个是数据域一个是指针域(存放指向下一个节点的指针),最后一个节点的指针域指向null(空指针的意思)。

链表的入口节点称为链表的头结点也就是head。

如图所示: 

链表1

链表的类型

接下来说一下链表的几种类型:

单链表

刚刚说的就是单链表。

双链表

单链表中的指针域只能指向节点的下一个节点。

双链表:每一个节点有两个指针域,一个指向下一个节点,一个指向上一个节点。

双链表 既可以向前查询也可以向后查询。

如图所示: 

链表2

循环链表

循环链表,顾名思义,就是链表首尾相连。

循环链表可以用来解决约瑟夫环问题。

链表4

链表的存储方式

了解完链表的类型,再来说一说链表在内存中的存储方式。

数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。

链表是通过指针域的指针链接在内存中各个节点。

所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理。

如图所示:

链表3

这个链表起始节点为2, 终止节点为7, 各个节点分布在内存的不同地址空间上,通过指针串联在一起。

链表的定义

接下来说一说链表的定义。

链表节点的定义,很多同学在面试的时候都写不好。

这是因为平时在刷leetcode的时候,链表的节点都默认定义好了,直接用就行了,所以同学们都没有注意到链表的节点是如何定义的。

而在面试的时候,一旦要自己手写链表,就写的错漏百出。

这里我给出C/C++的定义链表节点方式,如下所示:

链表的操作

删除节点

删除D节点,如图所示:

链表-删除节点

只要将C节点的next指针 指向E节点就可以了。

那有同学说了,D节点不是依然存留在内存里么?只不过是没有在这个链表里而已。

添加节点

如图所示:

链表-添加节点

可以看出链表的增添和删除都是O(1)操作,也不会影响到其他节点。

但是要注意,要是删除第五个节点,需要从头节点查找到第四个节点通过next指针进行删除操作,查找的时间复杂度是O(n)。

性能分析

再把链表的特性和数组的特性进行一个对比,如图所示:

链表-链表与数据性能对比

数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。

链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景。

class ListNode:def __init__(self, val, next=None):self.val = valself.next = next

例题:

203. 移除链表元素

简单

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

示例 1:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

提示:

  • 列表中的节点数目在范围 [0, 104] 内
  • 1 <= Node.val <= 50
  • 0 <= val <= 50

# 定义链表节点类
class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = next# 移除链表中指定值的节点的函数
def removeElements(head: ListNode, val: int) -> ListNode:# 创建一个虚拟头节点,方便处理头节点可能被删除的情况dummy = ListNode(next=head)prev = dummy  # 前一个节点curr = head  # 当前节点while curr:  # 只要当前节点不为空if curr.val == val:  # 如果当前节点的值等于要删除的值prev.next = curr.next  # 将前一个节点的下一个指针指向当前节点的下一个else:prev = curr  # 否则,更新前一个节点curr = curr.next  # 移动到下一个节点return dummy.next  # 返回新的头节点# 示例用法
head1 = ListNode(1, ListNode(2, ListNode(6, ListNode(3, ListNode(4, ListNode(5, ListNode(6)))))))
print(removeElements(head1, 6))head2 = ListNode()
print(removeElements(head2, 1))head3 = ListNode(7, ListNode(7, ListNode(7, ListNode(7))))
print(removeElements(head3, 7))

这段代码的逻辑是:使用一个虚拟头节点 dummy 来简化处理。通过两个指针 prev(前一个节点)和 curr(当前节点)遍历链表。如果当前节点的值等于目标值,就跳过当前节点,将前一个节点的下一个指针指向当前节点的下一个;否则就更新前一个节点。最后返回虚拟头节点的下一个节点,即新的头节点。

空间复杂度为 O(1),因为只使用了固定的额外空间来存储虚拟头节点和两个指针。时间复杂度为 O(n),需要遍历整个链表一次。

707. 设计链表

中等

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

  • MyLinkedList() 初始化 MyLinkedList 对象。
  • int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
  • void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
  • void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
  • void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
  • void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3

提示:

  • 0 <= index, val <= 1000
  • 请不要使用内置的 LinkedList 库。
  • 调用 getaddAtHeadaddAtTailaddAtIndex 和 deleteAtIndex 的次数不超过 2000 。

class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextclass MyLinkedList:def __init__(self):# 初始化一个虚拟头节点self.dummy = ListNode()def get(self, index):""" 获取指定下标的节点值 """curr = self.dummy.nextfor _ in range(index):if not curr:return -1curr = curr.nextif curr:return curr.valreturn -1def addAtHead(self, val):""" 在头部添加节点 """new_node = ListNode(val, self.dummy.next)self.dummy.next = new_nodedef addAtTail(self, val):""" 在尾部添加节点 """curr = self.dummywhile curr.next:curr = curr.nextcurr.next = ListNode(val)def addAtIndex(self, index, val):""" 在指定下标处添加节点 """prev = self.dummyfor _ in range(index):if not prev.next:returnprev = prev.nextnew_node = ListNode(val, prev.next)prev.next = new_nodedef deleteAtIndex(self, index):""" 删除指定下标的节点 """prev = self.dummyfor _ in range(index):if not prev.next:returnprev = prev.nextif prev.next:prev.next = prev.next.next# 测试用例
myLinkedList = MyLinkedList()
myLinkedList.addAtHead(1)
myLinkedList.addAtTail(3)
myLinkedList.addAtIndex(1, 2)
print(myLinkedList.get(1))  
myLinkedList.deleteAtIndex(1)
print(myLinkedList.get(1))  

代码逻辑和方法:

  • ListNode 类定义了链表节点。
  • MyLinkedList 类中:
    • __init__ 方法初始化一个虚拟头节点,方便操作。
    • get 方法通过遍历到指定下标获取节点值,如果不存在则返回 -1
    • addAtHead 直接在虚拟头节点后添加新节点。
    • addAtTail 遍历到尾部添加节点。
    • addAtIndex 先找到要插入位置的前一个节点,然后插入新节点。
    • deleteAtIndex 找到要删除节点的前一个节点,然后进行删除。

空间复杂度:主要是节点占用的空间,为 O(n),其中 n 是节点数量。
时间复杂度:

  • get 最坏情况需要遍历整个链表,为 O(n)。
  • addAtHeadaddAtTailaddAtIndexdeleteAtIndex 通常为 O(n)(找到特定位置的过程)。

206. 反转链表

简单

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]
输出:[2,1]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

1.以下是使用迭代方法反转链表的 Python 代码:

class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextclass Solution:def reverseList(self, head):"""迭代反转链表"""prev = None  # 前一个节点初始化为空curr = head  # 当前节点从头部开始while curr:  # 只要当前节点不为空next_node = curr.next  # 保存下一个节点curr.next = prev  # 当前节点的下一个指向改为前一个节点prev = curr  # 更新前一个节点为当前节点curr = next_node  # 移动到下一个节点return prev  # 最后返回反转后的头节点# 测试用例
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node5 = ListNode(5)node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5solution = Solution()
reversed_head = solution.reverseList(node1)curr = reversed_head
while curr:print(curr.val, end=" ")curr = curr.next

以上代码逻辑和方法:通过不断改变节点的指向来实现反转。用两个指针 prev 和 curr,遍历链表时,先保存下一个节点,然后将当前节点的下一个指向改为前一个节点,再更新前一个节点和移动当前节点到下一个节点。

空间复杂度:只使用了固定的额外空间来存储几个指针,所以空间复杂度为 O(1)。

时间复杂度:遍历整个链表一次,时间复杂度为 O(n)。

2.以下是使用递归方法反转链表的 Python 代码:

class ListNode:def __init__(self, val=0, next=None):self.val = valself.next = nextclass Solution:def reverseList(self, head):"""递归反转链表"""if not head or not head.next:  # 边界条件,链表为空或只有一个节点return headnew_head = self.reverseList(head.next)  # 递归反转后面的链表head.next.next = head  # 当前节点的下一个节点的下一个指向当前节点head.next = None  # 当前节点的下一个置空return new_head  # 返回反转后的头节点# 测试用例
node1 = ListNode(1)
node2 = ListNode(2)
node3 = ListNode(3)
node4 = ListNode(4)
node5 = ListNode(5)node1.next = node2
node2.next = node3
node3.next = node4
node4.next = node5solution = Solution()
reversed_head = solution.reverseList(node1)curr = reversed_head
while curr:print(curr.val, end=" ")curr = curr.next

代码逻辑和方法:递归到链表末尾,然后从后往前依次改变节点指向。

空间复杂度:由于递归调用会使用栈空间,在最坏情况下,栈空间的使用与链表长度成正比,所以空间复杂度为 O(n)。

时间复杂度:也是 O(n)。

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

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

相关文章

[数智人文实战] 02.舆情分析之词云可视化、文本聚类和LDA主题模型文本挖掘

【数智人文与文本挖掘】知识星球建立且正式运营,欢迎新老博友和朋友加入,一起分享更多数智人文知识和交流进步。该星球计划每周至少分享7个资源或文章,包括数智人文、文本挖掘、人工智能、大数据分析和图书情报的技术文章、代码及资源。同时,欢迎进入星球的朋友咨询我图情和…

操作系统课程实验1-进程调度模拟实验

操作系统课程实验1-进程调度模拟实验 一、实验介绍 1.1 实验目的 本实验模拟在单处理机环境下的处理机调度&#xff0c;帮助理解进程调度的概念&#xff0c;深入了解进程控制块的功能&#xff0c;以及进程的创建、撤销和进程各个状态间的转换过程。 1.2 实验内容 进程调度算…

NDIS小端口驱动开发(一)

在四种NDIS相关的驱动中&#xff0c;微型端口驱动(也经常翻译为为小端口驱动)位于驱动栈的底部&#xff0c;一般将它理解为NIC设备的驱动程序&#xff1a; 有几种类型的微型端口驱动程序类型&#xff1a; 无连接微型端口驱动程序用于控制无连接网络媒体 &#xff0c;如以太网的…

redis基本数据结构与应用

文章目录 概要String结构Hash结构List结构Set结构Zset结构bitmap位图类型geo地理位置类型其他常用命令 概要 redis常用的5种不同数据结构类型之间的映射如下&#xff1a; 结构类型结构存储的值结构的读写能力STRING可以是字符串、整数或者浮点数key-value形式&#xff1b;对整…

JMH304-剑侠情缘2网络版+2017纹饰端+翅膀+单机+外网整理+各种副本

资源介绍&#xff1a; 藏剑-太虚-梁山-杀手堂种树地宫师门纹饰装备长流云阳套等等———– 做登录器联系站长 资源截图&#xff1a; 下载地址

.NET周刊【5月第4期 2024-05-26】

国内文章 开源低代码框架 ReZero API 正式版本发布 &#xff0c;界面操作直接生成API https://www.cnblogs.com/sunkaixuan/p/18201175 ReZero是一款.NET6的中间件&#xff0c;采用MIT许可证开源&#xff0c;目的是降低.NET Core开发的门槛。它提供界面操作生成API的功能&am…

《Ai学习笔记》自然语言处理 (Natural Language Processing):常见机器阅读理解模型(上)02

Glove 词向量&#xff1a; 在机器理解中的词的表示&#xff1a; 词袋&#xff08;bow,bag of words&#xff09; one-hot 词向量 word2vec glove 目的&#xff1a;将一个词转换成一个向量 Word2vec 是一种用于生成词向量的工具包&#xff0c;由Google在2013年开源推出…

大规模语言模型的书籍分享

在当今人工智能领域&#xff0c;大规模语言模型成为了研究和应用的热点之一。它们以其大规模的参数和强大的性能表现&#xff0c;推动着机器学习和深度学习技术的发展。对于GPT 系列大规模语言模型的发展历程&#xff0c;有两点令人印象深刻。 第一点是可拓展的训练架构与学习范…

《TCP/IP网络编程》(第十二章)I/O复用(1)

本章将讨论实现并发服务器的第二种办法&#xff0c;基于I/O复用的服务器端构建。 I/O复用它允许单个进程或线程同时处理多个输入/输出&#xff08;I/O&#xff09;操作&#xff0c;而无需为每个I/O操作创建一个独立的线程或进程。这种技术可以显著提高应用程序的效率和性能&…

前端学习--React部分

文章目录 前端学习--React部分前言1.React简介1.1React的特点1.2引入文件1.3JSX&#x1f349;JSX简介与使用&#x1f349;JSX语法规则 1.4模块与组件&#x1f349;模块&#x1f349;组件 1.5安装开发者工具 2.React面向组件编程2.1创建组件&#x1f349;函数式组件&#x1f349…

Elasticsearch 认证模拟题 - 1

1、题目 定义一个数据流&#xff0c;满足 data-stream_*_*&#xff0c;数据首先分布在 data_hot&#xff0c;5分钟后移动到 data_warm&#xff0c;3分钟后到 data_cold&#xff0c;再过 8 分钟删除。 1.1 考点 生命周期索引模板数据流 1.2 答案 # 修改生命周期策略修改时间…

【算法】位运算算法——只出现一次的数字Ⅱ

题解&#xff1a;只出现一次的数字Ⅱ(位运算算法) 目录 1.题目2.题解&#xff1a;3.代码示例4.总结 1.题目 题目链接&#xff1a;LINK 要求&#xff1a;时间复杂度&#xff1a;O(N)&#xff0c;空间复杂度&#xff1a;O(1) 2.题解&#xff1a; 3.代码示例 class Solution {…

Java设计模式 _行为型模式_中介者模式

一、中介者模式 1、中介者模式 中介者模式&#xff08;Mediator Pattern&#xff09;是一种行为型模式。主要通过一个中介类&#xff0c;该类通常处理不同类之间的通信&#xff0c;并支持松耦合&#xff0c;使代码易于维护。 2、实现思路 &#xff08;1&#xff09;、定义实体…

【Linux】-Kafka集群安装部署[18]

简介 Apache Kafka是一款分布式的、去中心化的、高吞吐低延迟、订阅模式的消息队列系统。 同RabbitMQ一样&#xff0c;Kafka也是消息队列。不过RabbitMQ多用于后端系统&#xff0c;因其更加专注于消息的延迟和容错。 Kafka多用于大数据体系&#xff0c;因其更加专注于数据的…

K210 数字识别 笔记

一、烧写固件 连接k210开发板&#xff0c;点开烧录固件工具&#xff0c;选中固件&#xff0c;并下载 二、模型训练 网站&#xff1a;MaixHub 1、上传文件 2、开始标记数据 添加9个标签&#xff0c;命名为1~9&#xff0c;按键盘w开始标记&#xff0c;键盘D可以下一张图片&…

解密Prompt系列15. LLM Agent之数据库应用设计:DIN C3 SQL-Palm BIRD

上一章我们主要讲搜索引擎和LLM的应用设计&#xff0c;这一章我们来唠唠大模型和DB数据库之间的交互方案。有很多数据平台已经接入&#xff0c;可以先去玩玩再来看下面的实现方案&#xff0c;推荐 [sql translate]&#xff1a;简单&#xff0c;文本到SQL&#xff0c;SQL到文本…

后端企业级开发之yaml数据序列化格式文件详解2024

yaml格式 数据格式 yaml 是一种数据序列化的格式 容易阅读 容易与脚本语言交互 以数据为核心 重数据轻格式 我们要知道他怎么书写 大小写敏感 属性层级关系使用多行描述 每行结尾使用冒号结束 使用缩进表示层级关系 同层级左侧对其 只运行使用空格 属性前面添加空格 #表…

面试二十六、c++语言级别的多线程编程

一、 多线程编程 ​​​​​ 这里的c语言级别的多线程和linux的有一定的区别&#xff0c;c语言级别提供的多线程比较严格&#xff0c;如果主线程结束了&#xff0c;但是子线程没有结束&#xff0c;进程就会异常终止&#xff0c;而linux不会&#xff0c;会继续执行。 二、模拟卖…

Window VScode配置Conda教程(成功版)

VScode配置Conda 参考博文&#xff1a;https://blog.csdn.net/qq_51831335/article/details/126757014Anaconda安装&#xff08;注意勾选自动配置环境变量&#xff01;&#xff09; 官网&#xff1a;https://www.anaconda.com/download/success VScode配置 python插件安装安装 …

关于Java程序入口args参数

关于Java程序入口args参数 我们都知道一个java工程会有一个主程序入口&#xff0c;即我们常见到的如下代码 public static void main(String[] args) {// 中间程序 }但是很多人可能都不知道主程序入口中的String[] args后面的args会有怎么样一个使用场景呢&#xff1f; 其实在…