链表——怎么写出正确的链表?

2023-05-16

链表

相比数组,链表不需要一块连续的内存空间,而是通过指针将一组零散的内存块串联起来使用,而这里的内存块就叫做节点,一般节点除了保存data还会保存下一个节点的地址也就是指针。
Node

单链表

  • 头节点 链表基地址
  • 尾节点next指向空
  • 插入 ,删除 O(1) ,改变相邻的节点指针即可
  • 不支持随机访问,需要循着指针一个一个找O(n)
    在这里插入图片描述

循环链表

  • 在单链表基础上,尾节点next指向头节点
  • 循环链表的优点是从链尾到链头比较方便
    在这里插入图片描述

双向链表

  • 链表节点较一般节点多了一个prev指针,用来指向该节点前面一个节点,如果存储同样多的数据,双向链表要比单链表占用更多的内存空间
  • 双向链表支持双向遍历,操作灵活,用空间换时间
    在这里插入图片描述

双向循环链表

  • 顾名思义,双向链表的尾节点next指向头节点,头节点prev指向尾节点
    在这里插入图片描述

特点

  • 链表不支持随机访问,需要循着指针逐个查找时间复杂度为O(n)
  • 删除和插入简单,只要改变相邻节点指针即可复杂度低为O(1)
  • 删除和插入实际都需要查找到节点位置才可以进行删除或插入,而查找是主要的耗时点O(n)

单链表和双向链表:
1. 删除“值等于某个给定值”的节点
无论是单链表还是双向链表,为了删除值等于给定值的节点,都需要从头节点开始逐个遍历,直至查找到该节点。虽然删除操作为O(1),但是查找耗时为O(n),则该操作时间复杂度为O(n)
2. 删除给定指针指向的节点或者第N个节点
同上,需要找到指定节点后,如果是单链表由于删除操作还需要知道该节点的前置节点,所以还需要再遍历链表,直至p.next==q,如果是双向链表就不需要再遍历一遍
3.相较之下,双向链表在查找和删除方面更有优势
4. 对于一个有序链表,双向链表的按值查询的效率也要比单链表高一些
因为,我们可以记录上次查找的位置 p,每次查询时,根据要查找的值与 p 的大小关系,决定是往前还是往后查找,所以平均只需要查找一半的数据
5.空间换时间
实际的软件开发中,双向链表尽管比较费内存,但还是比单链表的应用更加广泛。Java的LinkedHashMap容器的实现原理就用到了双向链表数据结构

技巧

1、理解指针或者引用

指针是C的概念,Java是没有指针的概念的,取而代之的是引用,不过都一样是用来存储所指对象的内存地址

p->next=q
//p的next指针类型变量存储了q的内存地址
class Node{
	Object data;
	Node next;
}
//...
node1.next = node2;
//node1的next属性存储了node2的引用即内存地址

无论指针还是引用,next变量不是下一个节点,next只是保存了下一个节点的内存地址即指向下一个节点

2、警惕指针丢失和内存泄漏

指针丢失

主要会发生在链表的插入和删除操作中,删除和操作需要改变相邻节点指针的指向,一定要主要指针指向变更操作的顺序,一不小心就会导致指针丢失,可以看一下下面操作:

//1
p.next = x
x.next = p.next

这里操作目的是在p节点后插入x节点,但实际操作后这里链表到x节点就断了,后续节点指针丢失。
插入操作需要先将后续节点指针赋值给新节点x,然后再将p节点指向新节点x,如下:

//2
x.next = p.next
p.next = x

内存泄漏

内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
删除链表结点时,要记得手动释放被删节点的内存空间,不过Java的虚拟机会自动管理内存,所以不需要手动释放。

//3、删除p节点后一个节点
p.next = p.next.next

3、利用哨兵简化代码

