双指针
平方数之和 lc 633
给定一个非负整数 c ,你要判断是否存在两个整数 a 和 b,使得 a2 + b2 = c 。
class Solution:
def judgeSquareSum(self, c: int) -> bool:
for i in range(0,int(c**0.5)+1):
for j in range(i,int(c**0.5)+1):
if i**2+j**2==c:
return True
return False
class Solution:
def judgeSquareSum(self, c: int) -> bool:
i,j=0,int(c**0.5)
while i<=j:
if i*i+j*j==c:
return True
elif i*i+j*j>c:
j-=1
elif i*i+j*j<c:
i+=1
return False
- 下面的优化用二分的思路,第一个运算太多暴力穷举。
- 最长上升子序列是每次都从前面看一遍,如果是每一个数和所有数的关系,是往后看。
最接近的三数之和 lc 16
给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。
class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:
n=len(nums)
if(not nums or n<3):
return None
nums.sort()
res=float("inf")
for i in range(n-2):
#sort之后的剪枝操作,回溯也一样
if(i>0 and nums[i]==nums[i-1]):
continue
L=i+1
R=n-1
while(L<R):
cur_sum=nums[i]+nums[L]+nums[R]
if(cur_sum==target):
return target
if(abs(cur_sum-target)<abs(res-target)):
res=cur_sum
if(cur_sum-target<0):
L+=1
else:
R-=1
return res
lc 18. 四数之和
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
if not nums or len(nums)<4:return []
res = []
nums.sort()
for i in range(len(nums)-3):
for j in range(i+1,len(nums)-2):
left = j+1
right = len(nums)-1
while left<right:
cur_sum = nums[i]+nums[j]+nums[left]+nums[right]
if cur_sum==target:
tmp_lst =[nums[i],nums[j],nums[left],nums[right]]
if tmp_lst not in res:
res.append(tmp_lst)
right-=1
left+=1
elif cur_sum>target:
right=right-1
else:
left=left+1
return res
- 套路都是一样的,关键是后面有几个数不需要便利,比如本题第一个for后面有三个数,第二次for后面有两个数。
- 有重复直接说not in或者in res就ok。
反转字符串中的元音字母 lc 345
class Solution:
def reverseVowels(self, s: str) -> str:
yuanyin = ['a','e','i','o','u','A','E','I','O','U']
s_list = [i for i in s]
i,j = 0,len(s_list)-1
while i<j and i<len(s_list) and j>0:
if s_list[i] not in yuanyin:
i+=1
if s_list[j] not in yuanyin:
j-=1
if s_list[i] in yuanyin and s_list[j] in yuanyin:
s_list[i],s_list[j] = s_list[j],s_list[i]
i+=1
j-=1
return ''.join(s_list)
def reverseVowels(self, s: str) -> str:
l = len(s)
s = [char for char in s]
vowel = ["a", "e", "i", "o", "u", "A", "E", "I", "O", "U"]
left = 0
right = l - 1
while(left < right):
while(left <= l -1 and s[left] not in vowel):
left += 1
while(right >= 0 and s[right] not in vowel):
right -= 1
#这个位置必须判断,否则会内部越界
if left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
return "".join(s)
- 这是经典的写法,包括快排。
验证回文字符串 Ⅱ lc 680
最多只删除一次,判断是否是回文串,如果原来就是也可以。
class Solution:
def validPalindrome(self, s: str) -> bool:
def isPalindrome(s,start,end):
while start < end:
if s[start] == s[end]:
start+=1
end-=1
else:
return False
return True
i = 0
j = len(s)-1
while i<j:
if s[i]==s[j]:
i+=1
j-=1
else:
return isPalindrome(s,i+1,j) or isPalindrome(s,i,j-1)
return True
- 回文是一个相同的操作首尾的走,但是判断一次当时想的是设置一个flag,但是不太好构思后面的。
- 于是乎,其实错开位置再执行相同的内容,也别非去递归了,如上所示就可以。多看看,借鉴这个思路。
- 这个题目好在形式上看出来后无效性,dp,直接舍弃两边成功的,看中间的。
颜色分类 lc 75
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
输入:nums = [2,0,2,1,1,0]
输出:[0,0,1,1,2,2]
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
#p,k分别指向首尾,q是移动指针,要移动的那个指针和尾部的去查
p,q=0,0
k=len(nums)-1
while q<=k:
if p<q and nums[q] == 0:
nums[p],nums[q] = nums[q],nums[p]
p+=1
elif nums[q]==2:
nums[q],nums[k] = nums[k],nums[q]
k-=1
else:
q+=1
- 这个题是三个指针。
- 快速的在范围内查找。
- 不改变地方,原地的改变位置。
删除排序后重复的数
lc 26. 删除有序数组中的重复项
每个元素最多出现一次
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
for i in range(len(nums)):
left = i
right = i
while right<len(nums) and nums[left]==nums[right]:
right+=1
while right<len(nums) and right-left>=1 and nums[left]==nums[right]:
nums.pop(left)
right-=1
lc 80. 删除有序数组中的重复项 II
每个元素最多出现两次,但是其实我的框架可以最多出现k次都没所谓,很明显的双指针
class Solution:
def removeDuplicates(self, nums: List[int]) -> int:
for i in range(len(nums)):
left = i
right = i
while right<len(nums) and nums[left]==nums[right]:
right+=1
while right<len(nums) and right-left>=2 and nums[left]==nums[right]:
nums.pop(left)
right-=1
lc 42 接雨水
class Solution:
def trap(self, height) -> int:
# 边界条件
if not height: return 0
n = len(height)
maxleft = [0] * n
maxright = [0] * n
ans = 0
# 初始化
maxleft[0] = height[0]
maxright[n-1] = height[n-1]
# 设置备忘录,分别存储左边和右边最高的柱子高度
for i in range(1,n):
maxleft[i] = max(height[i],maxleft[i-1])
for j in range(n-2,-1,-1):
maxright[j] = max(height[j],maxright[j+1])
# 一趟遍历,比较每个位置可以存储多少水
for i in range(n):
if min(maxleft[i],maxright[i]) > height[i]:
ans += min(maxleft[i],maxright[i]) - height[i]
return ans
# 自然的想法
def trap(self, height) -> int:
ans = 0
for i in range(len(height)):
max_left, max_right = 0, 0
# 寻找 max_left
for j in range(0, i):
max_left = max(max_left, height[j])
# 寻找 max_right
for j in range(i, len(height)):
max_right = max(max_right, height[j])
if min(max_left, max_right) > height[i]:
ans += min(max_left, max_right) - height[i]
return ans
- 比较经典的题,如果是单调栈比较不好想,从中间往两边找,找到当前位置的最大值就找两边的小值之差就是能存住的雨水量。
lc 84 直方图的面积
class Solution:
def largestRectangleArea(self, heights) -> int:
res = 0
n = len(heights)
for i in range(n):
left_i = i
right_i = i
while left_i >= 0 and heights[left_i] >= heights[i]:
left_i -= 1
while right_i < n and heights[right_i] >= heights[i]:
right_i += 1
res = max(res, (right_i - left_i - 1) * heights[i])
return res
def largestRectangleArea(self, heights: List[int]) -> int:
maxarea = 0
stk = list()
for i in range(len(heights)):
# 只要栈不为空,并且当前遍历的数据小于栈顶元素,则说明找到了右边界
while stk and heights[i] < heights[stk[-1]]:
h = heights[stk[-1]];
stk.pop()
# 出栈后,如果栈为空,说明出栈的柱子目前遍历的最短的柱子,否则计算宽度差
w = i - stk[-1] - 1 if stk else i
maxarea = max(h * w, maxarea)
# 栈为空或者当前遍历的数据大于等于栈顶数据,则入栈,不会执行上面的while循环
# 保证了栈中的数据总是递增的 比如 0 1 2 2 3 4 4 5 6 ...
stk.append(i)
# 处理剩余栈中的数据(递增的数据,右边界是柱状图中最右边的柱子)
while stk:
h = heights[stk[-1]]
stk.pop()
# 出栈后,如果栈为空,说明出栈的柱子目前遍历的最短的柱子,否则计算宽度差
w = len(heights) - stk[-1] - 1 if stk else len(heights)
maxarea = max(h * w, maxarea)
return maxarea
- 单调栈还是不好想,基本的想法是:说从每一个柱子遍历,如果两边有比自己大,那用自己的高度✖️比他高的跨度。这是一定满足条件的,整体的遍历滚动起来则覆盖了所有的内容。
- 单调栈则是维护了一个单调递增的栈,计算当前。
- 这个题和接雨水不同的是接雨水是远端一直的最大值是目标。这个题好是求面积所以是说临近的比他小就停下了。