2024.8.24 Python,链表异常断裂问题,双链表的建立问题,全排列中的引用机制与copy的使用,最大子数组和

本文主要是介绍2024.8.24 Python,链表异常断裂问题,双链表的建立问题,全排列中的引用机制与copy的使用,最大子数组和,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.浏览器系统设计

你有一个只支持单个标签页的 浏览器 ,最开始你浏览的网页是 homepage ,你可以访问其他的网站 url ,也可以在浏览历史中后退 steps 步或前进 steps 步。

请你实现 BrowserHistory 类:

BrowserHistory(string homepage) ,用 homepage 初始化浏览器类。
void visit(string url) 从当前页跳转访问 url 对应的页面 。执行此操作会把浏览历史前进的记录全部删除。
string back(int steps) 在浏览历史中后退 steps 步。如果你只能在浏览历史中后退至多 x 步且 steps > x ,那么你只后退 x 步。请返回后退 至多 steps 步以后的 url 。
string forward(int steps) 在浏览历史中前进 steps 步。如果你只能在浏览历史中前进至多 x 步且 steps > x ,那么你只前进 x 步。请返回前进 至多 steps步以后的 url 。

***方法一:***我的办法,单链表:

class ListNode:def __init__(self, val: str, next=None):self.val = valself.next = nextclass BrowserHistory:def __init__(self, homepage: str):self.head = ListNode(homepage)self.cur = self.headdef visit(self, url: str) -> None:# 创建新节点new_node = ListNode(url)# 当前节点的 next 指向新节点self.cur.next = new_node# 将当前节点移到新节点self.cur = new_node# 确保新节点之后没有其他节点self.cur.next = Nonedef back(self, steps: int) -> str:temp = self.headtotal_step = 0# 计算当前节点的位置距离头的位置,n个节点走n-1步while temp != self.cur:temp = temp.nexttotal_step += 1# 如果步数超过了实际步数,只回退到起始点,这步很重要forward_step = max(0, total_step - steps)# 找到目标节点,从头开始走forward步temp = self.headfor _ in range(forward_step):temp = temp.next# 更新当前节点为目标节点self.cur = tempreturn self.cur.val def forward(self, steps: int) -> str:for _ in range(steps):if self.cur.next is None:  # 如果已经到达最后一个页面,无法再前进breakself.cur = self.cur.nextreturn self.cur.val

从早上开始到现在我一直没搞懂为什么我的代码有问题,我以为是back的问题,其实back也有问题,back和forward我都没有加限制条件,在测试用例中,他含有超过范围的特殊值,所以要给他加限定条件,我的代码的最大问题在于我的visit是这样写的:

def visit(self, url: str) -> None:self.cur=self.cur.next        self.cur=ListNode(url)
###上面的代码是错误的,下面的是对的
def visit(self, url: str) -> None:self.cur.next=ListNode(url)self.cur=self.cur.next        

chat说我的代码这样写链表会导致前面的链表断了。所以他一直在back中报错,说我的temp=temp.next中temp已经是None了,那么temp的next毫无意义。所以我一直在找back的问题。现在来看chat对于我的链表断裂问题是怎么说的:
1.在这段代码中,首先你将 self.cur 移动到 self.cur.next,然后将 self.cur 指向一个新的 ListNode(url) 节点。这会导致你丢失原本的链表结构。
2.具体来说,当你执行 self.cur = self.cur.next 后,self.cur 指向了原链表中的下一个节点。然而,你马上用 self.cur = ListNode(url) 创建了一个新的节点,并且 self.cur 再也不会指向原来的链表中的那个节点。也就是说,你原来链表的前一个节点的 next 指针仍然指向这个旧节点,而不是新的节点。这就导致前进操作不可行,因为你丢失了历史记录。
啊啊啊啊,也就是说,我应该先给next赋值,再移动指针,我的错误在于先移动了指针,再才建立了联系。
方法二:双链表

class ListNode:def __init__(self, val: str):self.val = valself.prev = Noneself.next = Noneclass BrowserHistory:def __init__(self, homepage: str):self.cur = ListNode(homepage)def visit(self, url: str) -> None:new_node = ListNode(url)self.cur.next = new_nodenew_node.prev = self.curself.cur = new_node  # 将当前指针更新为新页面new_node.next = None  # 断开当前节点的前进记录def back(self, steps: int) -> str:while steps > 0 and self.cur.prev is not None:self.cur = self.cur.prevsteps -= 1return self.cur.valdef forward(self, steps: int) -> str:while steps > 0 and self.cur.next is not None:self.cur = self.cur.nextsteps -= 1return self.cur.val
# Your BrowserHistory object will be instantiated and called as such:
# obj = BrowserHistory(homepage)
# obj.visit(url)
# param_2 = obj.back(steps)
# param_3 = obj.forward(steps)

