Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)

文章目录

  • Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ
    • 写在前面:
    • No.41.缺失的第一个正数
    • No.42.接雨水
    • No.43.字符串相乘
    • No.44.通配符匹配
    • No.45.跳跃游戏Ⅱ

Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ

写在前面:

作为一个计算机院的大学生,总觉得仅仅在学校粗略的学习计算机专业课是不够的,尤其是假期大量的空档期,作为一个小白,实习也莫得路子,又不想白白耗费时间。于是选择了Leetcode这个平台来刷题库。编程我只学过基础的C语言,现在在自学Python,所以用Python3.8刷题库。现在我Python掌握的还不是很熟练,算法什么的也还没学,就先不考虑算法上的优化了,单纯以解题为目的,复杂程度什么的以后有时间再优化。计划顺序五个题写一篇日志,希望其他初学编程的人起到一些帮助,写算是对自己学习历程的一个见证了吧。

有一起刷LeetCode的可以关注我一下,我会一直发LeetCode题库Python3解法的,也可以一起探讨。

觉得有用的话可以点赞关注下哦,谢谢大家!
········································································································································································
题解框架:

	1.题目,难度
	2.题干,题目描述
	3.题解代码(Python3(不是Python,是Python3))
	4.或许有用的知识点(不一定有)
	5.解题思路
	6.优解代码及分析(当我发现有比我写的好很多的代码和思路我就会写在这里)

········································································································································································

No.41.缺失的第一个正数

难度:困难
题目描述:
Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第1张图片

题解代码(Python3.8)

class Solution:
    def firstMissingPositive(self, nums: List[int]) -> int:
        def swap(index1,index2):
            nums[index1],nums[index2] = nums[index2],nums[index1]

        l=len(nums)
        for i in range(l):
            while 1 <= nums[i] <= l and nums[i] != nums[ nums[i]-1 ]:
                swap(i,nums[i]-1)
        
        for i in range(l):
            if i+1 != nums[i]:
                return i+1
        return l+1

或许有用的知识点:
这道题会用到‘桶的思想’和‘抽屉原理’。
在学习“排序算法”的时候,可能会忽略“桶排序”的作用,但它的思想的确可以解决一些特定问题。所谓桶排序,即每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个桶中,对每个桶中元素进行排序,最后将非空桶中的元素逐个放入原序列。
这道题还使用了“抽屉原理”,有些地方也叫“鸽巢原理”,说白了就是“一个萝卜一个坑”。抽屉原理的一般含义为:“如果每个抽屉代表一个集合,每一个苹果就可以代表一个元素,假如有 n + 1 个元素放到 n 个集合中去,其中必定有一个集合里至少有两个元素。” 抽屉原理有时也被称为鸽巢原理。它是组合数学中一个重要的原理。
这道题中用到了len个容纳范围为1的桶,len个桶容纳的总范围为[1,len] 但是有一些元素是超出范围[1,len]的,所以这个时候用到了抽屉原理,即有超出[1,len]范围的元素存在时,至少有一个抽屉中放的是超出范围的元素,对这些超出范围的元素,程序中选择的处理方式是忽略。

解题思路:
我们可以把数组进行一次“排序”,“排序”的规则是:如果这个数字 i 落在“区间范围里”,i 就应该放在索引为 i - 1 的位置上,下面具体解释。
1、数字 i 落在“区间范围里”;
例如:[3, 4, -1, 1],一共 4 个数字,那么如果这个数组中出现 “1”、“2”、“3”、“4”,就是我们重点要关注的数字了;又例如:[7, 8, 9, 11, 12] 一共 5 个数字,每一个都不是 “1”、“2”、“3”、“4”、“5” 中的一个,因此我们无须关注它们;
2、i 就应该放在索引为i - 1 的位置上;
这句话也可以这么说 “索引为 i 的位置上应该存放的数字是 i + 1”。例如,数字1应该放在索引为0的位置上,数字3应该放在索引为2的位置上,数字4应该放在索引为3的位置上。一个数字放在它应该放的位置上,我们就认为这个位置是“和谐”的,看起来“顺眼”的。
按照以上规则排好序以后,缺失的第1个正数一下子就看出来了,那么“最不和谐”的数字的索引+1,就为所求。那如果所有的数字都“和谐”,数组的长度+1就为所求。
其实,这道题中用到了len个容纳范围为1的桶,len个桶容纳的总范围为[1,len] 但是有一些元素是超出范围[1,len]的,所以这个时候用到了抽屉原理,即有超出[1,len]范围的元素存在时,至少有一个抽屉中放的是超出范围的元素,对这些超出范围的元素,程序中选择的处理方式是忽略。

