当前位置: 首页 > news >正文

《剑指offer》-数据结构篇-链表

题目

  1. 从尾到头打印链表

  2. 反转链表

  3. 链表中倒数第K个结点

  4. 合并两个/K个有序链表

  5. 复杂链表的复制

  6. 删除链表中的重复元素

  7. 两个链表的第一个公共结点

  8. 链表中环的入口结点

  9. 二叉搜索树与双向链表

代码实现

从尾到头打印链表

题目:输入一个链表,按链表从尾到头的顺序返回一个ArrayList。

思路:

  • 方式1:先加载链表中的所有值到新建的列表中,然后将该列表反转,返回一个ArrayList。

  • 方式2:借助辅助栈,一个栈存储链表中由前至后结点值,另一个栈存储该栈依次弹出结点值。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:# 返回从尾部到头部的列表值序列,例如[1,2,3]def printListFromTailToHead(self, listNode):# write code here"""#解法1:先加载链表中的所有值到列表中,然后将该列表反转,返回一个ArrayListret=[]head=listNodewhile(head):ret.append(head.val)head=head.nextret.reverse()return ret"""#解法2:利用辅助栈来解决stack = []result_array = []node_p = listNodewhile node_p:stack.append(node_p.val)node_p = node_p.next while stack:result_array.append(stack.pop(-1))return result_array

反转链表

题目描述:输入一个链表,反转链表后,输出新链表的表头。

思路:注意和第1题的区别,此题返回的是一个链表。设置None,从头指针开始依次改变指针的指向。注意:需用pHead.next作为判断条件,而非pHead,否则会导致头节点为 None,出现错误。反转链表很典型,一些链表的复杂题目是在其基础上产生的。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:# 返回ListNodedef ReverseList(self, pHead):# write code here#判断特殊情况#对于pHead的长度为1的情况依然是适用的if not pHead: return None pre = None #反转指针的指向#这里一定要是判断pHead.next,用pHead的话pHead的头节点是None,会出现问题while pHead.next:nex = pHead.next pHead.next = pre pre = pHead pHead = nex pHead.next = prereturn pHead

链表中倒数第K个结点

题目描述:输入一个链表,输出该链表中倒数第k个结点。

思路:采用快慢指针的方式。让一个指针先走k步,当该指针到达None时,另一个指针指向该链表中的倒数第k个结点,返回该指针所对应的结点值。快慢指针的方式的题目比较重要,很多链表题目都是使用这种方法求解的。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:def FindKthToTail(self, head, k):# write code here#考虑特殊条件if not head or k<=0: return None#快慢指针的初始位置fast_p = headslow_p = head #确保可以得到与最初的fast_p位置相隔k个结点的新的新的fast_p位置#该位置也是和slow_p相距k个结点的位置for _ in range(k):if fast_p:fast_p = fast_p.nextelse:return None#由于快慢指针相距为k,设原链表的长度为n,则最终慢指针的位置为n-k+1#即倒数第k个结点的位置while fast_p:fast_p = fast_p.nextslow_p = slow_p.nextreturn slow_p #使用slow_p返回该节点的数值

合并两个/K个有序链表

题目一:合并两个有序链表

题目描述:输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。

思路:构建新的列表存储两个单调递增的链表的数值,对列表进行排序生成新的列表后,再由其新的生成链表。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:# 返回合并后列表def Merge(self, pHead1, pHead2):# write code herep1 = pHead1p2 = pHead2#新建一个链表l = list()  #替换为[]会导致执行的速度下降#加载链表的值到列表while p1 or p2:if p1:l.append(p1.val)p1 = p1.nextif p2:l.append(p2.val)p2 = p2.nextl.sort()#构建新的链表for i in range(len(l)):if i ==0:l3 = ListNode(l[i])p3 = l3else:p3.next = ListNode(l[i])p3 = p3.next#注意返回的是链表,而不是最后一个结点,因而用了l3return l3 if l else None 

题目二:合并K个有序链表

