点击上方“小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达

1 知识点

1.1 什么是链表

提到链表,我们大家都不陌生,在平时的编码中我们也或多或少地使用过这个数据结构。算法(第4版) (豆瓣)一书中对链表的定义如下:

链表是一种递归的数据结构,它或者为空(null),或者是指向一个结点(node)的引用,该节点还有一个元素和一个指向另一条链表的引用。

链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而顺序表相应的时间复杂度分别是O(logn)和O(1)。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

1.2 链表结构

1.2.1 单向链表

链表中最简单的一种是单向链表,它包含两个域,一个信息域和一个指针域。这个链接指向列表中的下一个节点,而最后一个节点则指向一个空值。

一个单向链表包含两个值: 当前节点的值和一个指向下一个节点的链接

一个单向链表的节点被分成两个部分。第一个部分保存或者显示关于节点的信息,第二个部分存储下一个节点的地址。单向链表只可向一个方向遍历。

1.2.2 双向链表

一种更复杂的链表是“双向链表”或“双面链表”。每个节点有两个连接:一个指向前一个节点,(当此“连接”为第一个“连接”时,指向空值或者空列表);而另一个指向下一个节点,(当此“连接”为最后一个“连接”时,指向空值或者空列表)。

一个双向链表有三个整数值: 数值, 向后的节点链接, 向前的节点链接

双向链表也叫双链表双向链表中不仅有指向后一个节点的指针,还有指向前一个节点的指针。这样可以从任何一个节点访问前一个节点,当然也可以访问后一个节点,以至整个链表。一般是在需要大批量的另外储存数据在链表中的位置的时候用。双向链表也可以配合下面的其他链表的扩展使用。

1.2.3 循环链表

在一个 循环链表中, 首节点和末节点被连接在一起。这种方式在单向和双向链表中皆可实现。要转换一个循环链表,你开始于任意一个节点然后沿着列表的任一方向直到返回开始的节点。再来看另一种方法,循环链表可以被视为“无头无尾”。这种列表很利于节约数据存储缓存, 假定你在一个列表中有一个对象并且希望所有其他对象迭代在一个非特殊的排列下。

指向整个列表的指针可以被称作访问指针。

用单向链表构建的循环链表

1.3链表相关的核心点

  • null/nil 异常处理

  • dummy node 哑巴节点

  • 快慢指针

  • 插入一个节点到排序链表

  • 从一个链表中移除一个节点

  • 翻转链表

  • 合并两个链表

  • 找到链表的中间节点

2 常见题型

83. 删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

思路:直接法,遇到重复就跳过

class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:cur = headwhile cur and cur.next:if cur.val == cur.next.val:cur.next = cur.next.nextelse:cur = cur.nextreturn head

82. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

思路:定义哑节点指向链表头,定义双指针,一个指向当前节点,一个指向前一个节点。当有重复的元素时,cur指向下一位,直到下一位与当前节点不相等,用flag标记表示有重复数字,当循环完毕,pre的下一位指向cur的下一位,flag标记归位

当没有重复数字,pre指向cur

class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:if not head or not head.next:  # 空链表或单即节点链表return headdummy = ListNode(0)    # 定义哑节点dummy.next = headpre = dummy            # 哑节点指针cur = head             # 链表指针flag = False           # 标记是否重复while cur:while cur.next and pre.next.val == cur.next.val:cur = cur.next   # 将所有重复节点标记为Trueflag = Trueif flag is True:     # 重复节点就跳过pre.next = cur.nextflag = Falseelse:pre = cur        # 不重复就下一个节点cur = cur.nextreturn dummy.next

206. 反转链表

反转一个单链表。

思路1:迭代,每次存储前一个节点,并让当前节点的next指针指向前一个节点

思路2:递归,假设我子节点下的所有节点都已经反转好了,现在就剩我和我的子节点没有完成最后的反转了,所以反转一下我和我的子节点。

# 迭代
class Solution:def reverseList(self, head: ListNode) -> ListNode:pre = Nonecur = headwhile cur:temp = cur.next   # 存储节点的下一位cur.next = pre    # 指向前驱节点pre = cur         cur = temp        # cur指向原来的下一位return pre
# 递归
class Solution:def reverseList(self, head: ListNode) -> ListNode:if not head or not head.next:return headp = self.reverseList(head.next)head.next.next = headhead.next = Nonereturn p

