leetcode题解python版:41-45

41、 缺失的第一个正数
给你一个未排序的整数数组,请你找出其中没有出现的最小的正整数。
解:先排序,然后从第一个大于零的数nums[i]开始往前走,如果它大于1,就返回一,否则不断+1,如果后面的数是前面的+1或+0,继续,否则输出前面的数+1即可。

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        if not nums:
            return 1
        nums.sort()
        n=len(nums)
        index=0
        for i in range(n):
            if nums[i]>0:
                index=i
                break
        if nums[index]<=0:
            return 1
        if nums[index]>1:
            return 1
        if index==n-1:
            return 2
        i=index+1
        while i<n and (nums[i]==nums[i-1]+1 or nums[i]==nums[i-1]):
            i=i+1
        return nums[i-1]+1

执行用时:40 ms, 击败了76.27%的用户
内存消耗:13.7 MB, 击败了48.93%的用户

42、接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
还是用类似双指针的办法,显示让i=0,j向后走直到height[j]>=height[i],如果j-i>1则可以计算,更新i=j。如果j循环完之后发现i!=n-1,那么说明i是最大的那个,这时就要改变思路,每次都找i后面最大的那个坐标index,它可以和i一起装水,再更新i=index并重复操作直到i=n-1。

class Solution:
    def trap(self, height: List[int]) -> int:
        ways=[]
        i=0
        total=0
        n=len(height)
        if not height or len(height)<=2:
            return 0
        for j in range(1,n):
            if height[j]>=height[i] and j-i>1:
                if height[i]>0:
                    for k in range(i+1,j):
                        total=total+height[i]-height[k]
                    i=j
                else:
                    i=j
            elif height[j]>=height[i]:
                i=j
        while i<len(height)-1:
            j=self.findHighestofRest(i,height)
            if j-i>1:
                for k in range(i+1,j):
                    total=total+height[j]-height[k]
            i=j
        return total
   
    def findHighestofRest(self,index,height):
        lastheight=index+1
        if index+2==len(height):
            return index+1
        else:
            for k in range(index+2,len(height)):
                if height[k]>=height[lastheight]:
                    lastheight=k
            return lastheight

执行用时:44 ms, 击败了88.95%的用户
内存消耗:14.1 MB, 中击败了63.45%的用户

43、字符串相乘
给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        adict={"0":0,"1":1,"2":2,"3":3,"4":4,"5":5,"6":6,"7":7,"8":8,"9":9}
        ans=0
        n=len(num1)
        m=len(num2)
        for i in range(n-1,-1,-1):
            for j in range(m-1,-1,-1):
                ans=ans+adict[num1[i]]*adict[num2[j]]*(10**(n+m-2-i-j))
        return str(ans)

解:此问题就是让模拟乘法运算,只要做双循环即可。
执行用时:200 ms击败了36.58%的用户
内存消耗:13.7 MB击败了52.81%的用户

44、通配符匹配
给定一个字符串 (s) 和一个字符模式 § ,实现一个支持 ‘?’ 和 ‘*’ 的通配符匹配。

'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。

两个字符串完全匹配才算匹配成功。
说明:
s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *
解:与第10题正则表达式的匹配类似,但是在本题中p的任意一个字符都是相互独立的。在本题中*的取值是不确定的,需要进行枚举,这时可以考虑用动态规划来减少枚举的次数。dp[i][j]表示s[0:i]与p[0:i]是否匹配。
在考虑状态转移时:
如果pj是字母,那么si必须是与其相等的字母且dp[i-1][j-1]=True
如果pj是问号,那么si只需是一个字母,且dp[i-1][j-1]=True
如果pj是*,那么对si没有任何要求只需分用还是不用这个*,用的话就是dp[i-1][j]为True,不用的话就是dp[i][j-1]为True。两者只要有一个成立即可。
边界条件dp[0][0]为True,dp[0][j]只有当p的前j个都为时才成立,也就是说dp[0][j]为True当且仅当dp[0][j-1]为True且p[j-1]为。dp[i][0]为false
所以递推应该按照i+j=k,k=0,1,2,…的形式递推。

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        m=len(s)
        n=len(p)
        dp=[[False for i in range(n+1)] for j in range(m+1)]
        dp[0][0]=True
        for i in range(1,n+1):
            if p[i-1]=='*'and dp[0][i-1]:
                dp[0][i]=True
            else:
                break
        for i in range(1,m+1):
            for j in range(1,n+1):
                if p[j-1]=='*':
                    dp[i][j]=dp[i-1][j] | dp[i][j-1]
                elif p[j-1]=='?' or p[j-1]==s[i-1]:
                    dp[i][j]=dp[i-1][j-1]
        return dp[m][n]

执行用时:928 ms, 击败了56.48%的用户
内存消耗:21.8 MB, 击败了52.73%的用户

45、跳跃游戏 II
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
你的目标是使用最少的跳跃次数到达数组的最后一个位置。
解:用BFS就可以了,如果一个点能到另一个点就连边,从而构成一个有向无圈图。

class Solution:
    def jump(self, nums: List[int]) -> int:
        n=len(nums)
        gray=[]#已发现但还未探查
        black=[]#已经探查完毕
        distance={}#存储各点到0的距离
        distance[0]=0
        gray.append(0)
        if not nums or n==1:
            return 0
        while gray:
            a=gray.pop()
            for i in range(1,nums[a]+1):
                if i+a==n-1:
                    return distance[a]+1
                if (i+a<n) and (i+a not in black) and (i+a not in gray):
                    gray.insert(0,i+a)
                    distance[i+a]=distance[a]+1
                black.append(a)  

然而该方法会超出时间限制
可以对BFS进行初步的改进。注意到由于在本题中搜索是顺序进行的,不会重复,因此可以把black删去,此外还可以在每次遍历的时候先遍历大的元素,从而更快的到达n-1。

class Solution:
    def jump(self, nums: List[int]) -> int:
        n=len(nums)
        gray=[]
        distance={}
        distance[0]=0
        gray.append(0)
        if not nums or n==1:
            return 0
        while gray:
            a=gray.pop()
            for i in range(nums[a],0,-1):
                if a+i==n-1:
                    return distance[a]+1
                if a+i not in gray:
                    distance[a+i]=distance[a]+1
                    gray.insert(0,a+i)

执行用时:3288 ms, 击败了5.04%的用户
内存消耗:18.7 MB, 击败了5.07%的用户

解法二:我们可以对上述的BFS进行合理化改进,以节约内存和时间。我们可以用lastmax代表上一次查到的最大距离,也就是再gray里的元素,往gray里添加新元素也不用逐个添加,而是找出上一次添加元素能够走出的最大距离。
用i代表上一次能够探索到的最大距离的元素(上次探索从i出发可以探索到最大的距离),下一次只要遍历(i+1,i+1+nums[i])即可找到下一次能探索到的最大距离,并记下相应点

class Solution:
    def jump(self, nums: List[int]) -> int:
        n=len(nums)
        step=i=maxLen = 0
        while i<n:
            if i>=n-1:
                return step          
            step=step+1 #新加一次,再上次探索过的基础上
            for j in range(i+1,i+1+nums[i]):#
                if j>=n-1: 
                    return step
                if maxLen<nums[j]+j:
                    maxLen = nums[j]+j
                    temp=j                        
            i = temp

执行用时:52 ms, 在所有 Python3 提交中击败了83.91%的用户
内存消耗:15.2 MB, 在所有 Python3 提交中击败了40.76%的用户

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值