259. 较小的三数之和

在这里插入图片描述
不会的题;
看到题目首先想到暴力枚举,但是想到要用三层for循环就猜肯定timeout。所以又想其他办法,然后想到先排序,然后用一个窗口找三数相加小于target的元素,但是发现还是写不出来,情况还是很复杂,所以看了答案。
解法一:
将问题拆解成一个数和两数之和小于target,这样构成外层for循环,内层for循环就只需要考虑两数相加之和小于target的情况。对于两数相加,可以确定一个数,遍历排好序的数组,找到第一个两数相加大于target的位置,这个位置之前的到固定数位置的内容均为可行解。所以现在的目标是找到这个坐标的位置,然后求出所有的在第一个数确定的情况下两数相加小于target的数量,而后遍历。

class Solution {
    
    
    public int threeSumSmaller(int[] nums, int target) {
    
    
        Arrays.sort(nums);
        int res = 0;
        for (int i = 0; i < nums.length - 2; i++) {
    
    
        //之所以要从i+1开始是因为防止重复的(如【1,2,3,4,5,6】,target=6,其中【1+3】小于6满足,【1+4】小于6满足,但是如果没有限制开始遍历的位置,在第一个数为3的时候,又会得到【3+1】<6...这样就不行,而设定了初始遍历位置,每一次遍历就能获得第一个数是1,2...的所有情况,且不重复计算)
            res += twoSumSmaller(nums, i + 1, target - nums[i]);
        }
        return res;
    }

    private int twoSumSmaller(int[] nums, int startIndex, int target) {
    
    
        int sum = 0;
        for (int i = startIndex; i < nums.length; i++) {
    
    
        //通过二分查找找到target-num【i】的位置,这个位置减去当前的位置就是当前数可以有多少解
            sum += binarySearch(nums, i, target - nums[i]);
        }
        return sum;
    }

    public int binarySearch(int[] nums, int start, int target) {
    
    
        int left = start;
        int right = nums.length - 1;
        while (left < right) {
    
    
            int mid = (right + left + 1) / 2;
            if (nums[mid] < target) {
    
    
                left = mid;
            } else {
    
    
                right = mid - 1 ;
            }
        }
        return left - start;
    }
}

对于二分查找还不熟悉,尤其是为什么 int mid = (right + left + 1) / 2;这些细节问题


第二种解法

class Solution {
    
    
    public int threeSumSmaller(int[] nums, int target) {
    
    
        Arrays.sort(nums);
        int res = 0;
        for (int i = 0; i < nums.length - 2; i++) {
    
    
            res += twoSumSmaller(nums, i + 1, target - nums[i]);
        }
        return res;
    }

    private int twoSumSmaller(int[] nums, int startIndex, int target) {
    
    
        int sum = 0;
        int left = startIndex;
        int right = nums.length - 1;
        while (left < right) {
    
    
            if (nums[left] + nums[right] < target) {
    
    
                sum += right - left;
                left++;
            }
            if (nums[left] + nums[right] >= target) {
    
    
                right--;
            }
        }
        return sum;
    }
}

改变的就是将二分查找改成了双指针法,对于两数之和小于或者等于某一个数可以采用这种方法,从两头开始,向中间靠拢,个数等于right-left。
这个题还是对于二分查找法要再熟悉一下,以及双指针法,还有问题拆解的思想

猜你喜欢

转载自blog.csdn.net/m0_60388871/article/details/128741961
今日推荐