92. 反转链表 II

反转从位置 mn 的链表。请使用一趟扫描完成反转。

思路:用双指针法,现将慢指针移动到要反转的部分的前一个节点,用另一个指针指向慢指针后面的节点,然后两两交换节点

class Solution:def reverseBetween(self, head: ListNode, m: int, n: int) -> ListNode:dummy = ListNode(None)dummy.next = headpre = dummyfor i in range(1, m):pre = pre.next         # 前驱指针移动到位置mcur = pre.nextfor i in range(m, n):      # 两两交换节点temp = cur.next        cur.next = temp.next   # cur一直不变,只修改指针到下一个位置temp.next = pre.next   # temp.next指向pre的next,也就是最新的第m个位置pre.next = temp        # 更新temp为最新的第m个位置return pre

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

思路:建立新链表,依次按升序链接两个链表的元素

class Solution:def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:dummy = ListNode(None)pre = dummywhile l1 and l2:if l1.val < l2.val:pre.next = l1l1 = l1.nextelse:pre.next = l2l2 = l2.nextpre = pre.nextpre.next = l1 if l1 else l2return dummy.next

86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

思路:创建双链表,一个存储小于x的值,一个存储大于x的值

class Solution:def partition(self, head: ListNode, x: int) -> ListNode:before = befornHead = ListNode(None)after = afterHead = ListNode(None)while head:if head.val < x:before.next = headbefore = before.nextelse:after.next = headafter = after.nexthead = head.nextafter.next = Nonebefore.next = afterHead.nextreturn befornHead.next

148. 排序链表

O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

思路:归并排序,找中点和合并操作

# 归并排序(递归),空间复杂度O(n)
class Solution:def sortList(self, head: ListNode) -> ListNode:if not head or not head.next:return headslow, fast = head, head.nextwhile fast and fast.next:slow, fast = slow.next, fast.next.nextmid = slow.nextslow.next = Noneleft = self.sortList(head)right = self.sortList(mid)h = res = ListNode(0)while left and right:if left.val < right.val: h.next, left = left, left.nextelse: h.next, right = right, right.nexth = h.nexth.next = left if left else rightreturn res.next
# 快速排序
class Solution:def sortList(self, head: ListNode) -> ListNode:if head is None:return head# 分成三个链表,分别是比轴心数小,相等,大的数组成的链表big = Nonesmall = Noneequal = Nonecur = headwhile cur:temp = curcur = cur.nextif temp.val < head.val:temp.next = smallsmall = tempelif temp.val > head.val:temp.next = bigbig = tempelse:temp.next = equalequal = temp# 拆完各自排序即可,equal 无需排序big = self.sortList(big)small = self.sortList(small)ret = ListNode(None)cur = ret# 将三个链表组合成一起# 可以同时返回链表的头指针和尾指针加速链表的合并。for p in [small, equal, big]:while p is not None:cur.next = pp = p.nextcur = cur.nextcur.next = Nonereturn ret.next

143. 重排链表

给定一个单链表 LL0→L1→…→L**n-1→Ln , 将其重新排列后变为:L0→LnL1→Ln-1→L2→Ln-2→…

思路:找到中点断开,翻转后面部分,然后合并前后两个链表

class Solution:def reorderList(self, head: ListNode) -> None:"""Do not return anything, modify head in-place instead."""if not head or not head.next:return headslow, fast = head, head# 找到链表中点while fast.next and fast.next.next:   slow = slow.nextfast = fast.next.next# 反转后半部分print(slow.next)right = Nonecur = slow.nextwhile cur:temp = cur.nextcur.next = rightright = curcur = temp# 将反转的后半部分插入到前半部分left = headwhile left and right:slow.next = right.nextright.next = left.nextleft.next = rightleft = right.nextright = slow.next

141. 环形链表

给定一个链表,判断链表中是否有环。

思路1:哈希表,统计每个元素出现的次数