No.42.接雨水

难度:困难
题目描述:
Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第2张图片

题解代码(Python3.8)

class Solution:
    def trap(self, height: List[int]) -> int:
        #思路:雨水面积=组合成的凸边形面积-柱子面积
        area=0
        i=0
        j=len(height)-1
        left_highest=0
        right_highest=0
        pre=0
        if i==j:
            return 0
        while i<j:
            if left_highest<=right_highest:
                while i<j and height[i]<=left_highest:
                    i+=1
            if right_highest<=left_highest:
                while i<j and height[j]<=right_highest:
                    j-=1
            area += (min(height[i],height[j])-pre)*(j-i+1)
            pre = min(height[i],height[j])
            left_highest=height[i]
            right_highest=height[j]
        
        area_black=0
        for h in height:
            area_black+=h
        
        area_rain = area - area_black
        return area_rain

或许有用的知识点:
这道题要用到双指针法。

解题思路:
总思路:能够接到的雨水面积 = 输入数组所构成的凸多边形面积(S1) - 输入数组面积(S2)。
分解步骤:
1.若当前左最高值高于右最高值,则左指针保持不动;右指针同理;
2.用一个变量pre记录前一次增加的斜线标识部分面积的高度,下一次增加的面积应是(min(height[i], height[j]) - pre) * (j - i + 1)
3.更新左右最大高度
4.(最后返回时要注意无法接雨水的情况)

No.43.字符串相乘

难度:中等
题目描述:
Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第3张图片

题解代码(Python3.8)

class Solution:
    def multiply(self, num1: str, num2: str) -> str:
        def Multiply(string,n):
            s=string [::-1]
            res=[]
            for i,digit in enumerate(s):
                num=int(digit)
                res.append(num*n)
            res=Carry(res)
            res=res[::-1]
            return ''.join(str(x) for x in res)
        
        def Add(s1,s2):
            l1,l2=len(s1),len(s2)
            if l1<l2:                                       #保证被加数s1比加数s长
                s1,s2=s2,s1
                l1,l2=l2,l1
            s1=[int(x) for x in s1]
            s2=[int(x) for x in s2]
            s1,s2=s1[::-1],s2[::-1]
            for i,digit in enumerate(s2):
                s1[i]+=s2[i]
            s1=Carry(s1)
            s1=s1[::-1]
            return ''.join(str(x) for x in s1)

        def Carry(nums):
            l=len(nums)
            i=0
            while i<l:
                if nums[i]>=10:
                    carry=nums[i]//10
                    if i==l-1:
                        nums.append(carry)
                    else:
                        nums[i+1]+=carry
                    nums[i]=nums[i]%10
                i+=1
            return nums

        if num1=='0' or num2=='0':                      #特判
            return '0'
        l1,l2=len(num1),len(num2)
        if l1<l2:                                       #保证被乘数num1比乘数num2长
            num1,num2=num2,num1
            l1,l2=l2,l1

        num2=num2[::-1]
        res='0'
        for i,digit in enumerate(num2):
            tmp=Multiply(num1,int(digit))+'0'*i         #计算num1与num2当前位的乘积
            res=Add(res,tmp)                    #计算res与tmp的和
        return res

或许有用的知识点:
这道题可以用到python的enumerat()函数,enumerate()函数的部分介绍如下:

Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第4张图片

解题思路:
这道题按照题意应该不能使用任何标准库的大数类型(比如 BigInteger)或直接将输入转换为整数来处理。所以我们希望可以把最多1位字符串转换成整数(避免超长)。我们的代码可能跑出来排名比较落后,可能是有些人直接用了库函数或者直接转成整形,我们了解正确的方法就好。
我们的思路是像竖式乘法一样一位一位的算,我们先定义三个函数:乘法(一串数与一位数的乘积)、加法(两串数的加法)、进位计算。

No.44.通配符匹配

难度:困难
题目描述:
Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第5张图片
Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第6张图片

题解代码(Python3.8)

