空指针赋值 链表_链表:今日对我爱答不理,明天让你高攀不起!

本文总结了在面试中常见的链表题目及解题技巧,包括理解指针含义、避免指针丢失和内存泄漏、使用哨兵简化实现、处理边界条件、举例画图辅助思考以及强调多刷题的重要性。特别提到在链表插入和删除操作中,哨兵节点可以统一代码逻辑,提高代码的简洁性和正确性。
摘要由CSDN通过智能技术生成

247e0d888b2792a04b356857ce906eee.png

链表非常重要!虽然理论内容不多,但链表上的操作却很复杂。所以,面试中经常会考察,你一定要掌握。

笔者最近刷了十几道链表相关的题,总结了一些技巧分享给大家。

掌握的程度

经典的链表题目包括:比如链表反转、求中间结点等,都需要轻松无 bug 地实现出来。

链表这一块真的是靠刷题,笔者之前也是对链表的知识一窍不通,理解也不是很深刻,通过刷题,现在基本上能够写出常见的链表操作题了~

以下分享几个解决链表题的技巧

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

链表的主要难点在于指针的操作,需要深刻理解指针的含义

将某个变量赋值给指针,实际上就是将这个变量的地址赋值给指针,或者反过来说,指针中存储了这个变量的内存地址,指向了这个变量,通过指针就能找到这个变量。

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

插入结点时,一定要注意操作的顺序

不知道你有没有这样的感觉,写链表代码的时候,指针指来指去,一会儿就不知道指到哪里了。所以,我们在写的时候,一定注意不要弄丢了指针。

举个例子:要在 a 和 b 节点之间加一个 x 节点:

bc4391d14ba4910672ae3eedecb77ab6.png

是下面这样写吗?答案是 NO!!!下面这样写是错误的,但是它非常符合我们的直觉,这也是容易出错的原因。那么到底哪出错了呢?

p->next = x;  // 将p的next指针指向x结点;
x->next = p->next;  // 将x的结点的next指针指向b结点;

p->next 指针在完成第一步操作之后,已经不再指向结点 b 了,而是指向结点 x。第 2 行代码相当于将 x 赋值给 x->next,自己指向自己。因此,整个链表也就断成了两半,从结点 b 往后的所有结点都无法访问到了。

举个例子:

好比排队,如果每个人只知道自己后面是谁。此时,若在你后面加一个新人,这时,你憨憨的跑去告诉这位新人,说:你排在我后面就对了,那么这位新人一定非常懵逼,心想 “我后面排的是谁呢?”
真正的做法应该是,你应该告诉这位新人:“你现在排在我原本后面那个人的前面,然后你排在我前面”
又好比职场晋升,你是不是应该先把手头上的事交给另外一个人,然后再让这个人跟在你后面办事

所以正确的应该是(上面的代码交互一下顺序)

x->next = p->next;  // 将x的结点的next指针指向b结点;
p->next = x;  // 将p的next指针指向x结点;

也许我说了这么多,你应该还是不理解其中的含义,那么就多刷题吧~

所以链表感觉像是:

f87264d447c5158063c400fce5f6648d.png

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

首先,我们先来回顾一下单链表的插入和删除操作。如果我们在结点 p 后面插入一个新的结点(new_node),只需要下面两行代码就可以搞定。

new_node->next = p->next;
p->next = new_node;

但是,当我们要向一个空链表中插入第一个结点,刚刚的逻辑就不能用了。我们需要进行下面这样的特殊处理,其中 head 表示链表的头结点。所以,从这段代码,我们可以发现,对于单链表的插入操作,第一个结点和其他结点的插入逻辑是不一样的。

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

我们再来看单链表结点删除操作。如果要删除结点 p 的后继结点,我们只需要一行代码就可以搞定。

p->next = p->next->next;

但是,如果我们要删除链表中的最后一个结点,前面的删除代码就不 work 了。跟插入类似,我们也需要对于这种情况特殊处理。写成代码是这样子的:

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

针对链表的插入、删除操作,需要对插入第一个结点和删除最后一个结点的情况进行特殊处理。

这样代码实现起来就会很繁琐,不简洁,而且也容易因为考虑不全而出错。如何来解决这个问题呢?

还记得如何表示一个空链表吗?head=null 表示链表中没有结点了。其中 head 表示头结点指针,指向链表中的第一个结点。

如果我们引入哨兵结点,在任何时候,不管链表是不是空,head 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。

下面图中展示的是一个带头链表,你可以发现,哨兵结点是不存储数据的。因为哨兵结点一直存在,所以插入第一个结点和插入其他结点,删除最后一个结点和删除其他结点,都可以统一为相同的代码实现逻辑了。

9b30bfb4ef799168064c4bd0f7370d7c.png

如果你不知道什么时候要使用哨兵节点,那就无脑使用吧~~

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

经常用来检查链表代码是否正确的边界条件有这样几个:

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

同时可能需要留意的点还有

  • 如果要切断一个链表,那么需要把前半部分链表的最后一个节点的 next 指针指向空

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

画图帮助理解,能够让你快速的写出 Bug Free 的代码,也能搞清楚其中的逻辑

922bb6c54f08b4df5e714eddb24a535a.png

解决链表问题的一个比较常用的思想是:快慢指针

快慢指针在解决环形链表、找链表第 K 个位置的值时非常有用,需要认真掌握

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

以下是我刷过的有关链表的题目

书山有路勤为径,熟能生巧是良计!!!

b55493d215ed6bd445a58b0cee5e5dfb.png

刷题记录分享

笔者将自己的数据结构与算法刷题过程记录在语雀的一个仓库中,有兴趣的看一下

数据结构与算法学习之路 · 语雀​www.yuque.com
c653cc1165b41afef0eb0cf64d344de6.png

http://weixin.qq.com/r/qUNvd9TEU4ksrZAU9xa7 (二维码自动识别)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值