思路2:快慢指针,快慢指针相同则有环,证明:如果有环每走一步快慢指针距离会减 1

# 哈希表
class Solution:def hasCycle(self, head: ListNode) -> bool:dic = {}while head:if head in dic:return Trueelse:dic[head] = 1head = head.nextreturn False
# 快慢指针
class Solution:def hasCycle(self, head: ListNode) -> bool:if head is None or head.next is None:return Falseslow = headfast = slow.nextwhile fast and fast.next:fast = fast.next.nextslow = slow.nextif fast == slow:return Truereturn False

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。如果链表无环,则返回 null

思路:快慢指针,快慢相遇之后,慢指针回到头,快慢指针步调一致一起移动,相遇点即为入环点

class Solution:def detectCycle(self, head: ListNode) -> ListNode:fast, slow = head, headwhile True:if not (fast and fast.next):return slow = slow.nextfast = fast.next.nextif slow == fast:breakfast = headwhile fast != slow:fast = fast.nextslow = slow.nextreturn fast

138. 复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的 深拷贝

思路:1、hash 表存储指针,2、复制节点跟在原节点后面

class Solution:def copyRandomList(self, head: 'Node') -> 'Node':if not head:returndic = {}dic[None] = Nonecur = headwhile cur:if cur not in dic:dic[cur] = Node(cur.val)     # 将当前未拷贝的节点放入字典中if cur.random not in dic:dic[cur.random] = Node(cur.random.val)   # 将当前未拷贝的random指针放入字典中dic[cur].random = dic[cur.random]if cur.next not in dic:dic[cur.next] = Node(cur.next.val)     # 将当前未拷贝的next指针放入字典中dic[cur].next = dic[cur.next]cur = cur.nextreturn dic[head]

234. 回文链表

请判断一个链表是否为回文链表。

思路1:将值复制到数组中,时间复杂度O(n),空间复杂度O(n)

思路2:快慢指针,先找到链表中点,然后反转后半部分,再与前半部分比较,时间复杂度O(n)空间复杂度O(1)

# 将值复制到数组中
class Solution:def isPalindrome(self, head: ListNode) -> bool:visit = []cur = headwhile cur:visit.append(cur.val)cur = cur.nextreturn visit == visit[::-1]
# 找中点 -> 反转 -> 比较
class Solution:def isPalindrome(self, head: ListNode) -> bool:pre = Noneslow, fast = head, headwhile fast and fast.next:slow = slow.next      # 使慢指针指向中间节点fast = fast.next.nextwhile slow:  # 反转后半部分,pre指向反转部分temp = slow.nextslow.next = prepre = slowslow = tempwhile head and pre:   # 比较两部分是否相同if head.val != pre.val:return Falsehead = head.nextpre = pre.nextreturn True

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。

下载2:Python视觉实战项目52讲

在「小白学视觉」公众号后台回复:Python视觉实战项目即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。

下载3:OpenCV实战项目20讲

在「小白学视觉」公众号后台回复:OpenCV实战项目20讲即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。

交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~

