专栏首页机器视觉CV学习链表,这些题你值得一刷!

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

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

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

掌握的程度

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

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

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

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

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

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

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

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

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

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

是下面这样写吗?答案是 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结点;

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

所以链表感觉像是:

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

首先,我们先来回顾一下单链表的插入和删除操作。如果我们在结点 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 指针都会一直指向这个哨兵结点。我们也把这种有哨兵结点的链表叫带头链表。相反,没有哨兵结点的链表就叫作不带头链表。

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

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

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

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

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

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

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

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

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

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

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

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

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

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

本文分享自微信公众号 - 机器视觉CV(AIandCV),作者:Leong

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 数据结构与算法-求取两个整数的最大公约数

    定理:两个正整数 a、b (a>b),它们的最大公约数等于 a 除以 b 的余数 c 和 b 之间的最大公约数

    机器视觉CV
  • 【注意力机制】空间注意力机制之Spatial Transformer Network

    2015 NIPS(NeurIPS,神经信息处理系统大会,人工智能领域的 A 类会议)论文

    机器视觉CV
  • 谷歌出品|推出了史上最强的Python在线编辑器

    今天给大家推荐一款超级强大的在线编辑器Colaboratory,Colaboratory 是一个谷歌提供的 Jupyter notebook环境,不需要进行任何...

    机器视觉CV
  • [日常] 链表-头结点和头指针的区别

    理解下头结点 1.头结点是为了操作的统一与方便而设立的,放在第一个元素结点之前,其数据域一般无意义(当然有些情况下也可存放链表的长度)。 2.有了头结点后,对在...

    陶士涵
  • [leetcode链表系列] 1 链表的中间节点

    ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NU...

    我是程序员小贱
  • 数据结构算法入门--链表

    数据结构算法入门系列第三篇--链表,链表也是非常常见的数据结构,面试过程中也会经常考到相关的题目。

    材ccc
  • 从尾到头打印链表

    题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值。 链表结点定义如下: struct ListNode { int m_nKey; ...

    猿人谷
  • 《数据结构》 循环链表和双向链表常用操作代码集合

    Ps:每段代码中,添加了署名Solo的代码为博主本人所写,其余来自课本或者老师。 大量操作等同于单链表。重复的操作不再贴出,可以查看之前的博文。 循环链表 //...

    Steve Wang
  • 数据结构 单链表&顺序表

    顺序表: 一般使用数组(C语言中的数组采用顺序存储方式。即连续地址存储)来描述。 优点:在于随机访问元素, 缺点:插入和和删除的时候,需要移动大量的元素。 链表...

    Kindear
  • Day57:二叉树的下一个结点

    思路一:   在做题前,我们应该给大家介绍二叉树的中序遍历的相关知识:在前面的文章里给大家介绍过二叉树的遍历。因此,这里就给大家不详细介绍了。现在给大家介绍解...

    stefan666

扫码关注云+社区

领取腾讯云代金券