【个人刷题记录】因为鼠鼠还在实习,所以基本一天4题左右。两题错题+两题新题,我真的不想刷了就忘了😭
每写完一个板块就会记录一下(未来二刷三刷如果有新的体会都会更新),自我监督,让自己刷的有动力~大家一起加油🎉
2 双指针
2.1 移动零
这道题关键在于处理 p 和 q 的相对位置,如果发现 p 超过 q,应该赶紧重置 q
python
class Solution(object):
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
# 原地移动联想到 双指针
# 一个指针遇到0就停止,一个指针往后走遇到非0则停止
p,q=0,1 # p遇0停,q遇非0停
length=len(nums)
while q < length:
while p<length and nums[p]!=0:
p+=1
continue
if q<p:
q=p # 初始化q的出发点这样才能保证q在p后面
while q<length and nums[q]==0:
q+=1
continue
if q>=length or p>=length:
break
elif nums[p]==0 and nums[q]!=0:
temp=nums[p]
nums[p]=nums[q]
nums[q]=temp
p+=1
q+=1
解法2:
相对于解法1,这里优化的点在于:不需要考虑交换
我们唯一需要做的就是将快指针的值复制给慢指针即可,遇到0直接跳过
python
class Solution(object):
def moveZeroes(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
# 双指针,新方法的核心点在于不需要进行交换操作,只需要统排非0部分即可,后面数值可以保留,最后统一用0覆盖
not_zero=0 # 这个指针之前的都是非0
length=len(nums)
for i in range (length):
if nums[i]!=0: # 非0则放到前面
nums[not_zero]=nums[i]
not_zero+=1 # 指针推进
for i in range(not_zero,length):
nums[i]=0
2.2 盛最多水的容器
思路半对,知道是双指针,知道从两边往中间走
但是移动谁呢?
我原本考虑的是:比较两边移动后,最低位最高的那个。
这个的问题在于:需要分很多情况,需要比较 1. 左侧移动后,相对原来左侧更高怎么办 2. 左侧移动后,相对更低,同时需要计算右侧移动后是更高还是更低 3. 需要计算新低,是移动后产生的新低?还是维持原来的旧低?
但是题解的思路很清晰,我就移动当前最低的那个
理由很简单:如果想要在当前的情况下面积更大,那就只有破局当前最低的那个,只有当前最低的那个得到提升,我才能更大。如果未来这个最低的更低更低,我就算移动高的那个,也不可能比原来还大了,所以干脆就移动最低的那个才有破局希望
剪枝操作:当最大值>=最高峰*当前宽度,说明永远也不会有更大的情况了,直接break就行
python
class Solution(object):
def maxArea(self, height):
"""
:type height: List[int]
:rtype: int
"""
# 双指针,从两边往中间走
# 实时记录最大的面积
# 移动后最低位尽可能高 ❌
# 这是一个陷阱 因为考虑了未来 但是未来是有很多选择的 无法一口气全部录入
# 所以我们只考虑现在 将现在最小的那个给移走就行 只有现在最小的有变化,后面才有可能有变化
l=0
r=len(height)-1
max_area=min(height[l],height[r])*(r-l)
maxheight=max(height)
while l<r:
if height[l]<height[r]:
l+=1
else:
r-=1
if l<r:
cur_area=min(height[l],height[r])*(r-l)
max_area=max(max_area,cur_area)
else:
break
if max_area>=maxheight*(r-l):
break
return max_area
2.3 三数之和 🌟
这个双指针很有代表性,可以作为统一模板使用的捏:
python
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
# 就是找 nums[i]+nums[j]=-nums[k]
# 三重循环?会重复 -- 排序后+双指针
nums=sorted(nums)
length=len(nums)
ans=[]
for i in range(length-2):
if i > 0 and nums[i]==nums[i-1]:
continue # 跳出已经考虑过的值
l=i+1
r=length-1
target=-nums[i]
while l<r:
s=nums[l]+nums[r]
if s==target:
ans.append([nums[i],nums[l],nums[r]])
l+=1
r-=1
# 跳过重复元素
while l<r and nums[l]==nums[l-1]:
l+=1
while l<r and nums[r]==nums[r+1]:
r-=1
elif s<target: # 因为是有序的 所以这里可以进行策略性的
l+=1
else:
r-=1
return ans
二刷还错
错误代码:
python
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
# 思路:不重复->先排序+跳过重复项,三元组->-a=b+c
nums=sorted(nums)
length=len(nums)
ans=[]
for i in range(length-2):
while i>0 and nums[i]==nums[i-1]:
continue
target=-nums[i]
l=i+1
r=length-1
while l<r:
while l<r and nums[l]==nums[l+1]:
l+=1
while l<r and nums[r]==nums[r-1]:
r-=1
s=nums[l]+nums[r]
if s<target: # 要增加,只有l移动才行
l+=1
elif s>target:
r-=1
else:
ans.append([nums[i],nums[l],nums[r]])
l+=1
r-=1
return ans
错误原因:
- 超时 :
while i>0 and nums[i]==nums[i-1]: continue导致无限循环,应该将 while 改为 if - 逻辑问题 :匹配成功之后 才需要消重,匹配前直接提前跳过会漏掉大量配对;比如 0,0,0,0,0,要是没匹配前消重,直接别玩了
2.4 接雨水
暴力按层计算,会超时
python
class Solution(object):
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
# 从下往上走,每次整个数组-1,看两个非0数字之间0的数量,就是该层积水数量
hightest=0
sec_hightest=0
for h in height:
if h>hightest:
sec_hightest=hightest
hightest=h
length=len(height)
total=0
for water_h in range(sec_hightest):
l=0
r=0
cur_cnt=0
# 找到第一个高于水平面的柱子
while height[l]<=water_h:
l+=1
# print("first h",l)
while r<length:
# 找到第一个水平面
while l<length and height[l]>water_h:
l+=1
# 走水
r=l+1
while r<length and height[r]<=water_h:
r+=1
if r<length:
cur_cnt+=r-l
l=r
else:
break
# print("cur_cnt",cur_cnt)
total+=cur_cnt
return total
正解1:按列计算 空间换时间
每一列的积水量=min(左侧最高柱子,右侧最高柱子)-当前列柱子高度
所以对于每一列,我们都应该知道他的左右最高柱子是什么,然后比较二者最小的。可以通过暴力 ,也可以提前扫一遍存下来
python
class Solution(object):
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
# 每一列的积水量=min(左侧最高柱子,右侧最高柱子)-当前列柱子高度
length=len(height)
leftmax=[0]*length
rightmax=[0]*length
colwater=[0]*length
leftmax[0]=height[0]
for i in range(1,length):
leftmax[i]=max(leftmax[i-1],height[i]) # 注意,需要将自己也列入其中,为了让后面的参考
rightmax[-1]=height[-1]
for i in range(length-2,-1,-1):
rightmax[i]=max(rightmax[i+1],height[i])
for i in range(length):
colwater[i]=min(leftmax[i],rightmax[i])-height[i]
return sum(colwater)
正解2:双指针
根据正解一可以发现,其实我们的 leftmax rightmax 数组只使用过一次,所以是可以优化掉的
还记得双指针最重要的是什么吗?如果想要真正发挥双指针的作用,而不是遍历,则是需要一种贪心 策略在里面的。比如这道题,其实贪心来,应该是每次移动对应侧矮的那个指针。因为如果移动对应侧高的那个,新的指针就得考虑新的双侧高度的情况了,反之,如果移动矮的那侧,则只需要直接根据矮的高度进行计算
python
class Solution(object):
def trap(self, height):
"""
:type height: List[int]
:rtype: int
"""
# 双指针:策略性移动最矮的那个
# 当前计算的是 左右指针所在位置的那个
# 移动的是对应侧矮的指针 比如 leftmax矮 那就移动 left 因为如果leftmax矮 说明积水量只会根据leftmax的变化而变化
n=len(height)
leftmax=height[0]
rightmax=height[n-1]
# 指向两边的指针
l=0
r=n-1
total=0
while l<r:
if leftmax<rightmax:
total+=leftmax-height[l]
l+=1
else:
total+=rightmax-height[r]
r-=1
leftmax=max(leftmax,height[l])
rightmax=max(rightmax,height[r])
return total