如上所示代码,如果插入操作是向空链表插入第一个节点或者删除的是链表的最后一个节点 (不是尾节点,是链表只剩下一个节点,再删除这个节点),上面的代码就不适用了,需要加入if判断来应对特殊情况,如下:

//空链表插入第一个节点
if (head == null) {
  head = new_node;
}
//删除链表的最后一个节点
if (head.next == null) { //链表就剩下一个节点了
   head = null;
}

这里的哨兵不参与业务逻辑,只是为了简化代码而存在。
一般表示一个空链表:

Node head = null;

引入哨兵节点作为头节点,无论链表是否为空链表,head一直指向这个哨兵;

Node head = new Node(data,next);

在这里插入图片描述
如上图所示,哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一用相同的代码实现逻辑了。

实际上,这种利用哨兵简化编程难度的技巧,在很多代码实现中都有用到,比如插入排序、归并排序、动态规划等。

4、注意临界条件处理

  • 如果链表为空,代码处理是否正常?
  • 如果链表只有一个节点,代码处理是否正常?
  • 如果链表只有两个节点,代码处理是否正常?
  • 代码逻辑在处理头节点和尾节点时是否正常?

5、画图举例,辅助思考

关于算法,可以先思考解法,如果有一会想不出也不要死磕,去直接看解法。
其中对于比较复杂的链表操作,可以用画图的方式来演示每一步操作,可以帮助理解算法。

6、常见链表操作

  1. 单链表反转 (p\q指针借助head调换位置)
  2. 链表中环的检测 (快慢指针)
  3. 两个有序的链表合并 (递归)
  4. 删除链表倒数第 n 个节点 (快慢指针)
  5. 求链表的中间结点 (快慢指针)

熄灯

写链表代码是最考验逻辑思维能力的;
对于算法问题的解法,首相能想到解法的不用考虑解法是否优秀直接尝试去解,想不到的不要死磕,直接看解法,毕竟优秀的算法很多都是图灵奖的得主。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

链表——怎么写出正确的链表? 的相关文章

