【PythonCode】力扣Leetcode21~25题Python版

【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.mfbz.cn/a/689493.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【CS.CN】优化HTTP传输:揭示Transfer-Encoding: chunked的奥秘与应用

文章目录 0 序言0.1 由来0.2 使用场景 1 Transfer-Encoding: chunked的机制2 语法 && 通过设置Transfer-Encoding: chunked优化性能3 总结References 0 序言 0.1 由来 Transfer-Encoding头部字段在HTTP/1.1中被引入&#xff0c;用于指示数据传输过程中使用的编码方式…

OlSoul系统调校程序v2024.06.05

软件介绍 OlSoul是一款能够适配用于Win各个系统的系统调校软件&#xff0c;OlSoul内置有众多调校功能可以直接使用&#xff0c;如有启用无线网络功能、启用打印机功能、系统快速休眠与休眠开关、快捷方式小箭头去除功能等&#xff0c;具体的调校功能多达几十项&#xff0c;可自…

vsCode双击文件才能打开文件,单击文件只能预览?

解决&#xff1a; 1、打开设置 2、搜索workbench.editor.enablePreview 3、更改为不勾选状态 4、关闭设置 效果&#xff1a; 现在单击一个文件时&#xff0c;将会在编辑器中直接打开&#xff0c;而非是预览状态。

51单片机-实机演示(LED点阵)

目录 前言: 一.线位置 二.扩展 三.总结 前言: 这是一篇关于51单片机实机LED点阵的插线图和代码说明.另外还有一篇我写的仿真的连接在这:http://t.csdnimg.cn/ZNLCl,欢迎大家的点赞,评论,关注. 一.线位置 接线实机图. 引脚位置注意: 1. *-* P00->RE8 P01->RE7 …

多源最短路径算法–Floyd算法

多源最短路径算法–Floyd算法 Floyd算法是为了求出每一对顶点之间的最短路径 它使用了动态规划的思想&#xff0c;将问题的求解分为了多个阶段 先来个例子&#xff0c;这是个有向图 Floyd算法的运行需要两个矩阵 最短路径矩阵 从当前这个状态看各顶点间的最短路径长度 例…

网络编程: 高级IO与多路转接select,poll,epoll的使用与介绍

网络编程: 高级IO与多路转接select,poll,epoll的使用与介绍 前言一.五种IO模型1.IO的本质2.五种IO模型1.五种IO模型2.同步IO与异步IO3.IO效率 二.非阻塞IO1.系统调用介绍2.验证代码 三.select多路转接1.系统调用接口2.写代码 : 基于select的TCP服务器1.封装的Socket接口2.开始写…

攻防世界---misc---Hear-with-your-Eyes

1、题目描述&#xff0c;下载附件&#xff0c;是一个.gz后缀的文件&#xff0c;查找资料发现&#xff0c;这个后缀是Linux系统的压缩包后缀。这里题目提示了用眼睛听音频&#xff0c;说明会有个音频&#xff0c;并且信息就在音频&#xff0c;可以用眼睛看到 2、将文件放在linux…

读书笔记-《软件定义安全》之二:SDN/NFV环境中的安全问题

第2章 SDN/NFV环境中的安全问题 1.架构安全 SDN强调了控制平面的集中化&#xff0c;从架构上颠覆了原有的网络管理&#xff0c;所以SDN的架构安全就是首先要解决的问题。例如&#xff0c;SDN实现中网络控制器相关的安全问题。 1.1 SDN架构的安全综述 从网络安全的角度&…

C++面向对象程序设计 - 文件操作与文件流

在实际应用中&#xff0c;常以磁盘文件作为对象&#xff0c;即能从磁盘文件读取数据&#xff0c;也能将数据输出到磁盘文件&#xff0c;磁盘是计算机的外部存储器&#xff0c;能够长期保留信息&#xff0c;能读能写&#xff0c;可以刷新重写等等。 在C中&#xff0c;文件操作通…