class Solution:
    def isMatch(self, s: str, p: str) -> bool:
        ls=len(s)
        lp=len(p)
        dp=[[False]*(lp+1) for _ in range(ls+1)]                #初始化
        dp[0][0]=True
        for j in range(1,lp+1):
            if p[j-1]=='*':
                dp[0][j]=dp[0][j-1]
        for i in range(1,ls+1):
            for j in range(1,lp+1):
                if s[i-1]==p[j-1] or p[j-1] == '?':
                    dp[i][j]=dp[i-1][j-1]
                elif p[j-1]=='*':
                    dp[i][j] = dp[i-1][j] or dp[i][j-1]
        return dp[-1][-1]

或许有用的知识点:
本题可以使用动态规划,也可以使用回溯算法。

解题思路:
dp[i][j]表示s到i位置,p到j位置是否匹配!
初始化:
dp[0][0]:什么都没有,所以为true
第一行dp[0][j],换句话说,s为空,与p匹配,所以只要p开始为*才为true
第一列dp[i][0],当然全部为False
动态方程:
如果(s[i] == p[j] || p[j] == “?”) && dp[i-1][j-1] ,有dp[i][j] = true
如果p[j] == “*” && (dp[i-1][j] = true || dp[i][j-1] = true) 有dp[i][j] = true
note:
dp[i][j-1],表示*代表是空字符,例如ab,ab*
dp[i-1][j],表示*代表非空任何字符,例如abcd,ab*

优解代码及分析:
优解代码(Python3.8)

class Solution:  
    def isMatch(self, s: str, p: str) -> bool:  # 回溯函数
        i = j = 0  # 用i索引s字符串,用j索引p字符串
        back_j = -1  # 记录p字符串中星号出现的位置
        match_i = 0  # 记录s字符串中从0到match_i位置都能匹配
        ls = len(s)
        lp = len(p)
        while i < ls:
            if j < lp and (s[i] == p[j] or p[j] == '?'):  # 若匹配,i和j往后移
                i += 1
                j += 1
            elif j < lp and p[j] == '*':  # *号出现,i不变,j记录
                back_j = j  # 记录星号的位置,方便回溯
                match_i = i  # 因为j处的'*'可以匹配s[i],所以match_i = i
                j += 1  # i不变,j加1,看i能否和星号后面字符的匹配,若匹配则i和j后移,若不匹配则回溯
            elif back_j != -1:  # 回溯,j回到星号位置的下一个位置,i回到match_i的下一个位置
                j = back_j + 1
                match_i += 1  # match_i一直加1就是为了找到能和back_j的下一个位置匹配的,若匹配i和j各自加1继续往后移,若不匹配j继续回到                                  # back_j的下一个位置,match_i继续加1,直到match_i和back_j匹配,或者i溢出
                i = match_i
            else:  # 还没出现*,但s[i]和p[j]已经不匹配了,所以False
                return False
        return list(p[j:]).count('*') == len(p[j:])  # 此时s[:i]和p[:j]已经匹配,而且i==len(s),若j也刚好溢出即p[j:]=[],或者p[j:]的                                                       # 内容全是'*',则s和p匹配 

分析:
这是回溯算法的方法,详细的解释在代码中逐条给出了。

No.45.跳跃游戏Ⅱ

难度:困难
题目描述:
Python小白 Leetcode刷题历程 No.41-No.45 缺失的第一个正数、接雨水、字符串相乘、通配符匹配、跳跃游戏Ⅱ (有题干 有代码 有思路)_第7张图片

题解代码(Python3.8)

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

或许有用的知识点:
这道题可以用到贪心算法。

解题思路:
总思路:使用最少的步数到达最后一个位置,则第i步位置为第i−1步前的点中所能达到的最远位置。
贪心算法:
定义步数step=0,能达到的最远位置max_jump=0,和上一步到达的边界end=0。
遍历数组,遍历范围[0,n-1):
所能达到的最远位置max_jump=max(max_jump,nums[i]+i),表示上一最远位置和当前索引i和索引i上的步数之和中的较大者。
如果索引i到达了上一步的边界end,即i==end,则:
更新边界end,令end等于新的最远边界max_jump,即end=max_jump
令步数step加一
返回step
注意:数组遍历范围为[0,n-1),因为当i==0时,step已经加一,所以若最后一个元素也遍历的话,当end恰好为n−1,步数会多1。
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)

你可能感兴趣的:(Python,Leetcode刷题,No.1-100)