双链表的定义和上面单链表的类似,用next定义下一个节点,然后next的上一个是cur,cur的下一个是next,这样一个新的结点就定义完了。只有这样才能让链表把数据串起来,

def visit(self, url: str) -> None:self.cur.next = ListNode(url)         self.cur.next.prev = self.curself.cur = self.cur.next  # 将当前指针更新为新页面def visit(self, url: str) -> None:new_node = ListNode(url)self.cur.next = new_nodenew_node.prev = self.curself.cur = new_node  # 将当前指针更新为新页面new_node.next = None  # 断开当前节点的前进记录

这两个是一样的,所以一定要细致的去思考这个关系
方法三:动态数组

class BrowserHistory:def __init__(self, homepage: str):# 初始化浏览器历史记录,`history` 存储所有访问过的页面self.history = [homepage]# `current_index` 表示当前页面在历史记录中的位置self.current_index = 0def visit(self, url: str) -> None:# 删除当前页面之后的所有页面,因为新访问的页面会替代它们self.history = self.history[:self.current_index + 1]# 将新访问的页面添加到历史记录中self.history.append(url)# 更新当前页面的位置self.current_index += 1def back(self, steps: int) -> str:# 计算需要回退的步数,但不超过历史记录的开始位置self.current_index = max(0, self.current_index - steps)# 返回回退后的页面return self.history[self.current_index]def forward(self, steps: int) -> str:# 计算需要前进的步数,但不超过历史记录的末尾self.current_index = min(self.current_index + steps, len(self.history) - 1)# 返回前进后的页面return self.history[self.current_index]# Your BrowserHistory object will be instantiated and called as such:
# obj = BrowserHistory(homepage)
# obj.visit(url)
# param_2 = obj.back(steps)
# param_3 = obj.forward(steps)

这个代码没什么好说的,这是一个对于数组的很好的操作,我觉得这些代码中的max(0,index-step)还有min(index+steps,len(self.history)-1)这两个操作来去限制back和forward,是值得我去学习的。不一定非得要用if来加限制条件,有些限制条件是可以去想办法简化的

2.全排列,接8.16号的全排列

今天自行的做了一遍全排列的题,出现了一些问题,我的代码如下:

#这个代码是错的
class Solution:def permute(self, nums: List[int]) -> List[List[int]]:def backtrack(letter,remaining):if remaining==[]:res.append(letter)return n=len(remaining)for i in range(n):letter+=[remaining[i]]backtrack(letter,remaining[0:i]+remaining[i+1:n])res=[]backtrack([],nums)return res     

看起来逻辑是没什么问题的,但是实际的输出极其糟糕,大概是这样的:
[[1,2,3,3,2,2,1,3,3,1,3,1,2,2,1],[1,2,3,3,2,2,1,3,3,1,3,1,2,2,1],[1,2,3,3,2,2,1,3,3,1,3,1,2,2,1],[1,2,3,3,2,2,1,3,3,1,3,1,2,2,1],[1,2,3,3,2,2,1,3,3,1,3,1,2,2,1],[1,2,3,3,2,2,1,3,3,1,3,1,2,2,1]]
我以为是我对数据类型的操控有问题,检查了半天,我也没能看出问题,所以我就查了之前的代码,完全找不出和之前的代码有什么区别,之前代码如下:

class Solution:def permute(self,nums:List[int])->List[List[int]]:res=[]def backtrack(path,remaining):if not remaining:res.append(path)returnfor i in range(len(remaining)):backtrack(path+[remaining[i]],remaining[:i]+remaining[i+1:])backtrack([],nums)return res

我无非就是用了path和letter的区别,为什么这么大呢,原因在于,正确的代码里,应该带入的是letter加remaining[i],而这一层的letter的值并没有实际的改变,但是我的代码中,letter+=后,letter变了,带入的时候就在i=0的时候就改变了,那你想,在i=1的时候,letter不为空了,就继续算进去了,然后append这个函数也怪的很,他是一个引用机制,即便是现在你把当前的letter用append加入进res了,他仍然会因为后续的改变而改变当前的值,所以只用pop是不够的,还需要给letter一个复制版
具体的代码如下:

class Solution:def permute(self, nums: List[int]) -> List[List[int]]:def backtrack(letter,remaining):if remaining==[]:res.append(letter[:])		#改的部分return n=len(remaining)for i in range(n):letter+=[remaining[i]]backtrack(letter,remaining[0:i]+remaining[i+1:n])letter.pop()    #改的部分res=[]backtrack([],nums)return res            

我没有完全理解这个引用机制
方法二:仍然是做错了,交换法:

#以下是错误代码
class Solution:def permute(self,nums:List[int])->List[List[int]]:if nums==[]:return []n=len(nums)res=[]def backtrack(first):for i in range(first,n):nums[i],nums[first]=nums[first],nums[i]backtrack(first+1)nums[i],nums[first]=nums[first],nums[i]return res.append(nums[:])backtrack(0)return res

输出是:[[1,2,3],[1,2,3],[1,3,2],[1,3,2],[1,2,3],[2,1,3],[2,1,3],[2,3,1],[2,3,1],[2,1,3],[3,2,1],[3,2,1],[3,1,2],[3,1,2],[3,2,1],[1,2,3]]
这次的问题在于,没有给backtrack设定好合理的出口,nums[:]这次注意了,但是出口不对,不是在for循环里出的,而是要加一个if。当所有的事情完成的时候,再出去。最终的代码应该是:

class Solution:def permute(self,nums:List[int])->List[List[int]]:if nums==[]:return []n=len(nums)res=[]def backtrack(first):if first==n:res.append(nums[:])return for i in range(first,n):nums[i],nums[first]=nums[first],nums[i]backtrack(first+1)nums[i],nums[first]=nums[first],nums[i]backtrack(0)return res

3.最大子数组和

给你一个整数数组 nums ,请你找出一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。子数组是数组中的一个连续部分。
示例 1:
输入:nums = [-2,1,-3,4,-1,2,1,-5,4]
输出:6
解释:连续子数组 [4,-1,2,1] 的和最大,为 6 。
示例 2:
输入:nums = [1]
输出:1
示例 3:
输入:nums = [5,4,-1,7,8]
输出:23
**方法一:**暴力法,略,两个循环求就完事了,但是两个循环循死你时间复杂度O(N²)
**方法二:**暴力法但一遍遍历: 这个办法本来都不想看了,但是细细一看还是挺有意思的,虽然没有普适性,但是对于开阔思路有帮助:

class Solution:def maxSubArray(self,nums:List[int])->int:tmp=nums[0]n=len(nums)res=tmpfor i in range(1,n):if tmp+nums[i]>nums[i]:res=max(res,tmp+nums[i])tmp+=nums[i]else:res=max(res,tmp+nums[i],tmp,nums[i])tmp=nums[i]return res

代码逻辑:
1.初始化:tmp=nums[0]:tmp 用于记录当前子数组的和,初始化为第一个元素的值。
res=tmp:res 用于记录迄今为止找到的最大子数组的和,初始化为第一个元素的值。
2.遍历数组:for i in range(1,n):从数组的第二个元素开始遍历。
动态更新当前子数组和:
if tmp + nums[i] > nums[i]:判断是否将当前元素 nums[i] 加入到当前子数组(即 tmp + nums[i])中会得到一个更大的子数组和。如果是,说明当前子数组可以继续扩展。
tmp += nums[i]:如果当前子数组可以继续扩展,更新 tmp 为扩展后的和。
否则,tmp = nums[i]:如果扩展后的子数组和不如当前元素 nums[i] 本身大,说明之前的子数组和可能有负作用,应当从当前元素重新开始构建子数组。
3.更新最大子数组和:res=max(res,tmp):在每一步更新 res,使其始终保持为最大子数组的和。
4.返回结果:最终返回 res,即遍历整个数组后找到的最大子数组和。

为什么这段代码能够遍历完所有可能:
在每次遍历元素时,tmp 代表以当前元素结尾的最大子数组和。通过比较 tmp + nums[i] 和 nums[i],我们判断是否继续扩展子数组还是重新开始新的子数组。
res 始终保持遍历过程中遇到的最大子数组和,所以最终结果一定是全局最大的。
这段代码的核心思想是,既然我们需要找到最大和的子数组,那么在每一步都要判断当前子数组是否有扩展的潜力,如果没有,就从当前元素重新开始构建子数组。这样,最终遍历完成时,res 就会是最大和的子数组。
动态规划思想:这个过程实际上是在动态规划中使用了「状态转移」的思想,通过维护一个局部最优解 tmp,并逐步更新全局最优解 res。
方法三:分治法
它的最大子序和要么在左半边,要么在右半边,要么是穿过中间,对于左右边的序列,情况也是一样,因此可以用递归处理。中间部分的则可以直接计算出来,时间复杂度应该是 O(nlogn)。代码如下:

class Solution:def maxSubArray(self, nums: List[int]) -> int:n = len(nums)#递归终止条件if n == 1:return nums[0]else:#递归计算左半边最大子序和max_left = self.maxSubArray(nums[0:len(nums) // 2])#递归计算右半边最大子序和max_right = self.maxSubArray(nums[len(nums) // 2:len(nums)])#计算中间的最大子序和,从右到左计算左边的最大子序和,从左到右计算右边的最大子序和,再相加max_l = nums[len(nums) // 2 - 1]tmp = 0for i in range(len(nums) // 2 - 1, -1, -1):tmp += nums[i]max_l = max(tmp, max_l)max_r = nums[len(nums) // 2]tmp = 0for i in range(len(nums) // 2, len(nums)):tmp += nums[i]max_r = max(tmp, max_r)#返回三个中的最大值return max(max_right,max_left,max_l+max_r)

方法四:动态规划:

class Solution:def maxSubArray(self, nums: List[int]) -> int:for i in range(1, len(nums)):nums[i] += max(nums[i - 1], 0)return max(nums)

这篇关于2024.8.24 Python,链表异常断裂问题,双链表的建立问题,全排列中的引用机制与copy的使用,最大子数组和的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1104188

相关文章

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

java图像识别工具类(ImageRecognitionUtils)使用实例详解

《java图像识别工具类(ImageRecognitionUtils)使用实例详解》:本文主要介绍如何在Java中使用OpenCV进行图像识别,包括图像加载、预处理、分类、人脸检测和特征提取等步骤... 目录前言1. 图像识别的背景与作用2. 设计目标3. 项目依赖4. 设计与实现 ImageRecogni

Python将大量遥感数据的值缩放指定倍数的方法(推荐)

《Python将大量遥感数据的值缩放指定倍数的方法(推荐)》本文介绍基于Python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处理,并将所得处理后数据保存为新的遥感影像... 本文介绍基于python中的gdal模块,批量读取大量多波段遥感影像文件,分别对各波段数据加以数值处

python管理工具之conda安装部署及使用详解

《python管理工具之conda安装部署及使用详解》这篇文章详细介绍了如何安装和使用conda来管理Python环境,它涵盖了从安装部署、镜像源配置到具体的conda使用方法,包括创建、激活、安装包... 目录pytpshheraerUhon管理工具:conda部署+使用一、安装部署1、 下载2、 安装3

Mysql虚拟列的使用场景

《Mysql虚拟列的使用场景》MySQL虚拟列是一种在查询时动态生成的特殊列,它不占用存储空间,可以提高查询效率和数据处理便利性,本文给大家介绍Mysql虚拟列的相关知识,感兴趣的朋友一起看看吧... 目录1. 介绍mysql虚拟列1.1 定义和作用1.2 虚拟列与普通列的区别2. MySQL虚拟列的类型2

Python进阶之Excel基本操作介绍

《Python进阶之Excel基本操作介绍》在现实中,很多工作都需要与数据打交道,Excel作为常用的数据处理工具,一直备受人们的青睐,本文主要为大家介绍了一些Python中Excel的基本操作,希望... 目录概述写入使用 xlwt使用 XlsxWriter读取修改概述在现实中,很多工作都需要与数据打交

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

关于@MapperScan和@ComponentScan的使用问题

《关于@MapperScan和@ComponentScan的使用问题》文章介绍了在使用`@MapperScan`和`@ComponentScan`时可能会遇到的包扫描冲突问题,并提供了解决方法,同时,... 目录@MapperScan和@ComponentScan的使用问题报错如下原因解决办法课外拓展总结@

mysql数据库分区的使用

《mysql数据库分区的使用》MySQL分区技术通过将大表分割成多个较小片段,提高查询性能、管理效率和数据存储效率,本文就来介绍一下mysql数据库分区的使用,感兴趣的可以了解一下... 目录【一】分区的基本概念【1】物理存储与逻辑分割【2】查询性能提升【3】数据管理与维护【4】扩展性与并行处理【二】分区的

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超