【PythonCode】力扣Leetcode21~25题Python版

news2024/12/26 10:59:46

【PythonCode】力扣Leetcode21~25题Python版

前言

力扣Leetcode是一个集学习、刷题、竞赛等功能于一体的编程学习平台,很多计算机相关专业的学生、编程自学者、IT从业者在上面学习和刷题。
在Leetcode上刷题,可以选择各种主流的编程语言,如C++、JAVA、Python、Go等。还可以在线编程,实时执行代码,如果代码通过了平台准备的测试用例,就可以通过题目。
本系列中的文章从Leetcode的第1题开始,记录我用Python语言提交的代码和思路,供Python学习参考。

21. 合并两个有序链表

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

示例 1:
在这里插入图片描述
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
两个链表的节点数目范围是 [0, 50]
-100 <= Node.val <= 100
l1 和 l2 均按 非递减顺序 排列

代码实现:

class Solution:
    def mergeTwoLists(self, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
        listm = ListNode(0, next=None)
        cur = listm
        while list1 and list2:
            if list1.val <= list2.val:
                cur.next = list1
                list1 = list1.next
            else:
                cur.next = list2
                list2 = list2.next
            cur = cur.next
        cur.next = list1 if list1 is not None else list2
        return listm.next

解题思路:要将两个升序的链表合并成一个新的升序链表,可以从两个链表的头节点开始,依次比较节点的大小,将更小的节点添加到新的链表中。

先新建一个链表,在这个链表中初始化一个值为0的节点,作为前置节点。将待合并的节点依次添加到此链表中,合并完成后返回此链表的第二个节点至末尾,就是需要的结果。

当两个待合并的链表都不为空时,取两个链表的头节点进行比较,并将更小的节点添加到新链表中。节点被添加后,对应链表的头指向下一个节点,相当于将已合并的节点删除。链表头指向的节点不断往后移动,就是两个指针在移动,不断更新链表的头,比较大小时就只需要关注头节点。

当其中一个链表的节点被合并完成后,另一个链表中剩余节点的值都更大,此时,不需要再比较,直接将这些剩余的节点添加到新链表的后面。

在往新链表中添加节点时,也是用一个指针,指针一直都指向新链表的最后一个节点,要添加的节点直接加在指针后面。(链表基础,参考:Python实现单向链表)

22. 括号生成

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

示例 1:
输入:n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]
示例 2:
输入:n = 1
输出:[“()”]
提示:
1 <= n <= 8

代码实现:

class Solution:
    def generateParenthesis(self, n: int) -> List[str]:
        result = []
        def backtrack(temp, left, right):
            if len(temp) == 2 * n:
                result.append(temp)
                return
            if left < n:
                temp += '('
                backtrack(temp, left+1, right)
                temp = temp[:-1]
            if right < left:
                temp += ')'
                backtrack(temp, left, right+1)
                temp = temp[:-1]
        backtrack('', 0, 0)
        return result

解题思路:本题需要生成n对有效的小括号,小括号由左括号和右括号两个部分组成,最终生成的结果长度为2n,包含n个左括号和n个右括号。在生成有效括号的过程中,右括号的个数不能多于左括号的个数,否则括号是无效的。

本题求的是所有可能的括号组合,可以用回溯法,通过不断的回溯,穷举出所有的组合可能性。参考:循序渐进,搞懂什么是回溯算法。(前面的力扣17题也用了回溯算法,可以结合一起看)

按照回溯法的求解步骤,先定义回溯的解空间,本题是求所有可能的括号组合,所以结果是一个字符串数组。回溯时,搜索树的高度最高为2n,深度优先遍历时每次往字符串中添加一个左括号或右括号。当左括号的个数小于n时,可以往字符串中添加左括号,当字符串中的右括号个数小于左括号个数时,可以往字符串中添加右括号。这样可以保证括号组合一直是有效的,当字符串长度达到2n时,得到一种可能的组合,将组合添加到结果列表中。往回回溯时,去掉字符串的最后一个字符,通过回溯穷举所有的可能性,最后得到所有可能的括号组合。