题目:leetcode链接(https://leetcode-cn.com/problems/merge-k-sorted-lists/)

思路:分治递归,详见:合并K个有序链表

N是所有链表中元素的总数,K是链表数量,时间复杂度为NlogK,空间复杂度为logK

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:def mergeKLists(self, lists: List[ListNode]) -> ListNode:       if len(lists) <= 2: return self.mergeTwoLists(lists)def splitLists(lists):idx = len(lists) // 2return lists[:idx], lists[idx:]a, b = splitLists(lists)a_merge = self.mergeKLists(a)b_merge = self.mergeKLists(b)return self.mergeTwoLists([a_merge, b_merge])def mergeTwoLists(self, lists):if not lists: return Noneif len(lists)==1: return lists[0]head1, head2 = listshead = dump = ListNode(0)while head1 and head2:if head1.val < head2.val:head.next = head1head1 = head1.nextelse:head.next = head2head2 = head2.nexthead = head.nexthead.next = head1 if head1 else head2return dump.next

复杂链表的复制

题目描述:输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的head。(注意,输出结果中请不要返回参数中的节点引用,否则判题程序会直接返回空)

思路:先复制按照指向正常结点的结点所构成的链表,再在基础上复制指向任意结点的结点所构成的链表,最终输出复杂链表。

# -*- coding:utf-8 -*-
# class RandomListNode:
#     def __init__(self, x):
#         self.label = x
#         self.next = None
#         self.random = None
class Solution:# 返回 RandomListNodedef Clone(self, pHead):# write code here#判断链表不为空if not pHead: return Nonep = pHeadnew_h = RandomListNode(p.label)pre_p = new_hrandom_map = {pHead: new_h}p = p.next#先完成对于链表的复制while p:new_p = RandomListNode(p.label)random_map[p] = new_ppre_p.next = new_ppre_p = pre_p.nextp = p.nextp = pHeadnew_p = new_h#然后完成对于自由结点的复制while p:random_p = p.randomif random_p:new_p.random = random_map[random_p]p = p.nextnew_p = new_p.next#返回复杂链表return new_h 

删除链表中的重复元素

题目描述:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5

思路:依次遍历链表中的数值,将不重复结点的指针指向下一个不重复的结点,最后记得输出整个链表而非得到的链表中的最后一个结点的数值。题目很典型,类似的题目是:对于链表中的重复结点,只保留一次数值。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:def deleteDuplication(self, pHead):# write code heredummy_head = ListNode(None) dummy_head.next = pHead pre = dummy_head cur = pHead while cur: if cur.next and cur.val == cur.next.val: while cur.next and cur.val == cur.next.val: cur = cur.next # cur.val != cur.next.val pre.next = cur.next cur = cur.next continue pre = pre.next cur = cur.next#要用.next return dummy_head.next

扩展题目:删除链表中的重复元素(只保留重复元素中的一个)

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

思路:和第6题类似,只是指针指向的不同。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:def deleteDuplicates(self, head: ListNode) -> ListNode:""":type head: ListNode:rtype: ListNode"""cur = headwhile cur:while cur.next and cur.val == cur.next.val:cur.next = cur.next.nextcur = cur.next#注意返回headreturn head

两个链表的第一个公共结点

题目描述:输入两个链表,找出它们的第一个公共结点。

思路:

方法1:两个指针循环遍历链表,指针指向的数值相同时,说明是第一个公共结点。

方法2:找到两个链表的长度差k。让一个指针指向较长的链表的第k+1个结点,一个指针指向较短的链表的第一个结点,当指针指向的数值相同时,说明是第一个公共结点。

注意:题目描述中的两个链表A和B自公共结点L之后的结点都是一样的。

# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:def FindFirstCommonNode(self, pHead1, pHead2):# write code here"""解决方案1#判断条件if pHead1 is None or pHead2 is None:return Nonepa = pHead1pb = pHead2        #对于两个链表,相交的第一个节点之后的部分也是相同的while pa != pb:if pa:pa = pa.nextelse:pa = pHead2if pb:pb = pb.nextelse:pb = pHead1#pa==pb对应的是相交的节点。注意这里是返回节点,因而也可以是返回pa。return pb"""#解决方法2:采用对其最后的结点对齐的方式,使得较长的链表的#头结点先领先两者的长度差大小的步数,之后指针指向相同的结点值时就找到了第一个公共结点p1 = pHead1p2 = pHead2n_p1 = 0n_p2 = 0while p1:p1 = p1.nextn_p1 += 1while p2:p2 = p2.nextn_p2 += 1if n_p1 < n_p2:pHead1, pHead2 = pHead2, pHead1for _ in range(n_p1 - n_p2):pHead1 = pHead1.nextwhile pHead1:if pHead1 == pHead2:return pHead1else:pHead1 = pHead1.nextpHead2 = pHead2.nextreturn None

链表中环的入口结点

题目描述:给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

思路:利用快慢指针。刚开始指针都指向链表的第一个结点,先找到两个指针相遇的结点值。之后,根据链表的第一个结点到环入口的步数s等于相遇点到环入口的步数(r-m)加上环的长度r的整数倍进行while判断,当满足等式约束时返回该结点的数值。具体讲解见https://leetcode-cn.com/problems/linked-list-cycle-ii/solution/kuai-man-zhi-zhen-by-powcai-4/。

代码:
# -*- coding:utf-8 -*-
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:def EntryNodeOfLoop(self, pHead):# write code hereif not pHead or not pHead.next :return None# 快慢指针 slow = pHeadfast = pHead# 重新开始 start = pHead while fast and fast.next: slow = slow.next fast = fast.next.next #找到相遇点#根据初始点到环入口的步数s等于相遇点到环入口的步数(r-m)加上环的长度r的整数倍进行while判断#即s=(n-1)r+(r-m)if slow == fast: while slow != start: slow = slow.next start = start.next return slow #返回的是该节点的数值

扩展题目:判断链表是否是环形链表

题目来自leetcode(https://leetcode-cn.com/problems/linked-list-cycle/)

题目描述:给定一个链表,判断链表中是否有环。为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

思路:和第8题类似,只是返回的是True或False。这里不需要满足第8题的约束条件,因为对于快慢指针而言,不是“环形链表”的话,就不会相遇。

# Definition for singly-linked list.
# class ListNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.next = Noneclass Solution(object):def hasCycle(self, head):""":type head: ListNode:rtype: bool"""if not head:return Falsewalker = headrunner = head.nexttry:while walker!=runner:walker = walker.nextrunner = runner.next.nextreturn Trueexcept:return False

二叉搜索树与双向链表

题目描述:输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

思路:先进行中序遍历,然后改变链表的指针指向。

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:def treeToList(self,root):if not root:return []return self.treeToList(root.left)+[root]+self.treeToList(root.right)def Convert(self, pRootOfTree):# write code herelist1=self.treeToList(pRootOfTree)if len(list1)==0:return Noneif len(list1)==1:return pRootOfTreefor i in range(len(list1)-1):list1[i].right=list1[i+1]list1[i+1].left=list1[i]return list1[0] 

结尾

亲爱的读者朋友:感谢您在繁忙中驻足阅读本期内容!您的到来是对我们最大的支持❤️

正如古语所言:"当局者迷,旁观者清"。您独到的见解与客观评价,恰似一盏明灯💡,能帮助我们照亮内容盲区,让未来的创作更加贴近您的需求。

若此文给您带来启发或收获,不妨通过以下方式为彼此搭建一座桥梁: ✨ 点击右上角【点赞】图标,让好内容被更多人看见 ✨ 滑动屏幕【收藏】本篇,便于随时查阅回味 ✨ 在评论区留下您的真知灼见,让我们共同碰撞思维的火花

我始终秉持匠心精神,以键盘为犁铧深耕知识沃土💻,用每一次敲击传递专业价值,不断优化内容呈现形式,力求为您打造沉浸式的阅读盛宴📚。

有任何疑问或建议?评论区就是我们的连心桥!您的每一条留言我都将认真研读,并在24小时内回复解答📝。

愿我们携手同行,在知识的雨林中茁壮成长🌳,共享思想绽放的甘甜果实。下期相遇时,期待看到您智慧的评论与闪亮的点赞身影✨!

万分感谢🙏🙏您的点赞👍👍、收藏⭐🌟、评论💬🗯️、关注❤️💚~


自我介绍:一线互联网大厂资深算法研发(工作6年+),4年以上招聘面试官经验(一二面面试官,面试候选人400+),深谙岗位专业知识、技能雷达图,已累计辅导15+求职者顺利入职大中型互联网公司。熟练掌握大模型、NLP、搜索、推荐、数据挖掘算法和优化,提供面试辅导、专业知识入门到进阶辅导等定制化需求等服务,助力您顺利完成学习和求职之旅(有需要者可私信联系) 

友友们,自己的知乎账号为“快乐星球”,定期更新技术文章,敬请关注!    

http://www.dtcms.com/a/297919.html

相关文章:

  • GDB调试命令学习
  • spring boot项目使用Spring Security加密
  • k8s开启审计日志
  • 【SSL证书校验问题】通过 monkey-patch 关掉 SSL 证书校验
  • Fluent遇上AI:深度学习重塑计算流体动力学的未来
  • 【记录】C++生产者 / 消费者 案例
  • 刷题日记0725
  • 篇五 网络通信硬件之PHY,MAC, RJ45
  • PytorchLightning最佳实践基础篇
  • 谷歌母公司Alphabet发布超预期业绩,提高全年资本支出至850亿美元
  • 从 Elastic 到 ClickHouse:日志系统性能与成本优化之路
  • 【大模型实战】提示工程(Prompt Engineering)
  • 优秀案例:基于python django的智能家居销售数据采集和分析系统设计与实现,使用混合推荐算法和LSTM算法情感分析
  • 九联UNT413AS_晶晨S905L3S芯片_2+8G_安卓9.0_线刷固件包
  • 短剧小程序系统开发:构建影视娱乐生态新格局
  • Spring Boot License 认证系统
  • C#(数据类型)
  • k8s的存储之secerts
  • Python数据可视化利器:Matplotlib全解析
  • 智能制造——解读39页MOM数字化工厂平台解决方案【附全文阅读】
  • Linux网络配置全攻略:IP、路由与双机通信
  • 北京-4年功能测试2年空窗-报培训班学测开-第六十天-准备项目中
  • 图的遍历:深度优先与广度优先
  • SpringBoot学习路径二--Spring Boot自动配置原理深度解析
  • Qt 状态机框架:复杂交互逻辑的处理
  • R 语言绘制六种精美热图:转录组数据可视化实践(基于 pheatmap 包)
  • 从零开始学习Dify-数据库数据可视化(五)
  • java的设计模式及代理模式
  • 负载均衡:提升业务性能的关键技术
  • Zabbix告警系统集成指南:从钉钉机器人到网易邮件的全流程配置