About 4 min


1.两数之和

用hash做。

我们要获取,num[i]和它的对应元素target-nums[i] 的下标。

那就遍历每个元素,hash存元素 key 和它的下标 value。

如果发现 target-nums[i]存在,那么就不用存当前元素了,直接return 输出。

class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer, Integer> map = new HashMap<>();
        for(int i = 0; i<nums.length; i++){
            int another = target - nums[i];
            Integer v = map.get(another);
            if(v == null){
                map.put(nums[i], i);
            }
            else{
                return new int[]{v , i};
            }
        }
        return null;
    }
}

167.两数之和 II

有序数组、只需返回一个解

alt text

头尾指针。

sum > target , j--

sum < target, i++

sum == target,返回ij

i == j , return null

class Solution {
    public int[] twoSum(int[] numbers, int target) {
        for (int i = 0, j = numbers.length - 1; i != j;) {
            int sum = numbers[i] + numbers[j];
            if (sum == target) {
                return new int[] { i + 1, j + 1 };
            } else if (sum > target) {
                j--;
            } else {
                i++;
            }
        }
        return null;
    }
}

n数和

15.三数之和

只用 dfs 超时:因为不能一开始根据k和j已就排除整个子树。

// 40的解法套过来
class Solution {
    // 保存每条从根到叶子的链路
    LinkedList<Integer> stack = new LinkedList<>();
    // 所有的链路
    List<List<Integer>> result = new ArrayList<>();
    int[] nums;
    boolean[] vis;
    int target;

    public List<List<Integer>> threeSum(int[] nums) {
        this.nums = nums;
        this.target = 0;
        vis = new boolean[nums.length];
        Arrays.sort(this.nums);
        dfs();
        return result;
    }

    public void dfs() {
        if(stack.size() == 3){
            if (target == 0) {
                // 注意添加子列表时,直接引用对象会被修改,要clone新对象。
                result.add(new ArrayList<>(stack));
            }
            return;
        }

        for (int i = 0; i < nums.length; i++) {
            // 已遍历
            if (vis[i])
                continue;
            // nums逆序重复
            if (i > 0 && nums[i] == nums[i - 1] && !vis[i - 1])
                continue;
            // 剪枝大小逆序而重复的:2-2-3,3-2-2,2-3-2中的3-2-2,2-3-2
            if (!stack.isEmpty() && nums[i] < stack.peek())
                continue;
            // 剪枝剩余target超了的
            if (target - nums[i] < 0)
                break;
            
            stack.push(nums[i]);
            target -= nums[i];
            vis[i] = true;
            dfs();
            vis[i] = false;
            target += nums[i];
            stack.pop();
        }
    }
}

通用解法:dfs + 两数和,36ms

n表示n数和,dfs里的i表示固定的数后面的元素。

class Solution {
    int[] nums;
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> stack = new LinkedList<>();

    public List<List<Integer>> threeSum(int[] nums) {
        this.nums = nums;
        Arrays.sort(this.nums);
        dfs(3, 0, nums.length - 1, 0);
        return result;
    }

    public void dfs(int n, int i, int j, int target) {
        if (n == 2) {
            twoSum(i, j, target);
            return;
        }

        // n - 2:2表示余2个数进行两数组合,n-2表示n位数去掉2个数来进行遍历的范围
        for (int k = i; k < j - (n - 2); k++) {
            // 重复解
            if (k > i && nums[k] == nums[k - 1]) {
                continue;
            }
            // 都是大于target,就无解
            if (nums[k] > target) {
                break;
            }
            stack.push(nums[k]);
            dfs(n - 1, k + 1, j, target - nums[k]);
            stack.pop();
        }
    }

    public void twoSum(int i, int j, int sumTarget) {
        while (i < j) {
            int sum = nums[i] + nums[j];
            if (sum < sumTarget) {
                i++;
            } else if (sum == sumTarget) {
                List<Integer> t = new ArrayList<>(stack);
                t.add(nums[i]);
                t.add(nums[j]);
                result.add(t);
                i++;
                j--;
                // 重复解
                while (i < j && nums[i] == nums[i - 1]) {
                    i++;
                }
                while (i < j && nums[j] == nums[j + 1]) {       // j+1
                    j--;
                }
            } else {
                j--;
            }

        }
    }
}

道理一样,化递归为循环的写法:27ms

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);
        List<List<Integer>> res = new ArrayList<>();
        int target = 0;
        for (int i = 0; i < nums.length - 2; i++) {
            // 重复解
            if (i > 0 && nums[i] == nums[i - 1]) {
                continue;
            }
            // 都是大于target,就无解
            if (nums[i] > target) {
                break;
            }
            int target_sum = target - nums[i];
            // 两外两个数,j是正着走,k是倒着走
            int j = i + 1;
            int k = nums.length - 1;
            while(j < k){
                int sum = nums[j] + nums[k];
                if (sum < target_sum) {
                    j++;
                }
                else if (sum == target_sum) {
                    List<Integer> tuple = new ArrayList<>();
                    tuple.add(nums[i]);
                    tuple.add(nums[j]);
                    tuple.add(nums[k]);
                    res.add(tuple);
                    j++;
                    k--;
                    // 重复解
                    while (j < k && nums[j] == nums[j - 1]) {
                        j++;
                    }
                    while (j < k && nums[k] == nums[k + 1]) {       // k+1
                        k--;
                    }
                }else{
                    k--;    
                }
            }
        }
        return res;
    }
}

18.四数之和

把上面的三数通用dfs+两数组合,搬运下来。

class Solution {
    int[] nums;
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> stack = new LinkedList<>();

    public List<List<Integer>> fourSum(int[] nums, int target) {
        this.nums = nums;
        Arrays.sort(this.nums);
        dfs(4, 0, nums.length - 1, target);
        return result;
    }

    public void dfs(int n, int i, int j, long target) {
        if (n == 2) {
            twoSum(i, j, target);
            return;
        }

        // n - 2:2表示余2个数进行两数组合,n-2表示n位数去掉2个数来进行遍历的范围
        for (int k = i; k < j - (n - 2); k++) {
            // 重复解
            if (k > i && nums[k] == nums[k - 1]) {
                continue;
            }
            stack.push(nums[k]);
            dfs(n - 1, k + 1, j, target - nums[k]);
            stack.pop();
        }
    }

    public void twoSum(int i, int j, long sumTarget) {
        while (i < j) {
            long sum = nums[i] + nums[j];
            if (sum < sumTarget) {
                i++;
            } else if (sum == sumTarget) {
                List<Integer> t = new ArrayList<>(stack);
                t.add(nums[i]);
                t.add(nums[j]);
                result.add(t);
                i++;
                j--;
                // 重复解
                while (i < j && nums[i] == nums[i - 1]) {
                    i++;
                }
                while (i < j && nums[j] == nums[j + 1]) {       // j+1
                    j--;
                }
            } else {
                j--;
            }

        }
    }
}