从3sum开始-双指针

双指针,有几种适用情况

1.具有单调性的数组,例如找三数之和

2.结合其他思想,例如贪心(11. 盛最多水的容器),动态规划(接雨水),找规律(下一个排列)

3.字符串翻转题,双指针(151. 颠倒字符串中的单词)

4.链表快慢指针

  1. 19. 删除链表的倒数第 N 个结点
  2. 234. 回文链表
  3. 160. 相交链表
  4. 141. 环形链表
  5. 143. 重排链表
  6. 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值