算法工程师面试必考项——链表相关推荐

  1. 算法工程师面试必考项:二叉树

    点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送达 1 二叉树简介 二叉树是最基本的数据结构之一,二叉树(Binary ...

  2. 【深度学习】算法工程师面试必考点:Dropout和R-Dropout的使用技巧

    作者 | BetterBench 出品 | 对白的算法屋 编者寄语: 搞懂Dropout和R-Drop看这篇就够了. 上一篇R-Drop:提升有监督任务性能最简单的方法,很多小伙伴们都私信我说,让我介 ...

  3. 环形链表【手绘漫画】面试必考之双指针(LeetCode 141)

    文章目录 图解算法与数据结构 1.前言 2.实例 3.正文 4.代码 图解算法与数据结构 1.前言 今天开始的是双指针! 下面一起来看看吧!!! 让我们从一个经典问题开始: 环形链表进阶版[手绘漫画] ...

  4. 算法工程师面试问题及相关资料集锦(附链接)

    来源:专知 本文约9800字,建议阅读20分钟. 本文为你介绍算法工程师面试问题及相关资料集锦,相当全面,值得收藏. 目录 算法工程师 Github.牛客网.知乎.个人博客.微信公众号.其他 机器学习 ...

  5. 算法工程师面试问题及资料超详细合集(多家公司算法岗面经/代码实战/网课/竞赛等)

    这里是算法江湖,传授AI武林秘籍. 资源目录: 一.算法工程师 Github.牛客网.知乎.个人博客.微信公众号.其他 二.机器学习 面试问题.资料.代码实战 三.深度学习 面试.资料.代码实战Pyt ...

  6. 面试必考的网络协议相关题目应该如何回答

    转载自  面试必考的网络协议相关题目应该如何回答 最近,正处于校招季.很多小伙伴已经拿到了如愿以偿的Offer,有些小伙伴还在努力着. 平常,也会有一些粉丝会在公众号留言,或者在微信上问我一些面试题. ...

  7. 最强Java面试题全部合集,涵盖BAT大厂面试必考的9大技术!-强烈建议收藏

    过去2年我持续分享了BAT TMD为代表的大厂最新面试题目,特别是蚂蚁金服.天猫.淘宝.头条.拼多多等Java面试题目. 过去2年,我已经成功的帮助了部分同学进入了大厂. 2020开始,我依然会为大家 ...

  8. 华为算法工程师面试经历汇总

    一.概述 华为公司组织架构较为庞大,其总体组织架构图如下所示: 其中,较为知名的2012实验室,其下面包括的二级部门有:中央硬件工程学院.海思.研发能力中心.中央软件院.诺亚方舟实验室: 其中中央软件 ...

  9. 机器学习/推荐系统/推荐系统算法工程师面试指导

    面试指导 文章目录 面试指导 1.机器学习/推荐系统/推荐系统算法工程师面试技能图 2.1 推荐系统算法相关面试笔试题 2.2 机器学习相关问题 重点: 2.3 框架方面 2.4 业务流程 3.简历指 ...

最新文章

  1. python 内置函数
  2. 漫画:毕昇 JDK,重现了 “活字印刷术” 的传奇
  3. arcsde9.3 the arcsde repository is not successfully created
  4. 洛谷P3327:[SDOI2015]约数个数和(莫比乌斯反演)
  5. 显示照片的二维直方图
  6. 12_04_Linux软件管理之四yum
  7. MongoDB固定集合(capped collection)
  8. nginx css 304 导致图片丢失_Nginx面试三连问:如何工作?负载均衡策略有哪些?如何限流?...
  9. 点云的密度 曝光时间_200倍的提速!华人博士生提出大场景三维点云语义分割新框架...
  10. 数学建模可以用python吗_Python中常用的数学建模Scipy
  11. IDC:阿里云安全能力和IaaS市场份额双项领先
  12. java实现扫雷小游戏【完整版】
  13. 电容麦克风测试软件,大家都在用的测试话筒大搜罗
  14. (超详细)搜索软件Everything的安装与使用
  15. Android Studio入门级教程(详细)【小白必看】
  16. 大数据的产业链分析,大数据完整的产业链构成
  17. 难为知己,难为敌-职场之我见
  18. 用示波器实现跳舞视频
  19. 利用Pandas拆分Excel的单元格为多行并保留其他行的数据
  20. vlc 视频局部放大【WPF版】

热门文章

  1. 2097352GB地图数据,AI技术酷炫渲染,《微软飞行模拟器》游戏即将上线
  2. 测试工程师的好日子来啦?Testin发布AI测试产品,提升易用性和自动化效率
  3. 人工智能时代,开发者是逆袭还是走向末日?
  4. 李彦宏:简单搜索永远没有广告;安全是自动驾驶第一天条
  5. 阿里资深AI工程师教你逐个击破机器学习核心算法
  6. 前端抱怨 API 响应慢,怎么办?
  7. Spring Boot 五种热部署方式,极速开发就是生产力!
  8. 面试官问:对象池技术了解吗?apache common pool2呢?
  9. 如何优雅的实现 Spring Boot 接口参数加密解密?
  10. JDK1.8源码分析:线程安全的CopyOnWriteArrayList与CopyOnWriteArraySet