之前开了一版,再来一次看看可以用到什么程度了
125. Valid Palindrome | Easy
ver1的思路很简单,check是不是字母数字,不是的话就移动指针;如果是的话就判断(字母统一转小写)是否是相同的char,不是的话就不是,是的话就移动指针继续check;
class Solution:
def isPalindrome(self, s: str) -> bool:
left, right = 0, len(s)-1
def check(char):
return (ord('a') <= ord(char) <= ord('z') or
ord('A') <= ord(char) <= ord('Z') or
ord('0') <= ord(char) <= ord('9'))
while left < right:
if check(s[left]) and check(s[right]):
if s[left].lower() != s[right].lower():
return False
else:
left += 1
right -= 1
if not check(s[left]):
left += 1
if not check(s[right]):
right -= 1
return True
ver2是在check不是字母数字的时候做了优化,直接在当前while循环中找到合法的char的left & right位置,ps:顺序也重要hh
class Solution:
def isPalindrome(self, s: str):
left, right = 0, len(s) - 1
while left <= right:
while left < right and not self.helper(s[left]):
left += 1
while left < right and not self.helper(s[right]):
right -= 1
if s[left].lower() != s[right].lower():
return False
left += 1
right -= 1
return True
def helper(self, char):
return (ord('A') <= ord(char) <= ord('Z') or
ord('a') <= ord(char) <= ord('z') or
ord('0') <= ord(char) <= ord('9'))
167. Two Sum II - Input Array Is Sorted
- ps:划重点已经排序!
- left,right从起始位置0,len(numbers)-1出发,如果他们的和小于target,而此时right所指的是当前array的最大值,所以只能是left往右边挪;同理和大于target的情况;
- 哈哈哈哈哈自己写的时候出错了看了一下是写了if,if,else,问题在最后一个else那里,他的else是离它最近的if条件的补集,不是我想要的前两个if条件的补集,应该改为if,elif,else;
class Solution:
def twoSum(self, numbers: List[int], target: int) -> List[int]:
left, right = 0, len(numbers) - 1
while left < right:
tmp = numbers[left] + numbers[right]
if tmp == target:
return [left + 1, right + 1]
if tmp < target:
left += 1
if tmp > target:
right -= 1
15. 3Sum
DFS | Fail
- 救命 还以为dfs可以,还是超时了,不过记录一下去重的一点
- 一开始我得去重写的是,这个问题就是,你不仅是把同层的重复元素剪枝了,还把下一层和构建到这个节点的路径上的元素重复的也剪掉了:举个例子[-1,-1,0]这个也被剪掉了,因为也会进入以下这个去重语句中continue掉了;
if i > 0 and nums[i] == nums[i-1]:
continue
- 既然我只想去重同层出现过的元素,下一层在[start:]index中选择不管是否和[:start]index中的元素有重复,那么也就是说[-1,-1]:第二个-1进来之后他的start是等于1的,他不会进入到去重判断语句中;
感觉还是没有讲清楚orz
if i > start and nums[i] == nums[i-1]:
continue
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res, tmp = [], []
nums = sorted(nums)
def dfs(start, check):
if len(tmp) == 3 and check == 0:
res.append(tmp.copy())
return
for i in range(start, len(nums)):
if i > start and nums[i] == nums[i-1]:
continue
tmp.append(nums[i])
check += nums[i]
dfs(i+1, check)
tmp.pop()
check -= nums[i]
dfs(0, 0)
return res
排序+双指针 | Pass
- 不仅仅是转到两数之和(167)的问题,由于这道题是需要返回所有的可能但是不能有重复;去重就是关键了;
- 首先是target的去重;
- fix target假设找到一个符合条件的解了,仍然还要继续搜索,此时left right要挪到与那个符合条件解不同的值为止,这里不用while会超时orz;
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums = sorted(nums)
for i in range(len(nums) - 2):
if i > 0 and nums[i] == nums[i-1]: # target去重
continue
left, right = i + 1, len(nums) - 1
while left < right:
tmp = nums[i] + nums[left] + nums[right]
if tmp == 0:
res.append([nums[i], nums[left], nums[right]])
left += 1
while left < right and nums[left] == nums[left-1]: # 左加数去重
left += 1
right -= 1
while left < right and nums[right] == nums[right+1]: # 右加数去重
right -= 1
if tmp < 0:
left += 1
if tmp > 0:
right -= 1
return res
11. Container With Most Water
- 左右指针更新的问题:
- 当前左右指针的数值不等:好办;
- 当前左右指针的数值相等:为什么可以直接丢进上述1中的任意一个去做呢?
- 当[left, right]内没有比当前大的值时,实际上你没必要再去while,最大值就是现在的res;
- 当[left, right]内存在一个比当前值大时,实际上也没必要去while,有一个比他高的,但是次高的还是当前值,但是现在的宽度是比res小的,所以最大值还是现在的res;
- 当[left, right]内存在不止一个当前值大时,这个时候是有可能出现比res大的值的,按照我们的策略(保留左右的max height)不管是left+=1,还是right-=1,他们都会在第一个(左和右)比当前值高的时候统一步调(后续同步除非又又遇到等高,碰到两个高高又统一,嗯), 即res相同了;
class Solution:
def maxArea(self, height: List[int]):
res = 0
left, right = 0, len(height)-1
while left < right:
res = max(res, (right - left) * min(height[left], height[right]))
if height[left] < height[right]:
left += 1
else:
right -= 1
return res
42. Trapping Rain Water
- 一开始想到的是遍历两次,一次for去把左起的最高值save下来,同时把右起的最高值save下来;第二次for去看看能接多少雨水,只有当前高度小于当前左右两边最高值的时候能接;
- 那么有没有简化的可能呢?
- 双指针登场了!
- 这里left和right什么时候选谁接雨水嘞?判断条件是lmax和rmax的大小,小的那边指针指的先接;
- 为什么呢?因为实际上是对上面那个解法的一个简化:假设现在lmax<rmax,我们left想知道left前的最大值和left后的最大值,然后取这两最大值的最小值就是left水位最高处,简化做在哪里呢?实际上是在判断lmax<rmax的时候,我们知道从right位置之后的最高值是大于当前left之前的最大值的,也就是我们不用直到确切的两个最大值,我们只需要直到两个最大值的最小值,而left到right之间的值已经不需要check了,即left之后的最高值只会不小于当前的rmax,所以现在的left的最高水位就是lmax了;
还是没有讲顺的感觉orz
class Solution:
def trap(self, height):
if len(height) <= 2:
return 0
res = 0
left, right = 1, len(height) - 2
lmax, rmax = height[left - 1], height[right + 1]
while left < right:
if lmax < rmax:
res += max(0, lmax - height[left])
lmax = max(lmax, height[left])
left += 1
else:
res += max(0, rmax - height[right])
rmax = max(rmax, height[right])
right -= 1
return res