【基础篇】5 # 链表(下):写好链表代码的六个实用技巧

说明

【数据结构与算法之美】专栏学习笔记

技巧一:理解指针或引用的含义

指针或引用都是存储所指对象的内存地址。将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针。

例如:

  • p —> next = q:表示 p 结点中的 next 指针存储了 q 结点的内存地址。
  • p —> next = p —> next —> next:表示 p 结点的 next 指针存储了 p 结点的下下一个结点的内存地址。

技巧二:警惕指针丢失和内存泄漏

例子:在结点 a 和相邻的结点 b 之间插入结点 x,假设当前指针 p 指向结点 a。

在这里插入图片描述

错误写法如下

p —> next = x;
x —> next = p —> next;

如果按照上面的写法,先将 p 的 next 指针指向 x 结点,然后将 x 的结点的 next 指针指向 b 结点。但是这里第一步操作完指针指向结点 x,第二步就是 x 自己指向自己,导致链表断裂,结点 b 往后的所有结点都无法访问到。

正确写法如下:顺序倒一下

x —> next = p —> next;
p —> next = x;

插入结点时,要注意操作的顺序,这样才不会丢失指针,导致内存泄漏。另外删除链表结点时,也一定要记得手动释放内存空间。

技巧三:利用哨兵简化实现难度

什么是哨兵?

例子1:空链表中插入第一个结点

if (head == null) {
    
    
	head = new_node;
}

例子2:删除链表中的最后一个结点

if (head -> next == null) {
    
    
   head = null;
}

针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。哨兵就是用来处理这些边界问题的,它不直接参与业务逻辑。

引入哨兵结点,不管链表是不是空,head 指针都会一直指向这个哨兵结点,并且一直存在,哨兵结点也不存储数据。

  • 带头链表:指有哨兵结点的链表
  • 不带头链表:指没有哨兵结点的链表

带头链表示意图:
在这里插入图片描述
删除最后一个节点的时候:

  • 在没有加哨兵的时候,这个时候用 p -> next = p -> next -> next 没用,就一个节点的话 p -> nextp -> next -> next 都是 null,相当于null = null。
  • 如果加上哨兵,哨兵是恒存在于链表中的,删除链表中的最后一个元素(是删除哨兵以外的最后一个,哨兵不参与业务逻辑)所以当哨兵后还跟着一个元素时,也就是有最后一个元素时,站在哨兵的位置依旧可以执行 p -> next = p -> next -> next,进而把最后一个删掉。

利用哨兵简化编程难度技巧的常见的有插入排序、归并排序、动态规划等。

技巧四:重点留意边界条件处理

检查链表代码是否正确的边界条件:

  • 如果链表为空时、链表只包含一个结点时、链表只包含两个结点时,代码是否可行?
  • 代码逻辑在处理头结点和尾结点的时候,代码是否可行?

技巧五:举例画图,辅助思考

对于稍微复杂的链表操作,可以使用举例法和画图法。

示例:往单链表中插入一个数据
在这里插入图片描述

技巧六:多写多练,没有捷径

熟练的写出一些常见的链表操作:

  • 单链表反转
  • 链表中环的检测
  • 两个有序的链表合并
  • 删除链表倒数第 n 个结点
  • 求链表的中间结点

猜你喜欢

转载自blog.csdn.net/kaimo313/article/details/128798875