leetcode的2数,3数,4数之和,双指针

leetcode的2数,3数,4数之和

给你一个整数数组,一个目标值,在数组中,找到两个数 || 三个数 || 四个数 之和 为 目标值 的数字下标

一. 2数之和

在这里插入图片描述

 for 循环 2层 遍历 
1. 我们可以将所有的数字 放入map中,(--下标)
2. 然后遍历数组,算出对应差值,看map中是否存在 (这个数的下标不能是 当前 遍历的下标)
1. 在遍历的时候,放入map中
2. (一边遍历查找 ,一边放入)
// 每一次 ,找的是 当前位置 之前的数字 是否 存在 与 当前数字 匹配
// 存在 ,返回结果
// 不存在 ,put进map, 给之后查找
class Solution {
   public int[] twoSum(int[] nums, int target) {
        Map<Integer,Integer> map = new HashMap<>(); 
        for (int i = 0; i < nums.length; i++) {
            int cha = target - nums[i];
            if(map.containsKey(cha) ){
                return new int[]{i,map.get(cha)};
            }
            map.put(nums[i], i); 
        }
        return null;
    }
}
 这是一个 互补的过程
 假设 满足的 下标为 x,y (x < y )
 过程: 遍历到 x 时候,没有满足的 ,put进map
 	   遍历到 y 时候, map包含x ,返回结果

二. 3数之和

在这里插入图片描述

不可重复问题 :往往需要排序

1. 排序
2. 遍历集合 ,
3.  因为 是求 3 数之和 ,所以固定 一个下标,往后遍历找到
class Solution {
    //  3指针,两个夹逼
    public List<List<Integer>> threeSum(int[] nums) {
        //        1. 排序
                Arrays.sort(nums);
                List<List<Integer>> list = new ArrayList<>();
  		//		 i 就是标志指针 ,在它的后面找满足的 2 个数字 所以 i 的后面至少 有 2 个数字
                for (int i = 0; i < nums.length-2; i++) {
        //            大于0,结束,后面的和,一定大于0
                    if (nums[i] > 0) break;
        //            非第一个重复,下一个 ,
        //            【1,0, 0, 0】 对于连续重复 ,将第一个作为标志位,后面相同的 continue
                    if (i > 0 && nums[i] == nums[i-1]) continue;
        //            2指针 ,首尾  夹逼 ,不会跳过 可行解
                    int a = i+1, b = nums.length - 1;
                    while (a < b) {
                        int sum = nums[i] + nums[a] + nums[b];
                        if (sum < 0){  
        //                    低指针往后移 , 之和 变大,
                            while ( a < b && nums[a] == nums[++a]);
                        }else if(sum > 0) {
        //                    高指针往前移 ,之和 变小
                            while ( a < b && nums[b] == nums[--b]); 
                        }else {
                           list.add(new ArrayList<Integer>(Arrays.asList(nums[i],nums[a],nums[b])));
        //                    低,高指针往后,前移动,进行下一次while,即同时往 大,小变化,抵消
                            while ( a < b && nums[a] == nums[++a]);
                            while ( a < b && nums[b] == nums[--b]);
                        }
                    }
                }
                return list;
            }
}
    [-1 , 0 , 1 , 2 , -1 , -4]
模拟过程:

1. 排序: 		[-4-1 , -10 , 1 , 2 ]
2. 固定 -4  ,[-1 , -10 , 1 , 2 ]  范围中 双指针夹逼 找满足的下标
3. 固定 -1  ,[-10 , 1 , 2 ]       范围中 双指针夹逼 找满足的下标
4. ....