随机推荐

  • OpenGL--使用Shader

    创建Shader 关于在OpenGL中怎么创建Shader这个在很早我博客中就有过详细介绍了 这里全当复习 xff0c 温故而知新 xff5e 在OpenGL中 xff0c 存在Program和Shader两个概念 xff0c Progra
  • 轻松学习CAN总线系列---3.CAN数据遵循的格式

    CAN帧 帧类型数据帧远程帧寻址CRC和应答位填充 帧类型 数据帧 xff08 Data frame xff09 为传输用户数据 xff0c ISO 11898 1定义了数据帧 数据帧可以传输的最大有效负载为八个字节 xff0c 即数据场
  • 无法解析的外部符号 "public: virtual struct CRuntimeClass

    无法解析的外部符号 34 public virtual struct CRuntimeClass thiscall CMessageBox GetRuntimeClass void const 34 以下原因是会引起上述错误的 xff1a
  • Qt控件美化 用好CSS/QSS可视化工具

    一 CSS概念 级联样式表 CSS 包含应用于网页中的元素的样式规则 CSS 样式定义元素的显示方式以及元素在页中的放置位置 可以创建一个通用规则 xff0c 只要 Web 浏览器遇到一个元素实例 xff0c 或遇到一个分配给某个特定样式类
  • c/c++中的struct和class的区别

    主要有两种情况 xff1a 1 C语言中的struct和c 43 43 中的class区别 2 c 43 43 中的struct和c 43 43 中的class的区别 下面分别介绍 xff1a 1 C语言中的struct和c 43 43 中
  • Linux应用层对串口的使用操作

    在Linux中串口作为字符设备 xff0c 设备节点在 dev 目录下 xff0c 使用普通的open xff0c close xff0c write和read等系统调用即可使用 这其中会涉及到一些串口的基本属性的设置 xff0c 如 波特
  • 给定两个字符串str1和str2,查找str2在str1中出现的位置

    给定string str1和string str2 xff0c 编写一个库函数 xff0c 返回str2在str1中的位置 如 xff1a str1为 34 ABCDLANCEXYZ 34 xff0c str2为 34 LANCE 34 x
  • 中国交通标志检测数据集

    版权声明 xff1a 本文为转发问 xff0c 原文见博客 https blog csdn net dong ma article details 84339007 中国交通标志检测数据集 xff08 CCTSDB xff09 来源于 A
  • CentOS 修改ls目录的颜色

    修改ls目录的颜色 linux系统默认目录颜色是蓝色的 xff0c 在黑背景下看不清楚 xff0c 可以通过以下2种方法修改ls查看的颜色 bash profile文件在用的根目录下 xff0c ls al可以看到 方法一 xff1a 1
  • Tiny210(S5PV210) U-BOOT(六)----DDR内存配置

    上次讲完了Nand Flash的低级初始化 xff0c 然后Nand Flash的操作主要是在board init f nand xff0c 中 xff0c 涉及到将代码从Nand Flash中copy到DDR中 xff0c 这个放到后面实
  • NAND FLASH命名规则

    基于网络的一个修订版 三星的pure nandflash xff08 就是不带其他模块只是nandflash存储芯片 xff09 的命名规则如下 xff1a 1 Memory K 2 NANDFlash 9 3 Small Classifi
  • s3c6410 DMA

    S3C6410中DMA操作步骤 xff1a 1 决定使用安全DMAC SDMAC 还是通用DMAC DMAC xff1b 2 开始相应DMAC的系统时钟 xff0c 并关闭另外一组的时钟 xff08 系统默认开启SDMA时钟 xff09 x
  • Visual Studio和VS Code的区别

    1 Visual Studio简介 xff1a 是一个集成开发环境 IDE xff0c 安装完成后就能直接用 xff0c 编译工具 xff0c 调试工具 xff0c 各个语言的开发工具 xff0c 都是已经配置好的 xff0c 开箱即用 适
  • 博客转移

    由于CSDN 文章不间断的会丢失图片 xff0c 然后逼格也不够高 xff0c 导致几年都没有写博客 xff0c 全部是记录至印象笔记中 xff0c 但是久了也不太好 xff0c 所以最近搞了一个自己的个人博客 xff0c 以后文章全部写至
  • 安装qt-everywhere-opensource-src-4.8.6

    1 下载 qt everywhere opensource src 4 8 6 http mirrors hust edu cn qtproject official releases qt 4 8 4 8 6 qt everywhere
  • CentOS6.5上安装qt-creator-opensource-linux-x86-3.1.2.run

    1 qt creator opensource linux x86 3 1 2 run的下载 wget http mirrors hustunique com qt official releases qtcreator 3 1 3 1 2
  • atoi()函数

    atoi 函数 原型 xff1a int atoi xff08 const char nptr xff09 用法 xff1a include lt stdlib h gt 功能 xff1a 将字符串转换成整型数 xff1b atoi 会扫描
  • ubuntu中printk打印信息

    1 设置vmware添加seria port 使用文件作为串口 2 启动ubuntu xff0c 修改 etc default grub GRUB CMDLINE LINUX DEFAULT 61 34 34 GRUB CMDLINE LI
  • 静态库、共享库、动态库概念?

    通常库分为 xff1a 静态库 共享库 xff0c 动态加载库 下面分别介绍 一 静态库 xff1a 1 概念 xff1a 静态库就是一些目标文件的集合 xff0c 以 a结尾 静态库在程序链接的时候使用 xff0c 链接器会将程序中使用
  • 链表——怎么写出正确的链表?

    链表 相比数组 xff0c 链表不需要一块连续的内存空间 xff0c 而是通过指针将一组零散的内存块串联起来使用 xff0c 而这里的内存块就叫做节点 xff0c 一般节点除了保存data还会保存下一个节点的地址也就是指针 单链表 头节点