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

2024.8.31 Python,合并区间,用sort通过列表第一个元素给列表排序,三数之和,跳跃游戏

1.合并区间

以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] = [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。
示例 1:
输入:intervals = [[1,3],[2,6],[8,10],[15,18]]
输出:[[1,6],[8,10],[15,18]]
解释:区间 [1,3] 和 [2,6] 重叠, 将它们合并为 [1,6].
示例 2:
输入:intervals = [[1,4],[4,5]]
输出:[[1,5]]
解释:区间 [1,4] 和 [4,5] 可被视为重叠区间。

class Solution:
    def merge(self, intervals: List[List[int]]) -> List[List[int]]:
        if intervals==[]:
            return intervals
        intervals.sort(key=lambda x: x[0])	#第二次报错
        res=[intervals[0]]
        for i in range(1,len(intervals)):
            changed=False
            for j in range(0,len(res)):
                if res[j][0]<=intervals[i][0]<=res[j][1] or \
                        res[j][0]<=intervals[i][1]<=res[j][1] or \
                        intervals[i][0]<=res[j][0]<=intervals[i][1] or \
                        intervals[i][0]<=res[j][1]<=intervals[i][1]:
                    res[j][0]=min(res[j][0],intervals[i][0])
                    res[j][1]=max(res[j][1],intervals[i][1])
                    changed=True
            if changed==False:
                res.append(intervals[i])			#第一次出错
        return res

上面是我的代码,中间报错了一次,第一次报错是因为我把res.append(intervals[i])那里写成了+=。你永远记住加等这个操作是在本层加元素进去才这样用的,如果要层套层一定是append,第二次出错是因为,测试用例里出现了这样的一个
输入:[[2,3],[4,5],[6,7],[8,9],[1,10]]
我的输出是:[[1,10],[1,10],[1,10],[1,10]]
造成这样的原因是,前面四个都没有找到合适的好兄弟来给他缩进去,导致最后一个救赎他们的进来了以后每一个都救赎了一遍,但是本身他们应该是一体的,也就是说,如果1,10早早来的话,就没这么多事了,所以我就不知道该怎么办了,一看答案,发现答案里有一个先排序的操作:intervals.sort(key=lambda x: x[0]),这个操作的含义是按照第一个元素先给所有的列表排序,现在我才懂了,列表的最后一个控制到底有没有连接,列表的第一个控制先后顺序,所以在这个操作中必须得先排序。最后我的代码过了,但是我的代码用时太久了,用了4秒才做完,所以这对整体而言是不合适的。

intervals.sort(key=lambda x: x[0])
#等效于
def get_first_element(x):
    return x[0]
# 使用自定义函数进行排序
intervals.sort(key=get_first_element)

lambda就相当于一个弱定义,在一些一行定义中使用,这个让我来做我可能做不到,但是可以学习一下
方法二:

class Solution:
	def merge(self,intervals:List[List[int]])->List[List[int]]:
		intervals.sort(key=lambda x:x[0])
		merged=[]
		for interval in intervals:
			if not merged or merged[-1][1]<interval[0]:
				merged.append(interval)
			else:
				merged[-1][1]=max(merged[-1][1],interval[1])
	
		return merged		

也就是说排序以后,可能性就已经排除了很多了,我的方法是和项目中每一个进行比较,如果有那就融合,因为我想的是给的排序是乱的,那么就没法确定谁先谁后,我当时心想,要是这玩意是有顺序的那就好了,但是我没想到的事,真的可以先排序再进行操作,对列表进行排序还是头一次见。

2.sort和sorted

一句话,sort是方法,作用于对象,sorted是函数,输出得有东西接住

# 使用 sort()
numbers = [3, 1, 4, 1, 5, 9]
numbers.sort()
print(numbers)  # 输出: [1, 1, 3, 4, 5, 9]

# 使用 sorted()
numbers = [3, 1, 4, 1, 5, 9]
sorted_numbers = sorted(numbers)
print(sorted_numbers)  # 输出: [1, 1, 3, 4, 5, 9]
print(numbers)         # 原列表未改变,输出: [3, 1, 4, 1, 5, 9]

3.三数之和

这个题在8.31的文章中写过,这个题的大概要求就是,在列表的一堆数字里找出三个数字,然后让他等于0,当时我的想法是通过组合combine来找出所有的三连的选项,然后计算每一组的答案,如果等于0,那就输出,但是这样的效率我不知道高不高,然后我昨天看这个双指针的答案的时候就有点不屑一顾,我心想这玩意能有啥说法,但是让chat写这个题的答案,也是这样的处理,说明这个方法就是最佳解法,所以我现在就来尝试一下

#以下代码是错误的
class Solution:
    def threeSum(self,nums:List[int])->List[List[int]]:
        n=len(nums)
        nums.sort()
        res=[]

        for first in range(n):
            if first>0 and nums[first]==nums[first-1]:
                continue
            target=-nums[first]
            
            for second in range(first+1,n):
                third=n-1			#注意这里			
                if second>first+1 and nums[second]==nums[second-1]:
                    continue
                while second<third and nums[second]+nums[third]!=target: #注意这里,不是错了,在我的代码中是对的,但是如果把third=n-1提出去这里就不对了,就得改成>target
                    third-=1
                if second==third:
                    continue		#注意这里
                if nums[second]+nums[third]==target:
                    res.append([nums[first],nums[second],nums[third]])
        return res

评价:
其实是个三指针问题了,但是这个题巧就巧在他可以使用sort来降低工作量,我上面的代码是学习这个题的代码思路自己写的,但是我写的过程可以通过大部分的测试,但是我的代码和纯枚举已经没什么太大区别了,所以要优化这个代码还是有说法的
代码逻辑如下:
1.排序,sort()方法,非常的合适
2.first用来遍历所有,在这里的判断非常的好,是我没想到的东西,他的逻辑是当first超过0以后,如果当前值和上一个值一样的话,那就直接continue,跳过这次循环
3.target等于-nums[first]这样就去找另外两个就好了。
4.进入遍历second的循环,这里因为列表已经是排序过了,所以在这里我不如second从左往右走,third从右往左走,等到这两个相遇的时候停下来,继续下一个first的循环,这样的好处是,你如果两个for循环从头到尾遍历,可以,当然可以,那你排序的意义是什么,既然排序了,那我两头效率快一倍。
5.while循环我写的其实还更符合直觉一些我写的是当这两个加起来不等于target的时候不停,当然,右边一定要在右边也是必须的,你third没必要到左边去遍历了,second已经走过了。
6.如果这两个重了,那就进行下一次循环
然后超时了,我昨天的代码只能过70%的量,今天的代码已经过了90%,最后还是超时了,那就需要优化代码

class Solution:
    def threeSum(self,nums:List[int])->List[List[int]]:
        n=len(nums)
        nums.sort()
        res=[]

        for first in range(n):
            if first>0 and nums[first]==nums[first-1]:
                continue
            target=-nums[first]
            third=n-1 #注意这里1
            for second in range(first+1,n):
                
                if second>first+1 and nums[second]==nums[second-1]:
                    continue
                while second<third and nums[second]+nums[third]>target:  #注意这里2
                    third-=1
                if second==third:
                    break   	#注意这里3
                if nums[second]+nums[third]==target:
                    res.append([nums[first],nums[second],nums[third]])
        return res

优化的部分:
1.我的third在second的里面,我保证每个second都有权利去找一次最大,所以保证了正确,但是你想,因为排序以后,他们都是递进式的,在固定了second的时候,缩小third,会出现从大于target到小于target的过程,而这个过程second也可以配合增加,这个时候你没必要把third从头再遍历一遍,所以就这个时候固定third,增加second是可以的,这里如果没看懂欢迎来评论区讨论
2.这个2,我中间报错了,我把third=n-1提出去以后,还用不等于判断while,然后就漏了一种可能性,那就是third没有找到相等的情况然后加起来已经比target小了,然后second增加了,third没有机会去往回走了,那就错过了,具体的测试用例是[-1,-1,0,1],所以要保证加起来大于target,就能避免这种情况,要不然就是third=n-1放循环里面,再跑一次得了
3.我当时写的是continue,但是你想,再continue的话,second和third在之后的循环中,他们的和都会比target大,也就是进入了垃圾循环,所以之后的循环就没有意义了,所以我还不如直接退出这一层循环,该改first了。
这个题确实有点难想,在实际操作的时候也有很多问题,这个测试用例写的非常的好,非常的合适。

4.跳跃游戏

给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。
每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:
0 <= j <= nums[i]
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。
下面是我写的版本

#错的
class Solution:
    def jump(self, nums: List[int]) -> int:
        n=len(nums)
        if n==1:
            return 0
        index=0
        step=0
        if nums[0]>=n:
            return 1
        while index<n-1:
            max_step=nums[index] 
            tmp_index=0
            tmp=[]
            while tmp_index<max_step:
                if index+tmp_index+1< n: #假设max_step是2那么tmp_index就可以取0,1
                    tmp.append(nums[index+tmp_index+1])  #+1是为了模拟跳数,因为没有0跳
                tmp_index+=1
            #此时的tmp已经好了,接下来要找tmp的最大值和最大下标
            max_value, max_index=self.find_max_with_index(tmp)
            step+=1
            index+=max_index+1
        return step



    def find_max_with_index(self,lst):
     # 假设最大值的索引为0
        max_value = max(lst)
        for i in range(len(lst)-1,-1,-1):
            if lst[i]==max_value:
                max_index=i
                break

        return max_value, max_index

下面是chatGPT根据我写的代码的修改版

class Solution:
    def jump(self, nums: List[int]) -> int:
        n = len(nums)
        if n == 1:
            return 0
        
        index = 0
        step = 0
        
        while index < n - 1:
            max_step = nums[index]
            if index + max_step >= n - 1:  # 如果当前可以直接跳到最后位置
                return step + 1
            
            # 找出从当前 index 开始,能够跳跃到的最远位置
            max_reach = 0
            next_index = index
            
            for i in range(1, max_step + 1):
                if index + i < n and index + i + nums[index + i] > max_reach:
                    max_reach = index + i + nums[index + i]
                    next_index = index + i
            
            index = next_index
            step += 1
        
        return step

更好的版本:贪心算法

class Solution:
    def jump(self, nums: List[int]) -> int:
        
        if len(nums)==1:
            return 0

        start,cnt=0,0
        
        while start<len(nums)-1:
            step=nums[start]
            if step+start>=len(nums)-1:#如果能直接跳到最后一位
                return cnt+1
           
            #默认从第一位开始跳
            next_index=start+1
            max_address=nums[next_index]+next_index
          
            #选择能达到的最大索引的作为跳点
            for i in range(start+2,start+step+1):
                if i+nums[i]>=max_address:
                    next_index=i
                    max_address=i+nums[i]
            
            start=next_index
            cnt+=1
        return cnt+1

我将在明天的文章中详细品鉴每一种代码


http://www.kler.cn/news/284683.html

相关文章:

  • AcWing 897. 最长公共子序列
  • JVM 内存参数
  • JetBrains WebStorm 2024.2 (macOS, Linux, Windows) - 最智能的 JavaScript IDE
  • 合宙LuatOS开发板使用手册——Air700EAQ
  • 图像边缘检测Canny
  • HTTP 之 Web Sockets处理恶意的Payload的策略(十一)
  • const、inline、nullptr的使用
  • Android Activity 的启动模式(Launch Mode)
  • Vue 2 vs Vue 3:v-if 和 v-for 的差异
  • 物流需求回复势头稳定,目前全国社会物流总额达197.7万亿元
  • 从零开学C++:vector类
  • 【MySQL索引】4索引优化
  • Django Compressor压缩静态文件(js/css)
  • 搭建双主四从的MySQL集群
  • 【大模型】LangChain基础学习
  • 某大厂前端面试题
  • 自然语言处理与深度学习的结合
  • Eureka简介与开发
  • Axure RP实战:打造高效文字点选验证码
  • 销冠大模型案例
  • (一) 初入MySQL 【认识和部署】
  • Promise学习
  • k8s-pod 实战六 (如何在不同的部署环境中调整startupprobe的参数?)
  • [QCTF2018]X-man-A face1
  • 基于STM32的智能物料运载小车:OpenMV和OpenCV结合图像识别与运动控制算法优化(代码示例)
  • Linux和Unix的区别及为什么鸿蒙系统不用Unix的原因
  • 安卓中synchronized 关键字 的作用和介绍
  • java篇 常用工具类 0x05:基本类型的自动装箱拆箱
  • 通过Amazon Bedrock上的Stability AI模型开发生成式AI应用(上篇)
  • MySQL——基础操作