5. 在这个过程中,首先要 去重:
  对于 第一个指针的去重 :  if (i > 0 && nums[i] == nums[i-1]) continue;
		 例如情况:【1 , 1 , 1 , 2 , 2 , 2】 在将 下标为 01 作为 标志位指针 后,要直接移动到 下标为 32 位置,进行 下一次计算
		 固定 下标为 01 搜索的范围 : 【  , 1 , 1 , 2 , 2 , 2】
		 固定 下标为 11 搜索的范围 : 【  ,   , 1 , 2 , 2 , 2】
		 固定 下标为 21 搜索的范围 : 【  ,   ,   , 2 , 2 , 2】
		 很显然,后面的搜索范围 , 在第一次,已经搜索过了,得到的结果集: 必然与之前重复
		 
6. 对于双指针的去重及加速: 
	     对于 while ( a < b && nums[a] == nums[++a]);  类似代码的解释
	     当我们的 左右指针满足 或 不满足条件后 ,都需要移动指针
	     以 sum < 0 为例:
	     双指针的值应该增加,让左指针 往 左 移动,并且 移动后 的值不等于之前的值 ,并满足左右指针原则 :明了代码
	     
		    while (a < b){
		           a++;    //左移一位 
		           if (nums[a] != nums[a-1]){   // 当前位置 与 之前位置 的值是否 不等
		               break;
		           }
		    }
		    
7. 为什么这样做可以 完全搜索 满足的 下标?
		这与 https://leetcode-cn.com/problems/container-with-most-water/  11. 盛最多水的容器 的原理类似

为什么这样做可以 完全搜索 满足的 下标? 两数和的解

  1. 可以先将问题简化 ,将左右指针的范围的元素 首先去重(不是 将[1,1,1,2,2] 变为 [1,2] 去重)
  2. 这是 代码的一部分 功能 ,while代码的功能
  3. 对于if ,else的解释,按照暴力解法,双重for循环,
  4. 但是数组现在已经 有序(升序),以一种情况为例:[ a , b ], sum < 0, 左右指针和,应该变大,
  5. 直接将范围 变为 [ a+1 , b ], 我们在这一步里,直接将 暴力遍历 中的
【a,a+1,  【a,a+2, 【a,a+3,  ..... 【a,b-1

这些情况 剪枝 , 因为a+1,…,b-1 ,都 小于 b, 不满足解
在这里插入图片描述
为什么 可以 完全 搜索 3 数之和 的解 ,不会有遗漏

在排序后 , 所有 满足条件的三元组 下标都可以写  为 [x , y , z ]0 < x < y < z < len-1)
要证明的是 代码对其 没有任何遗漏

在这里插入图片描述

  1. 代码的 遍历顺序,从前到后
  2. X 肯定会作为 指针标志位 ,Y,Z 也一定会 作为 左右指针可行解
  3. 在结合 遍历出 左右指针 所有可行解,没有遗漏

三. 4数之和

在这里插入图片描述
与三数之和一样的思路,霸王硬上弓

class Solution {
    public List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> list = new ArrayList<>(); 
          // 倒数第四个
        for (int i = 0; i < nums.length-3; i++) {
        	// 与自己的起点 0 相比较
            if (i > 0 && nums[i] == nums[i-1]) continue;
            
            	// 倒数第 三 个
            for (int j = i+1; j < nums.length-2; j++) {
                  //  与自己的起点 i+1 相比较
                if (j > i+1 && nums[j] == nums[j-1]) continue;
                
                	// 后面双指针 要凑成 的数字 和
                int flag = target-nums[i]-nums[j];
                
                		// 左右指针,夹逼
                int a = j+1 , b = nums.length-1;
                while(a < b){
                    if(nums[a] + nums[b] > flag){
                        while(a < b && nums[b] == nums[--b]) ;
                    }else if(nums[a] + nums[b] < flag){
                        while(a < b && nums[a] == nums[++a]) ;
                    }else{
                        list.add(Arrays.asList(nums[i],nums[j],nums[a],nums[b]));
                        while(a < b && nums[b] == nums[--b]) ;
                        while(a < b && nums[a] == nums[++a]) ;
                    }
                }
            }
        }
        return list;
    }
}

原理与3数之和,一模一样

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值