【PythonCode】力扣Leetcode11~15题Python版

【PythonCode】力扣Leetcode11~15题Python版

前言

力扣Leetcode是一个集学习、刷题、竞赛等功能于一体的编程学习平台,很多计算机相关专业的学生、编程自学者、IT从业者在上面学习和刷题。
在Leetcode上刷题,可以选择各种主流的编程语言,如C++、JAVA、Python、Go等。还可以在线编程,实时执行代码,如果代码通过了平台准备的测试用例,就可以通过题目。
本系列中的文章从Leetcode的第1题开始,记录我用Python语言提交的代码和思路,供Python学习参考。

11. 盛最多水的容器

给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。

返回容器可以储存的最大水量。

说明:你不能倾斜容器。

在这里插入图片描述

示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
示例 2:
输入:height = [1,1]
输出:1
提示:
n == height.length
2 <= n <= 105
0 <= height[i] <= 104

代码实现:

class Solution:
    def maxArea(self, height: List[int]) -> int:
        i, j = 0, len(height) - 1
        max_area = 0
        while i < j:
            max_area = max(max_area, (j-i) * min(height[i], height[j]))
            if height[i] < height[j]:
                i += 1
            else:
                j -= 1
        return max_area

解题思路:根据题意,本题求的结果是一个长方形的面积,目的是在数组中找到两个数,使它们围成的长方形面积最大。如果这两个数在数组中的索引分别为i和j,那长方形的宽就是 j-i ,长方形的高就是 height[i]和height[j] 中较小的一个。分析到这里,原理基本就清楚了,最粗暴的方式就是遍历数组中所有 i和j 的组合,以找到最大面积,但是这种方式的时间复杂度为 O(N^2),不能通过测试,所以要进行优化。

解题时,可以使用双指针方式,i 初始指向数组的第一个元素,j 初始指向数组的最后一个元素,在初始状态下,长方形的宽度 j-i 是最大的,当指针移动时,宽度不断变小。在宽度不断变小的情况下,如果想得到更大的面积,肯定要找到更高的值,才有可能,所以,每次都保留值更大的指针,移动值更小的指针,看能否找到更大的数,直到指针相遇。指针移动的过程中不断更新最大面积,最后将值返回。这样时间复杂度为 O(N),满足题目要求。

12. 整数转罗马数字

罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

例如, 罗马数字 2 写做 II ,即为两个并列的 I。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给你一个整数,将其转为罗马数字。

示例 1:
输入: num = 3
输出: “III”
示例 2:
输入: num = 4
输出: “IV”
示例 3:
输入: num = 9
输出: “IX”
示例 4:
输入: num = 58
输出: “LVIII”
解释: L = 50, V = 5, III = 3.
示例 5:
输入: num = 1994
输出: “MCMXCIV”
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= num <= 3999

代码实现:

class Solution:
    def intToRoman(self, num: int) -> str:
        ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"]
        tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"]
        hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"]
        thousands = ["", "M", "MM", "MMM"]
        return thousands[num//1000] + hundreds[num%1000//100] + tens[num%100//10] + ones[num%10]

解题思路:根据罗马数字的规则,个、十、百、千的表示字母各不相同。所以,要将一个整数转换成罗马数字,可以先分解一下,分别将数字的个位数、十位数、百位数、千位数依次转换成罗马数字,再拼接在一起就可以了。

题目规定的数字在1-3999之间,所以在代码中,可以先初始化几个数组,分别表示1-9, 10-90, 100-900, 1000-3000的罗马数字,并且每个数组都要加上0(空字符)。计算数字的千位、百位、十位、个位,并从数组中取出对应的值拼接即可完成转换。

13. 罗马数字转整数

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。
给定一个罗马数字,将其转换成整数。

示例 1:
输入: s = “III”
输出: 3
示例 2:
输入: s = “IV”
输出: 4
示例 3:
输入: s = “IX”
输出: 9
示例 4:
输入: s = “LVIII”
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: s = “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15
s 仅含字符 (‘I’, ‘V’, ‘X’, ‘L’, ‘C’, ‘D’, ‘M’)
题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。

代码实现:

class Solution:
    def romanToInt(self, s: str) -> int:
        roman = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
        sum = 0
        for i in range(len(s) - 1):
            if roman[s[i]] >= roman[s[i+1]]:
                sum += roman[s[i]]
            else:
                sum -= roman[s[i]]
        return sum + roman[s[-1]]

解题思路:本题是上一题的逆转换,但是代码并不是完全一样的思路。根据罗马数字的规则,通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。也就是说,如果罗马数字中小的数字在大的数字的右边,那么是加的关系,如果罗马数字中小的数字在大的数字的左边,那么是减的关系。

所以要完成罗马数字转整数,只需要遍历罗马数字中的每个符号,如果符号比它的后一个符号大,则加上该符号表示的数值,如果符号比它的后一个符号小,则减掉符号的值。

14. 最长公共前缀

编写一个函数来查找字符串数组中的最长公共前缀。

如果不存在公共前缀,返回空字符串 “”。

示例 1:
输入:strs = [“flower”,“flow”,“flight”]
输出:“fl”
示例 2:
输入:strs = [“dog”,“racecar”,“car”]
输出:“”
解释:输入不存在公共前缀。
提示:
1 <= strs.length <= 200
0 <= strs[i].length <= 200
strs[i] 仅由小写英文字母组成

代码实现:

class Solution:
    def longestCommonPrefix(self, strs: List[str]) -> str:
        for i in range(len(strs[0])):
            for s in strs[1:]:
                if len(s) == i or s[i] != strs[0][i]:
                    return strs[0][0: i]
        return strs[0]

解题思路:本地要返回的是多个字符串的公共前缀,最后的返回结果也是一个字符串,如果没有公共前缀则返回空字符串。公共前缀是每一个字符串中都有的字符,并且都在字符串的开头,位置和顺序也一样。所以可以先从第一个字符串 strs[0] 中的第一个字符开始寻找,依次取出第一个字符串中的每个字符,遍历其他字符串,对比相同索引位的字符是否相等,如果遇到字符不相等则停止遍历,返回第一个字符串中前面的字符,就得到公共前缀。

同时,每个字符串的长度不一定相同,其他字符串的长度可能比第一个字符串更短,所以如果对比到了第 i 个字符,遇到一个字符串的长度只有 i-1,则停止遍历。

上面的代码中,先计算第一个字符串的长度,用索引从第一个字符串的第一个字符开始遍历,然后遍历剩余字符串,并进行条件判断,遇到不匹配时,返回结果。Python比较巧妙的一点是可以使用切片语法,让代码更简洁。此外,假如 strs 中只有一个字符串,遍历的代码跑不进去,这种情况应直接返回 strs[0] 。

15. 三数之和

给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请

你返回所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
解释:
nums[0] + nums[1] + nums[2] = (-1) + 0 + 1 = 0 。
nums[1] + nums[2] + nums[4] = 0 + 1 + (-1) = 0 。
nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0 。
不同的三元组是 [-1,0,1] 和 [-1,-1,2] 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = [0,1,1]
输出:[]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = [0,0,0]
输出:[[0,0,0]]
解释:唯一可能的三元组和为 0 。
提示:
3 <= nums.length <= 3000
-105 <= nums[i] <= 105

代码实现:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        result = []
        for i in range(len(nums) - 2):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            if nums[i] > 0:
                break
            k = len(nums) - 1
            for j in range(i + 1, len(nums) - 1):
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                while j < k and nums[i] + nums[j] + nums[k] > 0:
                    k -= 1
                if j == k:
                    break
                if nums[i] + nums[j] + nums[k] == 0:
                    result.append([nums[i], nums[j], nums[k]])
        return result

解题思路:根据题意,要在数组中找到三个数,使三个数之和为0,并且每个组合中不能重复利用同一个数字,最终要返回所有可能的不重复组合。因为结果是不重复的组合,并且题目也不要求数字组合的顺序与原数组一样,为了方便解题,可以先对数组进行排序。

排序后先取第一个数,就是遍历数组中的第一个到倒数第三个数字,如果遇到相等的数字就跳过,因为这种情况得到的组合是重复的。如果遇到大于0的数字就退出循环,因为已经将数组排序了,如果第一个数字大于0,后面的数字都大于或等于第一个数字,三个数的和不可能等于0。

取了第一个数,继续嵌套遍历取第二个数,即从第一个数字的后一个数字遍历到数组的倒数第二个数字,同理如果遇到相等的数字就跳过。

在遍历第二个数字的同时,可以用指针从数组的最后一个数字开始获取第三个数字,并在遍历的过程中计算三个数字的和。如果三个数的和等于0,说明找到了一种组合,将这种组合保存下来。如果三个数的和小于0,则说明当前找不到第三个数来与前两个数组合,不需要做处理,继续遍历改变前两个数。如果三个数的和大于0,则向左移动指针,使第三个数变小,直到找到满足和为0的组合或指针与第二个数的索引相遇。用指针的方式获取第三个数字,可以降低整体的时间复杂度。


相关阅读

PythonCode】力扣Leetcode6~10题Python版

📢欢迎 点赞👍 收藏⭐ 评论📝 关注 如有错误敬请指正!

☟ 学Python,点击下方名片关注我。☟

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小斌哥ge

非常感谢,祝你一切顺利。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值