结合代码,首先初始化结果数组result,回溯时的括号字符串temp。然后定义回溯函数backtrack(),在回溯函数中,如果括号字符串长度达到2n,就找到一种组合,把当前的括号字符串temp保存到result中,如果长度没有达到2n,就继续按括号有效的规则往字符串中添加左括号或右括号,往回回溯时,将上一个添加的括号从temp中移除,也就是将temp中的最后一个字符移除。最后调用回溯函数backtrack(),从左括号和右括号个数都为0个开始,执行回溯函数。

23. 合并 K 个升序链表

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:
链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4

代码实现:

class Solution:
    def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
        return self.merge(lists, 0, len(lists)-1)

    def merge(self, lists, left, right):
        if left == right:
            return lists[left]
        if left > right:
            return
        mid = (left + right) // 2
        return self.mergeTwoLists(self.merge(lists, left, mid), self.merge(lists, mid+1, right))

    def mergeTwoLists(self, list1, list2):
        listm = ListNode(0, next=None)
        cur = listm
        while list1 and list2:
            if list1.val <= list2.val:
                cur.next = list1
                list1 = list1.next
            else:
                cur.next = list2
                list2 = list2.next
            cur = cur.next
        cur.next = list1 if list1 is not None else list2
        return listm.next

解题思路:合并k个升序链表是21题合并两个升序链表的升级版,因此我们可以调用合并两个链表的代码,来重复合并操作。最简单的方式就是从第一个和第二个链表开始依次合并,直到合并完k个链表。但这种方式需要合并k-1次,前面的链表被反复遍历,时间复杂度太高,提交代码会超时。

要将时间复杂度降低,就要减少合并的次数,所以考虑的合并策略是二分法合并。用倒推的思路,将k个链表分成两份,如果两部分都合并完成,则将这两个链表合并在一起就完成了,这两个部分再分别递归地往下二分,直到最后分的链表剩一个或两个。

如果从前往后依次合并,假设每个链表的长度为n,第一个和第二个链表合并需要遍历两个链表,时间复杂度为2n,随着每次合并,其中一个链表的长度逐渐变成2n,3n,…,子链表的长度越来越长,需要合并k-1次,最后一次合并时,前k-1个链表合并成的子链表长度为(k-1)*n,与第k个链表合并时间复杂度为k*n,所以整体的时间复杂度为2n+3n+…+kn=(k/2)*k*n(约等于),时间复杂度为O(k2*n)。

使用二分法合并时,最后一次合并两个链表的长度都为(k/2)*n,时间复杂度为k*n。往前推时每次合并的时间复杂度依次分别为(k/2)*n,(k/4)*n,…,并且时间复杂度为(k/2)*n的操作需要做两次,时间复杂度为(k/4)*n的操作需要做四次,以此类推,所以每一轮的合并时间复杂度都为k*n。k个链表二分法合并需要进行logk轮的合并,所以时间复杂度为O(k*logk*n)。O(k*logk*n)的时间复杂度优于O(k2*n)。

最后看一下代码,两个链表合并的代码直接复用,二分法合并时,使用递归的方式对k个链表进行二分,分到剩一个或两个的情况,代码往回递归,完成合并。

24. 两两交换链表中的节点

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

示例 1:
在这里插入图片描述
输入:head = [1,2,3,4]
输出:[2,1,4,3]
示例 2:
输入:head = []
输出:[]
示例 3:
输入:head = [1]
输出:[1]
提示:
链表中节点的数目在范围 [0, 100] 内
0 <= Node.val <= 100

代码实现:

class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        prev = ListNode(0, head)
        temp = prev
        while temp.next and temp.next.next:
            nodea = temp.next
            nodeb = temp.next.next
            nodea.next = nodeb.next  # 第一步
            temp.next = nodeb  # 第二步
            nodeb.next = nodea  # 第三步
            temp = nodea
        return prev.next