【Java】Java18的新特性

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

第四篇红队笔记-百靶精讲之Prime-wfuzz-wpscan-openssl enc

靶机Prime渗透 主机发现 nmap扫描与分析 目录爆破与模糊测试 dirb 目录扫描 dev secret.txt wfuzz发现 file参数 根据secret.txt-location.txt 和 file参数结合 secrettier360 根据filelocation.txt得到的on some other php page&#xff08;改用之前扫到image.p…

mqtt-emqx:设置遗嘱消息

【pom.xml】 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.3.12.RELEASE</version> </dependency> <dependency><groupId>org.eclipse…

新书推荐:2.2.4 第11练:消息循环

/*------------------------------------------------------------------------ 011 编程达人win32 API每日一练 第11个例子GetMessage.c&#xff1a;消息循环 MSG结构 GetMessage函数 TranslateMessage函数&#xff1a;将虚拟键消息转换为字符消息 DispatchMessage函数…

Vue根据后端返回的tabList动态渲染组件信息

最近做了一个功能&#xff0c;后端根据配置信息&#xff0c;动态返回一个tabList&#xff0c;其中结构是List<String,Object> tabList; map里面的数据是 label、value 页面需要根据tablist动态渲染组件&#xff08;不同的tab都使用了组件进行了封装&#xff09; 实现效果…

解决福昕风腾PDF套装无法打印在线电子签章的方法

使用福昕风腾PDF套装打印在线电子签章文件时发现&#xff0c;在线盖的电子印章和签名却打印不出来&#xff0c;后现发现&#xff0c;按图中选项选择“文档”&#xff0c;即可完整打印文件内容及电子签章。留印。

【PL理论】(8) F#:列表高阶函数之 filter 函数 | 内联谓词函数 | 链式操作:先过滤再映射

&#x1f4ad; 写在前面&#xff1a;上一章中&#xff0c;我们详细讲解了列表的合并&#xff0c;本章我们来详细讲解一下列表的过滤&#xff0c;在 F# 中&#xff0c;过滤列表是指从列表中提取满足某个条件的元素&#xff0c;形成一个新的列表。这个操作通常使用 List.filter 函…

超详解——Python模块文档——小白篇

目录 1. Unix起始行 示例&#xff1a; 2. 对象和类型 示例&#xff1a; 3. 一切都是对象 示例&#xff1a; 4. 理解对象和引用 示例&#xff1a; 5. 理解对象和类型 示例&#xff1a; 6. 标准类型 示例&#xff1a; 7. 其他内建类型 示例&#xff1a; 8. 类型的类…

HTML静态网页成品作业(HTML+CSS)—— 保护环境环保介绍网页(1个页面)

&#x1f389;不定期分享源码&#xff0c;关注不丢失哦 文章目录 一、作品介绍二、作品演示三、代码目录四、网站代码HTML部分代码 五、源码获取 一、作品介绍 &#x1f3f7;️本套采用HTMLCSS&#xff0c;未使用Javacsript代码&#xff0c;共有1个页面。 二、作品演示 三、代…

基于zyyo主页与無名の主页合并二改,一款适合新手的个人主页

pengzi主页&#x1f64b; 项目地址 简洁的布局&#xff1a;主页应该有清晰的布局&#xff0c;包括一个简洁的导航菜单和易于浏览的内容区域。避免使用过多的花哨效果&#xff0c;保持页面简洁明了。 个人资料介绍&#xff1a;在主页上展示一段简短的个人介绍&#xff0c;包括…

bat脚本简介

一、bat脚本 概念定义 BAT 批处理是一种在 Windows 系统中用于将一系列命令组合成一个可执行文件&#xff08;.bat 文件&#xff09;的脚本技术。 允许用户将多个操作命令按顺序编写在一起。形成一个自动化执行的流程。批处理文件可以包含各种系统命令和程序调用。 如文件操作…