双指针,有几种适用情况
1.具有单调性的数组,例如找三数之和
2.结合其他思想,例如贪心(11. 盛最多水的容器),动态规划(接雨水),找规律(下一个排列)
3.字符串翻转题,双指针(151. 颠倒字符串中的单词)
4.链表快慢指针
- 19. 删除链表的倒数第 N 个结点
- 234. 回文链表
- 160. 相交链表
- 141. 环形链表
- 143. 重排链表
- 148.排序链表
5.荷兰国旗问题,本质上是快排的衍生,思路是选一个基准index,小于nums[index]的移到左边,大于nums[index]的移到右边。一个left指针交换0,一个right指针交换2,nums[index]等于0,就换到left上,等于2就换到right上。
16.最接近的三数之和
排序+双指针
双指针法,循环中更新diff
class Solution(object):
def __init__(self):
self.ans=0
def threeSumClosest(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: int
"""
diff=float("inf")
nums.sort()
#[-4,-1,1,2]
for i in range(len(nums)):
left=i+1
right=len(nums)-1
while left<right:
tmp=nums[i]+nums[left]+nums[right]
cur_diff=abs(target-tmp)
if cur_diff<diff:
diff=cur_diff
self.ans = tmp
if tmp>target:
right=right-1
elif tmp<target:
left=left+1
else:
return target
return self.ans
15 三数之和
两数之和的那种map解法
两数之和要返回下标,所以不能排序
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
#转化为两数之和,nums[i],-(nums[j]+nums[k]) in maps
def twosum(alist,target):
maps = {}
cur_res = []
for k in range(len(alist)):
if target - alist[k] in maps:
cur_res.append([target - alist[k],alist[k]])
maps[alist[k]] = k
return cur_res
res = []
nums.sort()
for i in range(len(nums)):
if i > 0 and nums[i] == nums[i-1]:
continue
cur_res = twosum(nums[i+1:],-nums[i])
if len(cur_res) > 0:
for res1 in cur_res:
if [nums[i],res1[0],res1[1]] not in res:
res.append([nums[i],res1[0],res1[1]])
return res
排序+双指针
为什么不能排序+二分法,用二分法代替双指针是不是复杂度更低?
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res=[]
nums.sort()
# [-4,-1,-1,0,1,2]
for i in range(len(nums)-1):
#跳过重复值
if i > 0 and nums[i] == nums[i - 1]:
continue
left=i+1
right=len(nums)-1
while left<right:
tmp=nums[i]+nums[left]+nums[right]
if tmp<0:
#跳过重复值
while left<right and nums[left]==nums[left+1]:
left=left+1
left=left+1
elif tmp>0:
#跳过重复值
while left<right and nums[right]==nums[right-1]:
right=right-1
right=right-1
else:
res.append([nums[i],nums[left],nums[right]])
#跳过重复值
while left<right and nums[left]==nums[left+1]:
left=left+1
while left<right and nums[right]==nums[right-1]:
right=right-1
left=left+1
right=right-1
return res
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
#难点在于如何去除重复解
nums.sort()
res = []
for i in range(len(nums)):
#去掉重复值
if i > 0 and nums[i] == nums[i-1]:
continue
left = i+1
right = len(nums) - 1
while left < right:
#去掉重复值
if left > i+1 and nums[left] == nums[left-1]:
left += 1
continue
#去掉重复值
if right < len(nums) - 1 and nums[right] == nums[right+1]:
right -= 1
continue
cur_value = nums[i] + nums[left] + nums[right]
if cur_value < 0:
left += 1
elif cur_value > 0:
right -= 1
else:
res.append([nums[i],nums[left],nums[right]])
left += 1
right -= 1
return res
二分查找
复杂度较高
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> ans = new ArrayList<List<Integer>>();
for(int first = 0;first < n;first++){
if(first > 0 && nums[first] == nums[first - 1]){
continue;
}
for(int second = first + 1;second < n;second++){
if(second > first + 1 && nums[second] == nums[second - 1]){
continue;
}
int left = second + 1;
int right = n - 1;
int target = -nums[first] - nums[second];
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] < target){
left = mid + 1;
}else if(nums[mid] > target){
right = mid - 1;
}else{
ans.add(Arrays.asList(nums[first],nums[second],nums[mid]));
break;
}
}
}
}
return ans;
}
}
作者:wudi__
链接:https://leetcode-cn.com/problems/3sum/solution/ti-jie-zhong-de-shuang-zhi-zhen-yong-er-uu2hw/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
18 四数之和
排序+双指针
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
res=[]
nums.sort()
for i in range(len(nums)):
for j in range(i+1,len(nums)):
left=j+1
right=len(nums)-1
while left<right:
tmp=nums[i]+nums[j]+nums[left]+nums[right]
if tmp==target:
if [nums[i],nums[j],nums[left],nums[right]] not in res:
res.append([nums[i],nums[j],nums[left],nums[right]])
left=left+1
right=right-1
elif tmp<target:
left=left+1
else:
right=right-1
return res
259 较小的三数之和
11. 盛最多水的容器
贪心+双指针
class Solution:
def maxArea(self, height: List[int]) -> int:
#为什么用双指针?贪心的思路
#如果当前height[left]<height[right],那么以left为高度的容器,最大的就是当前这个。因为当前这个宽度最大
res = 0
left = 0
right = len(height) - 1
while left < right:
cur = min(height[left],height[right]) * (right - left)
res = max(res,cur)
if height[left] < height[right]:
left += 1
else:
right -= 1
return res
42. 接雨水
动态规划
class Solution:
def trap(self, height: List[int]) -> int:
#左边最高的柱子,右边最高的柱子,取min
#用一个数组存左边最高的和右边最高的,当前往左最高的,不包括当前
max_left = [0 for _ in range(len(height))]
max_right = [0 for _ in range(len(height))]
for i in range(1,len(height)):
max_left[i] = max(height[i-1],max_left[i-1])
for i in range(len(height)-2,0,-1):
max_right[i] = max(height[i+1],max_right[i+1])
res = 0
for i in range(1,len(height)-1):
min_height = min(max_right[i],max_left[i])
if height[i] < min_height:
res += min_height - height[i]
return res
双指针:由动态规划演化
动态规划里
public int trap(int[] height) {
int sum = 0;
int max_left = 0;
int max_right = 0;
int left = 1;
int right = height.length - 2; // 加右指针进去
for (int i = 1; i < height.length - 1; i++) {
//从左到右更
if (height[left - 1] < height[right + 1]) {
max_left = Math.max(max_left, height[left - 1]);
int min = max_left;
if (min > height[left]) {
sum = sum + (min - height[left]);
}
left++;
//从右到左更
} else {
max_right = Math.max(max_right, height[right + 1]);
int min = max_right;
if (min > height[right]) {
sum = sum + (min - height[right]);
}
right--;
}
}
return sum;
}
作者:windliang
链接:https://leetcode-cn.com/problems/trapping-rain-water/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-w-8/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
31.下一个排列
双指针+找规律
class Solution(object):
def nextPermutation(self, nums):
"""
:type nums: List[int]
:rtype: None Do not return anything, modify nums in-place instead.
"""
#从后往前遍历,找到nums[i]<nums[i+1]的第一个元素
i=len(nums)-2
while i>=0 and nums[i]>=nums[i+1]:
i=i-1
#从后往前遍历,找到nums[j]>nums[i]的第一个元素
if i>=0:
j=len(nums)-1
while j>i and nums[i]>=nums[j]:
j=j-1
#交换
nums[i],nums[j]=nums[j],nums[i]
#从i+1到最后升序排列
left=i+1
right=len(nums)-1
while left<right:
nums[left],nums[right]=nums[right],nums[left]
left=left+1
right=right-1
return nums
19. 删除链表的倒数第 N 个结点
56 合并区间
76 颜色分类
快排衍生
这个题是快排的衍生,考察对快排中元素交换的理解!核心思想是找一个基准,小于这个基准的排在前面,大于这个基准的排在后面。如果这个题没做出来,说明快排理解的不透彻。以下是我的做法,一个左指针,一个右指针,如果nums[i]是0,就交换到左边,如果nums[i] 是2,就交换到右边。这个思路是对的,但是实现的时候细节有问题。
比如nums=[1,2,0],交换的时候,会把nums[i]等于2交换到right上,交换过来的等于0.这个时候还得继续交换,所以不能用for循环,得用while,当前位置等于1的时候才停止交换,把i前移
class Solution:
def sortColors(self, nums):
"""
Do not return anything, modify nums in-place instead.
"""
# 双指针,left排0,right排2
left = 0
right = len(nums) - 1
for i in range(len(nums)):
if i > right:
break
if nums[i] == 0:
nums[left], nums[i] = nums[i], nums[left]
left += 1
elif nums[i] == 2:
nums[right], nums[i] = nums[i], nums[right]
right -= 1
return nums
# leetcode submit region end(Prohibit modification and deletion)
# nums = [2,0,2,1,1,0]
nums = [1,2,0]
res = Solution().sortColors(nums)
print(res)
正确的做法
class Solution:
def sortColors(self, nums: List[int]) -> None:
"""
Do not return anything, modify nums in-place instead.
"""
# 双指针,left排0,right排2
left = 0
right = len(nums) - 1
index = 0
while index <= right:
if nums[index] == 0:
nums[left], nums[index] = nums[index], nums[left]
#把nums[index]和 nums[left] 做了交换,nums[left]已经判断过了(在左边,肯定判断过),所以index+1
left += 1
index += 1
elif nums[index] == 2:
nums[right], nums[index] = nums[index], nums[right]
#因为把当前nums[index]和 nums[right] 做了交换,但是nums[right]还没有被判断过,所以这里index不能 + 1
right -= 1
else:
index += 1
return nums