前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >学习链表,这些题你值得一刷!

学习链表,这些题你值得一刷!

作者头像
机器视觉CV
发布2020-07-23 11:12:18
3950
发布2020-07-23 11:12:18
举报
文章被收录于专栏:机器视觉CV机器视觉CV

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

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

掌握的程度

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

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

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

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

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

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

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

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

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

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

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

代码语言:javascript
复制
p->next = x;  // 将p的next指针指向x结点;
x->next = p->next;  // 将x的结点的next指针指向b结点;

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

举个例子:

好比排队,如果每个人只知道自己后面是谁。此时,若在你后面加一个新人,这时,你憨憨的跑去告诉这位新人,说:你排在我后面就对了,那么这位新人一定非常懵逼,心想 “我后面排的是谁呢?” 真正的做法应该是,你应该告诉这位新人:“你现在排在我原本后面那个人的前面,然后你排在我前面”

又好比职场晋升,你是不是应该先把你手头的事交给另外一个人,然后再让这个人跟在你后面做事

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

代码语言:javascript
复制
x->next = p->next;  // 将x的结点的next指针指向b结点;
p->next = x;  // 将p的next指针指向x结点;

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

所以链表感觉像是:

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

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

代码语言:javascript
复制
new_node->next = p->next;
p->next = new_node;

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

代码语言:javascript
复制
if (head == null) {
  head = new_node;
}

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

代码语言:javascript
复制
p->next = p->next->next;

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

代码语言:javascript
复制
if (head->next == null) { 
    head = null;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 机器视觉CV 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 掌握的程度
  • 技巧一:理解指针或引用的含义
  • 技巧二:警惕指针丢失和内存泄漏
  • 技巧三:利用哨兵简化实现难度
  • 技巧四:重点留意边界条件处理
  • 技巧五:举例画图,辅助思考
  • 技巧六:多写多练,没有捷径
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档