1. 两数之和

该题要求返回下标,且至多有一对数和为target,不存在重复问题

  1. public int[] twoSum(int[] nums, int target) {
  2. Map<Integer, Integer> hm = new HashMap<>();
  3. for(int i = 0; i < nums.length; ++i){
  4. if (hm.containsKey(target - nums[i])){
  5. return new int[]{i, hm.get(target - nums[i])};
  6. }else{
  7. hm.put(nums[i], i);
  8. }
  9. }
  10. return new int[0];
  11. }

167. 两数之和 II - 输入有序数组

该题要求必须使用常量级的额外空间,且是非递减数列,那说明,排好序了,直接双指针完事了

15. 三数之和

该题要求,不能包含重复的三元组,当用两层循环计算得到a+b的值,在看哈希表里是否有0-a-b,但是这样会包含重复三元组,去重会非常的麻烦
思考:两数之和,可不可以使用双指针呢?由于两数之和问题返回的是下标,而双指针解法一定要排序,排序了下标就变了,如果要返回数值,则可以使用

  1. public List<List<Integer>> threeSum(int[] nums) {
  2. List<List<Integer>> ans = new ArrayList<>();
  3. Arrays.sort(nums);
  4. //找出a + b + c = 0
  5. //a = nums[i], b = nums[left], c = nums[right]
  6. for (int i = 0; i < nums.length; ++i){
  7. //必须有正有负才能使结果为0
  8. if ( nums[i] > 0)
  9. return ans;
  10. //去重
  11. //如果没有i>0这个条件,即nums[i] == nums[i + 1], 会产生什么影响? [-1, -1, 2]
  12. //因此应该判断是否与之前一样,因为之前的结果已经算出来了
  13. if ( i > 0 && nums[i] == nums[i-1])
  14. continue;
  15. int left = i + 1;
  16. int right = nums.length - 1;
  17. while(left < right){
  18. // 去重复逻辑如果放在这里,0,0,0 的情况,可能直接导致 right<=left 了,从而漏掉了 0,0,0 这种三元组
  19. //while (right > left && nums[right] == nums[right - 1]) right--;
  20. //while (right > left && nums[left] == nums[left + 1]) left++;
  21. if ( nums[i] + nums[left] + nums[right] > 0){
  22. right --;
  23. }else if ( nums[i] + nums[left] + nums[right] < 0){
  24. left ++;
  25. }else{
  26. ans.add(Arrays.asList(nums[i], nums[left], nums[right]));
  27. //再次去重,左右分别去重
  28. while (right > left && nums[right] == nums[right - 1])
  29. right--;
  30. while (right > left && nums[left] == nums[left + 1])
  31. left++;
  32. right --;
  33. left ++;
  34. }
  35. }
  36. }
  37. return ans;
  38. }

16. 最接近的三数之和

个人思路

同三数之和,只是不用去重,其实仍可以去重,降低时间消耗,但是这里去重的细节跟三数之和稍微不同

  1. class Solution {
  2. public int threeSumClosest(int[] nums, int target) {
  3. int ans = nums[0] + nums[1] + nums[2];
  4. Arrays.sort(nums);
  5. for(int i = 0; i < nums.length; ++i){
  6. //去重
  7. if( i > 0 && nums[i] == nums[i-1])
  8. continue;
  9. int left = i+1;
  10. int right = nums.length - 1;
  11. int temp = 0;
  12. while(left < right){
  13. temp = Math.abs(target - nums[i] - nums[left] - nums[right]);
  14. ans = temp < Math.abs(ans - target) ? nums[i]+nums[left]+nums[right] : ans;
  15. if(nums[i]+nums[left]+nums[right] > target){
  16. //right --; 注意这里的去重操作,应该夹在哪里,但是无论如何,都应该有两个right--,left省略了
  17. while( right > left && nums[right] == nums[right - 1])
  18. right--;
  19. right --;
  20. }else if(nums[i]+nums[left]+nums[right] < target){
  21. left++;
  22. }else{
  23. return target;
  24. }
  25. }
  26. }
  27. return ans;
  28. }
  29. }

18. 四数之和

解法同三数之和,不过是在外面再加一层循环

public List<List<Integer>> fourSum(int[] nums, int target) {
        List<List<Integer>> ans = new ArrayList<>();
        if ( nums==null || nums.length < 4)
            return ans;
        Arrays.sort(nums);

        for (int i = 0; i < nums.length; ++i){
            if ( i > 0 && nums[i] == nums[i-1])
                continue;
            for (int j = i + 1; j < nums.length; ++j){
                if (j > i + 1 && nums[j] == nums[j-1])
                    continue;
                int left = j+1;
                int right = nums.length-1;
                while (left<right){
                    int sum = nums[i] + nums[j] + nums[left] + nums[right];
                    if (sum > target){
                        right--;
                    }else if(sum < target){
                        left++;
                    }else{
                        ans.add(Arrays.asList(nums[i] , nums[j] , nums[left] , nums[right]));
                        while (left < right && nums[right]==nums[right-1]) right--;
                        while (left < right && nums[left] == nums[left+1]) left++;
                        right--;
                        left++;
                    }
                }
            }
        }
        return ans;
    }

454. 四数相加 II

    public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
        //暴力方法是四层遍历,时间复杂度为n^4
        //如果先遍历第一个数组,在便利另外三个是n^3
        //所以最低时间复杂度就是两两组合吗?
        Map<Integer, Integer> map = new HashMap<>();
        int temp = 0;
        int res = 0;
        for (int i : nums1){
            for (int j : nums2){
                temp = i + j;
                map.put(temp, map.getOrDefault(temp, 0)+1);
            }
        }
        for(int i : nums3){
            for(int j : nums4){
                temp = i + j;
                if (map.containsKey(-temp)){
                    //res++; //不是++,两个元素之和可能有重复
                    res += map.get(-temp);
                }
            }
        }
        return res;
    }