解题思路:题目要求两两交换链表的节点,也就是将链表的第一个节点与第二个节点交换位置,第三个节点与第四个节点交换位置,… 以此类推,不能修改节点内部的值,也就是不能将第一个节点的值改成第二个节点的值,第二个节点的值改成第一个节点的值。

要满足节点的交换,就是要修改节点的next指向关系,为了方便说明,可以参考下图:

在这里插入图片描述
创建一个前置节点,将前置节点的next指向链表的头,创建一个临时指针指向前置节点,创建两个指针指向临时节点后面的两个节点,这两个节点就是需要交换位置的节点。

交换需要执行三次next指针修改,分为三个步骤:

第一步,将第一个节点的next指向第二个节点的next(也就是指向第三个节点,可能为空)。

第二步,将前置节点的next指向第二个节点。

第三步,将第二个节点的next指向第一个节点。

修改next指针后,原来的指针“断掉”了,此时,看链表新的指向关系(图中红线),第一个节点和第二个节点的位置已经交换了。

在这里插入图片描述

要继续交换链表后面的节点(如第三个和第四个),将临时指针指向第三个节点的前一个节点,也就是节点1,并更新临时指针后两个指针指向的节点。如此循环,直到链表中的节点剩余0个或1个,则交换完成。返回前置节点的后一个节点就是结果。

25. K 个一组翻转链表

给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。

k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。

你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。

示例 1:
在这里插入图片描述
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2:
在这里插入图片描述
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
提示: 链表中的节点数目为 n
1 <= k <= n <= 5000
0 <= Node.val <= 1000

代码实现:

class Solution:
    def reverseKGroup(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
        prev = ListNode(0, head)
        temp = prev
        k_head = head 
        while k_head:
            node = []
            cur = temp
            for _ in range(k):
                node.append(cur.next)
                cur = cur.next
                if not cur:
                    return prev.next
            k_head = cur.next
            node[0].next = k_head  # 第一步
            temp.next = node[-1]  # 第二步
            for i in range(1, k):  # 第三步
                node[i].next = node[i-1]
            temp = node[0]
        return prev.next

解题思路:本题是上一题24题的升级版,上一题是两两翻转节点的位置,本题是k个一组翻转(所以要先看懂上一题再看本题)。翻转思路与两两翻转的思路相通,在两两翻转的时候,每一组的节点是两个。

k个节点一组翻转时,翻转方法也可以分为三个步骤,为了方便说明,可以参考下图:

在这里插入图片描述

首先还是创建一个前置节点,将前置节点的next指向链表的头,创建一个临时指针指向前置节点。不同的是这里创建一个k_head指针指向k个结点的头,以便代码中找到这k个结点。

第一步,将这组节点中的第一个节点的next指向最后一个节点的next。

第二步,将临时指针的next指向这组节点的最后一个节点。

第三步,在这组节点内部,将除第一个节点的next都指向它的前一个节点。

执行这三步后,就完成了一组节点的翻转。此时将临时指针指向组内的第一个节点,该节点就是下一组待翻转节点的前置节点,并更新k_head指针,如此循环,当链表中剩余的节点数量不足k个时,翻转完成,返回前置节点的后一个节点,就是本题的结果。


相关阅读

【PythonCode】力扣Leetcode16~20题Python版

📢欢迎 点赞👍 收藏⭐ 评论📝 关注 如有错误敬请指正!

☟ 学Python,点击下方名片关注我。☟

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1800981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【会议征稿,IEEE出版】EEI 2024,6月28-30

第六届电子工程与信息学国际学术会议&#xff08;EEI 2024&#xff09;将于2024年6月28日至6月30日在中国重庆召开。EEI 2024将围绕“电子工程”、“信息学”与“计算机科学”等相关最新研究领域 &#xff0c;为来自国内外高等院校、科学研究所、企事业单位的专家、教授、学者、…

海康威视综合安防管理平台 多处 FastJson反序列化RCE漏洞复现

0x01 产品简介 海康威视综合安防管理平台是一套“集成化”、“智能化”的平台,通过接入视频监控、一卡通、停车场、报警检测等系统的设备。海康威视集成化综合管理软件平台,可以对接入的视频监控点集中管理,实现统一部署、统一配置、统一管理和统一调度。 0x02 漏洞概述 由于…

javacv ffmpeg使用笔记 (补充中...)

javacv ffmpeg使用笔记 一、maven依赖二、示例代码1. 获取视频时长 三、小技巧 一、maven依赖 使用javacv ffmpeg并指定classifier之后&#xff0c;就不需要额外安装ffmpeg软件&#xff08;jar包中已经内置&#xff09;了。 全量依赖包&#xff08;不推荐&#xff09;安装包总大…

Docker 管理 | 代理配置、内网共享和 Harbor 部署

唠唠闲话 在现代软件开发和运维中&#xff0c;容器技术已经成为构建、部署和管理应用程序的标准工具。然而&#xff0c;在实际操作中&#xff0c;我们常常需要面对一些常见的挑战&#xff0c;如容器访问外部资源的代理配置、内网环境下的镜像共享以及企业级镜像管理。 本教程…

Transformer动画讲解:注意力计算Q、K、V

暑期实习基本结束了&#xff0c;校招即将开启。 不同以往的是&#xff0c;当前职场环境已不再是那个双向奔赴时代了。求职者在变多&#xff0c;HC 在变少&#xff0c;岗位要求还更高了。提前准备才是完全之策。 最近&#xff0c;我们又陆续整理了很多大厂的面试题&#xff0c…

自动化Reddit图片收集:Python爬虫技巧

引言 Reddit&#xff0c;作为一个全球性的社交平台&#xff0c;拥有海量的用户生成内容&#xff0c;其中包括大量的图片资源。对于数据科学家、市场研究人员或任何需要大量图片资源的人来说&#xff0c;自动化地从Reddit收集图片是一个极具价值的技能。本文将详细介绍如何使用…

GCB | 全球草地和森林土壤呼吸对降水量变化的不对称响应

全球变化导致地球水文循环的强化放大了降水的年际变化&#xff0c;这将显著影响陆地碳&#xff08;C&#xff09;循环。然而&#xff0c;在极端降水变化情况下&#xff0c;先前观测到的土壤呼吸&#xff08;Rs&#xff09;和降水之间的关系是否仍然适用&#xff0c;目前仍不清楚…

thinkphp6.0版本下子查询sql处理

目录 一&#xff1a;背景 二&#xff1a;查询实例 三&#xff1a;总结 一&#xff1a;背景 我们在实际业务的开发过程中&#xff0c;经常会碰到这样的场景&#xff0c;查询某些部门的客户信息&#xff0c;查询下过订单的客户信息。这里查询客户信息实际上就用到了子查询&…

PS的stable diffusion插件安装指南

PS的stable diffusion插件安装指南 1.首先要安装stable diffusion&#xff0c;具体安装方法&#xff0c;参考https://blog.csdn.net/sheji888/article/details/139196688 stable diffusion要求要启用API功能 2.安装ps2023以上版本&#xff0c;低于这个版本不能使用stable diff…

TCP攻击是怎么实现的,如何防御?

TCP&#xff08;Transmission Control Protocol&#xff09;是互联网协议族中的重要组成部分&#xff0c;用于在不可靠的网络上提供可靠的数据传输服务。然而&#xff0c;TCP协议的一些特性也使其成为攻击者的目标&#xff0c;尤其是DDoS&#xff08;Distributed Denial of Ser…

马斯克五步流程法在产品创新中的实践与应用

引言&#xff1a; 在科技创新的浪潮中&#xff0c;埃隆马斯克以其独到的思维方式和创新实践&#xff0c;引领着多个行业的前沿。他提出的“第一性原理”下的五步流程法&#xff0c;不仅是对创新过程的深刻洞见&#xff0c;也为产品经理和工程师们提供了一套行之有效的工作方法。…

【Redis】Redis经典问题:缓存穿透、缓存击穿、缓存雪崩

目录 缓存的处理流程缓存穿透解释产生原因解决方案1.针对不存在的数据也进行缓存2.设置合适的缓存过期时间3. 对缓存访问进行限流和降级4. 接口层增加校验5. 布隆过滤器原理优点缺点关于扩容其他使用场景SpringBoot 整合 布隆过滤器 缓存击穿产生原因解决方案1.设置热点数据永不…

强国机械制造有限公司引入先进制造技术,提升产品质量和生产效率

强国机械制造有限公司2024年6月3日宣布引入了一系列先进制造技术,包括机器学习、人工智能和物联网等,旨在提升其产品的质量和生产效率。这些前沿技术的应用,使得公司的制造过程更加智能化和数据驱动,显著提高了产品的精度和稳定性。 通过机器学习算法,强国机械能够分析和预测生…

【Mybatis】动态SQL标签3

foreach标签是使用举例 在实际应用中&#xff0c;我常常需要根据多个id批量的操作&#xff1a; 查询指定id的记录&#xff1a; 这时就可以用foreach标签&#xff1a; collection"ids" &#xff1a; 接口上传过来的数值或list集合或者map集合都可以 item"id&…

区块链技术:供应链金融的革新者与引领者

一、引言 在供应链金融领域,区块链技术以其独特的去中心化、不可篡改、透明公开等特性,正在逐步成为该领域的革新者与引领者。本文将深入探讨区块链技术在供应链金融中的应用特点、功能、使用场景,并结合具体案例和技术方案,展现其巨大的潜力和价值。 二、区块链在供应链金…

【动态规划-BM71 最长上升子序列(一)】

题目 BM71 最长上升子序列(一) 分析 dp[i] 考虑到下标i&#xff0c;其组成的最长上升子序列长度 可以用动态规划的原因&#xff1a; 到i的结果可以由到j &#xff08;j<i) 的结果推出&#xff0c;只需要判断下标j对应的数字是否比下标i 对应的字母小即可 注意&#xf…

Three.js——粒子效果、粒子水波、粒子组成立方体

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 ⚡开源项目&#xff1a; rich-vue3 &#xff08;基于 Vue3 TS Pinia Element Plus Spring全家桶 MySQL&#xff09; &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1…

blazehttp下载安装和自动化测试防护效果

blazehttp下载安装和自动化测试防护效果 说明测试环境的准备网站和waf配置blazehttp下载安装和测试测试效果waf安全日志查看 说明 需要docker环境和1panel面板 本测试使用blazehttp南墙waf进行测试&#xff0c;有兴趣的同学推荐使用雷池waf 测试环境的准备 使用1panel面板&am…

JavaSE--【类和对象】

本篇目标 1. 掌握类的定义方式以及对象的实例化 2. 掌握类中的成员变量和成员方法的使用 3. 掌握对象的整个初始化过程 一、面向对象的初步认知 1.1 面向对象的初步认知 Java是一门纯面向对象的语言(Object Oriented Program&#xff0c;简称OOP)&#xff0c;在面向对象的世界里…

【免费Web系列】大家好 ,今天是Web课程的第十七天点赞收藏关注,持续更新作品 !

这是Web第一天的课程大家可以传送过去学习 http://t.csdnimg.cn/K547r SpingBoot原理 在前面十多天的课程当中&#xff0c;我们学习的都是web开发的技术使用&#xff0c;都是面向应用层面的&#xff0c;我们学会了怎么样去用。而我们今天所要学习的是web后端开发的最后一个篇…