第一次刷leetcode,结果的执行时间用同样的程序运行出来起伏都很大,我不太懂是为什么。
目录
167. 两数之和 II - 输入有序数组(easy)
题目
给定一个已按照 升序排列 的整数数组 numbers ,请你从数组中找出两个数满足相加之和等于目标数 target 。
函数应该以长度为 2 的整数数组的形式返回这两个数的下标值。numbers 的下标 从 1 开始计数 ,所以答案数组应当满足 1 <= answer[0] < answer[1] <= numbers.length 。
你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum-ii-input-array-is-sorted
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的答案V1
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i = 1
j = len(numbers)
answer = [0,0]
while i < j:
if numbers[i-1] + numbers[j-1] == target:
answer[0] = i
answer[1] = j
break
elif numbers[i-1] + numbers[j-1] > target:
j = j - 1
else:
i = i + 1
return answer
结果V1:
出错的地方
1、answer未初始化。
2、numbers[i-1]和numbers[j-1]未减1,因为i从1开始,而数组索引从0开始。
V2
在循环中将相等的判断条件放在了最后。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i = 1
j = len(numbers)
answer = [0,0]
while i < j:
if numbers[i-1] + numbers[j-1] > target:
j = j - 1
elif numbers[i-1] + numbers[j-1] < target:
i = i + 1
else:
answer[0] = i
answer[1] = j
break
return answer
结果V2:
V3
将numbers[i-1] + numbers[j-1]先算好赋值给sum。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i = 1
j = len(numbers)
answer = [0,0]
while i < j:
sum = numbers[i-1] + numbers[j-1]
if sum > target:
j = j - 1
elif sum < target:
i = i + 1
else:
answer[0] = i
answer[1] = j
break
return answer
结果V3:
V4
去掉answer。
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
i = 1
j = len(numbers)
while i < j:
sum = numbers[i-1] + numbers[j-1]
if sum > target:
j = j - 1
elif sum < target:
i = i + 1
else:
return [i, j]
return []
结果V4:
633. 两数的平方和(easy)
题目
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。
我的答案V1
class Solution:
def judgeSquareSum(self, c: int) -> bool:
a = 0
b = int(sqrt(c))
while a <= b:
sum = a*a + b*b
if sum > c:
b = b - 1
elif sum < c:
a = a + 1
else:
return True
if a > b:
return False
结果:
V2
class Solution:
def judgeSquareSum(self, c: int) -> bool:
assert c >= 0 # 严谨!
a = 0
b = int(sqrt(c))
while a <= b:
sum = a*a + b*b
if sum > c:
b = b - 1
elif sum < c:
a = a + 1
else:
return True
return False # 删掉不必要的判断
结果:
345. 反转元音字符(easy)
题目
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
我的答案V1
将所有元音字母所在位置保存在index数组中,并将字符串转换为字符数组,替换其中符合条件的元素。
class Solution:
def reverseVowels(self, s: str) -> str:
vowel = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']
index = []
string = list(s)
for indexi, i in enumerate(s):
if i in vowel:
index.append(indexi)
print(index)
length = len(index)-1
for i, j in enumerate(index):
string[j] = s[index[length-i]]
str = ''
return str.join(string)
结果:
V2
依照前面两题的思路,从字符串两头开始寻找。
class Solution:
def reverseVowels(self, s: str) -> str:
vowel = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']
ls = list(s)
string = ls
length = len(s)
a = 0
b = length-1
while a < b:
if s[a] not in vowel:
a = a + 1
elif s[b] not in vowel:
b = b - 1
else:
string[a] = s[b]
string[b] = s[a]
a = a + 1
b = b - 1
str = ''
return str.join(string)
结果:
V3(最佳)
class Solution:
def reverseVowels(self, s: str) -> str:
vowel = "aeiouAEIOU" # 改成字符串,输入方便
ls = list(s)
string = ls
a, b = 0, len(s)-1 # 一起初始化
while a < b:
if s[a] not in vowel:
a = a + 1
elif s[b] not in vowel:
b = b - 1
else:
ls[a], ls[b] = ls[b], ls[a] #一起交换,避免值已发生改变的问题
a = a + 1
b = b - 1
return ''.join(ls) # 不需要str
125. 验证回文串(easy)
题目
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
我的答案V1
class Solution:
def isPalindrome(self, s: str) -> bool:
start = 0
end = len(s) - 1
while start < end:
while not s[start].isalnum():
if start == end:
break
start = start + 1
while not s[end].isalnum():
if start == end:
break
end = end - 1
if s[start].upper() != s[end].upper():
return False
start = start + 1
end = end - 1
return True
结果:
V2
class Solution:
def isPalindrome(self, s: str) -> bool:
if len(s) == 1: #判断是否为单个字符
return True
start, end = 0, len(s) - 1
while start < end:
while not s[start].isalnum():
if start == end:
break
start = start + 1
while not s[end].isalnum():
if start == end:
break
end = end - 1
if s[start].upper() != s[end].upper():
return False
start = start + 1
end = end - 1
return True
结果:
680. 验证回文字符串2(easy)
题目
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
我的答案V1
class Solution:
def validPalindrome(self, s: str) -> bool:
flag = False
if len(s) == 1:
return True
start, end = 0, len(s) - 1
while start < end:
if s[start] != s[end]:
if flag:
break
i, j = start, end
start += 1
flag = True
continue
start += 1
end -= 1
if start >= end:
return True
start, end = i, j-1
flag = False
while start < end:
if s[start] != s[end]:
return False
start += 1
end -= 1
return True
结果:
V2
把出现start!=end之后的判断过程写成一个函数。
class Solution:
def validPalindrome(self, s: str) -> bool:
if len(s) <= 2:
return True
start, end = 0, len(s) - 1
def isPalindrome(s, start, end):
while start < end:
if s[start] != s[end]:
return False
start += 1
end -= 1
return True
while start < end:
if s[start] != s[end]:
return isPalindrome(s, start+1, end) or isPalindrome(s, start, end-1)
start += 1
end -= 1
return True
结果:
344. 反转字符串(easy)
题目
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。
不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。
你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/reverse-string
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的答案V1
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
start, end = 0, len(s)-1
while start < end:
s[start], s[end] = s[end], s[start]
start += 1
end -= 1
return s
结果:
V2
class Solution:
def reverseString(self, s: List[str]) -> None:
"""
Do not return anything, modify s in-place instead.
"""
if not s: # 考虑s为空字符串的情况
return []
start, end = 0, len(s)-1
while start < end:
s[start], s[end] = s[end], s[start]
start += 1
end -= 1
return s
11.盛最多水的容器(medium)
题目
给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器。
示例 1:
输入:[1,8,6,2,5,4,8,3,7]
输出:49
解释:图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/container-with-most-water
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
我的答案
我的第一个想法是以固定宽度interval(interval在迭代中逐渐减小)穷举,为了缩小这个“固定宽度”的范围,先找了height中最大的两个数,得到可能的最高水位,在interval迭代的过程中,若当前interval即使乘上最高水位都不可能比当前最大面积大,则不必再减小interval了,也就是结束循环,当前最大面积即为最终要求的最大面积。
class Solution:
def maxArea(self, height: List[int]) -> int:
interval = len(height) - 1
n = interval - 1
h1, h2 = 0, 0
# 找到最大的两个值
for i in range(len(height)):
if height[i] >= h2:
if height[i] >= h1:
h1, h2 = height[i], h1
else:
h2 = height[i]
max_height = min(h1, h2)
current_maxArea = 0
if h2 == 0: # 一开始没考虑到
return 0
while interval > int(current_maxArea/max_height):
start, end = 0, interval
while end < len(height):
current_Area = min(height[start], height[end]) * interval
if current_Area > current_maxArea:
current_maxArea = current_Area
start += 1
end += 1
interval -= 1
return current_maxArea
结果很悲惨,超出时间限制了:
V2
后来看了双指针(对撞指针)中的方法,理解他的思路。我发现在思考这个问题的时候我一直漏掉一个关键点:水位的高度取决于两端中最矮的那个值
,当我的两端从首尾开始循环时,我的目的是找到能让水位更高的值(因为宽度已经是最大的了,只有水位更高才有可能超过初始面积),所以总是让左端和右端中最矮的那个向内移动,试图在*height[start]到height[end]*之间找到更大的值使水位升高。
class Solution:
def maxArea(self, height: List[int]) -> int:
if len(height) < 2:
return 0
start, end = 0, len(height) - 1
current_maxArea = 0
while start < end:
current_Area = (end - start) * min(height[start], height[end]) # end和start一开始写反了
if current_Area > current_maxArea:
current_maxArea = current_Area
if height[start] < height[end]:
start += 1
else:
end -= 1
return current_maxArea
结果:
V3
在V2中,可以发现每次移动指针都需要计算一次面积,为了进一步减少面积的计算次数,在移动指针时一直移动到比原本值更大时才会计算面积。
class Solution:
def maxArea(self, height: List[int]) -> int:
if len(height) < 2:
return 0
start, end = 0, len(height) - 1
current_maxArea = 0
while start < end:
current_Area = (end - start) * min(height[start], height[end]) # end和start一开始写反了
if current_Area > current_maxArea:
current_maxArea = current_Area
min_start_end = min(height[start], height[end])
if height[start] < height[end]:
while height[start] <= min_start_end: # 一直移动到找到更大的值
start += 1
if start == end:
break
else:
while height[end] <= min_start_end: # 一直移动到找到更大的值
end -= 1
if start == end:
break
return current_maxArea
结果:
V4(四个版本中最优)
更进一步的,受
盛最多水的容器(双指针法,易懂解析,图解)
的启发,V3中第8行和第12行重复比较了height[start]和height[end]的值,因此将面积计算公式放在第12行判断了大小之后。
class Solution:
def maxArea(self, height: List[int]) -> int:
if len(height) < 2:
return 0
start, end = 0, len(height) - 1
current_maxArea = 0
while start < end:
min_start_end = min(height[start], height[end])
if height[start] < height[end]:
current_maxArea = max(current_maxArea, (end - start) * height[start])
while height[start] <= min_start_end:
start += 1
if start == end:
break
else:
current_maxArea = max(current_maxArea, (end - start) * height[end])
while height[end] <= min_start_end:
end -= 1
if start == end:
break
return current_maxArea
结果:
总结
- 涉及数组或字符串中前后元素关系的问题考虑双指针。
- 在最开始先考虑一些特殊情况,比如“盛水最多的容器”这道题,受限排除掉height只有一个值的情况。
- 用断言确保输入变量符合题目所给条件,如“两数平方和”中通过assert c >= 0确保c为非负整数,这样更加严谨。
- 多个变量一起初始化时可以写在同一行,缩短代码长度。
- 交换两个数时同第4点,避免用到额外变量,如“反转元音字母”的V3版本。
- 需要多次实现的功能可以写成一个函数,缩短代码长度,如“验证回文字符串2”的V2版本。
- 减少重复的判断,如“盛水最多的容器”的V4。