[leetcode]组合和排列题目汇总

151 阅读2分钟

组合

组合类的解空间树一般都有二叉树和多叉树两种。

组合

leetcode-cn.com/problems/co…

写法一: 考虑每个数选或不选,解空间树为二叉树。 解空间树如下。

class Solution {
    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(res, new ArrayList<>(), n, k, 1);
        return res;
    }

    public void dfs(List<List<Integer>> res, List<Integer> list, int n, int k, int t) {
    	// 递归结束的条件
        // t等于n+1时,n选或不选方可确定
        if (t == n + 1) {
            if (list.size() == k) {
                res.add(new ArrayList<>(list));
            }
            return;
        }
        // 选择t
        list.add(t);
        dfs(res, list, n, k, t + 1);
        // 不选择t
        list.remove(list.size() - 1);
        dfs(res, list, n, k, t + 1);
    }
}

写法二: 考虑 解空间树如下。

public class Solution {

    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(n, k, 1, new ArrayList<>(), res);
        return res;
    }

    private void dfs(int n, int k, int begin, List<Integer> path, List<List<Integer>> res) {
        // 递归终止条件是:path 的长度等于 k
        if (path.size() == k) {
            res.add(new ArrayList<>(path));
            return;
        }

        // 第begin轮, 从[begin..n]中依次只选择一个
        for (int i = begin; i <= n; i++) {
            // 选择i进入下一轮
            path.add(i);
            dfs(n, k, i + 1, path, res);
            // 恢复原状
            path.remove(path.size() - 1);
        }
    }
}

组合总和

leetcode-cn.com/problems/co… 写法一: 解空间树如下。

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(candidates, target, 0, new ArrayList<>(), res);
        return res;
    }

    private void dfs(int[] arr, int target, int t, List<Integer> list, List<List<Integer>> result) {
        if (t == arr.length) {
            return;
        }
        if (target == 0) {
            result.add(new ArrayList(list));
            return;
        }
        // 不选择t
        dfs(arr, target, t + 1, list, result);
        // 满足条件才选择t
        if (arr[t] <= target) {
            list.add(arr[t]);
            dfs(arr, target - arr[t], t, list, result);
            list.remove(list.size() - 1);
        }
    }
}

写法二: 解空间树如下。

class Solution {
    public List<List<Integer>> combinationSum(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(candidates, target, 0, new ArrayList<>(), res);
        return res;
    }

    private void dfs(int[] arr, int target, int t, List<Integer> list, List<List<Integer>> result) {
        if (target <= 0) {
            if (target == 0) {
                result.add(new ArrayList(list));
            }
            return;
        }
        //
        for (int i = t; i < arr.length; i++) {
            list.add(arr[i]);
            // 递归继续从i开始,因为元素可以重复选择
            dfs(arr, target - arr[i], i, list, result);
            list.remove(list.size() - 1);
        }
    }
}

组合总和II

leetcode-cn.com/problems/co… 写法一:

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        dfs(candidates, target, res, new ArrayList<>(), 0);
        return res;
    }

    private void dfs(int[] candidates, int target, List<List<Integer>> res, List<Integer> list, int t) {
        if (target == 0) {
            res.add(new ArrayList<>(list));
            return;
        }
        if (target < 0 || t > candidates.length - 1) {
            return;
        }
        
        // 左子树:选择t
        list.add(candidates[t]);
        dfs(candidates, target - candidates[t], res, list, t + 1);
        // 右子树:不选择t
        list.remove(list.size() - 1);
        t++;
        // 防止 [选择x,不选择x]和[不选择x,选择x]两种重复
        while (t < candidates.length && candidates[t] == candidates[t - 1]) {
            t++;
        }
        dfs(candidates, target, res, list, t);
    }
}

写法二:

class Solution {
    public List<List<Integer>> combinationSum2(int[] candidates, int target) {
        List<List<Integer>> res = new ArrayList<>();
        Arrays.sort(candidates);
        dfs(candidates, target, res, new ArrayList<>(), 0);
        return res;
    }

    private void dfs(int[] candidates, int target, List<List<Integer>> res, List<Integer> list, int t) {
        if (target == 0) {
            res.add(new ArrayList<>(list));
            return;
        }
        if (target < 0 || t > candidates.length - 1) {
            return;
        }
        
        for (int i = t; i < candidates.length; i++) {
        	// 不同分支间不允许取相同元素,否则会出现[1,7]和[1*,7]这种重复
            // i == t时,是一个分支的上下节点间,允许[1,1*,7]这种
            if (i > t && candidates[i] == candidates[i - 1]) {
                continue;
            }
            list.add(candidates[i]);
            dfs(candidates, target - candidates[i], res, list, i + 1);
            list.remove(list.size() - 1);
        }
    }
}

组合总和III

leetcode-cn.com/problems/co… 写法一:

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(n, k, 1, res, new ArrayList<>());
        return res;
    }

    private void dfs(int n, int k, int t, List<List<Integer>> res, List<Integer> list) {
        if (n < 0 || list.size() > k || t > 10) {
            return;
        }
        if (n == 0 && list.size() == k) {
            res.add(new ArrayList<>(list));
            return;
        }
        list.add(t);
        dfs(n - t, k, t + 1, res, list);
        list.remove(list.size() - 1);
        dfs(n, k, t + 1, res, list);
    }
}

写法二:

class Solution {
    public List<List<Integer>> combinationSum3(int k, int n) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(n, k, 1, res, new ArrayList<>());
        return res;
    }

    private void dfs(int n, int k, int t, List<List<Integer>> res, List<Integer> list) {
        if (n < 0 || list.size() > k || t > 10) {
            return;
        }
        if (n == 0 && list.size() == k) {
            res.add(new ArrayList<>(list));
            return;
        }
        for (int i = t; i < 10; i++) {
            list.add(i);
            dfs(n - i, k, i + 1, res, list);
            list.remove(list.size() - 1);
        }
    }
}

组合总和IV

leetcode-cn.com/problems/co… 这道题回溯翻车了,要用动态规划。

排列

全排列

leetcode-cn.com/problems/pe…

class Solution {
    private List<List<Integer>> res;
    public List<List<Integer>> permute(int[] nums) {
        res = new ArrayList<>();
        permute(nums, 0);
        return res;
    }

    private void permute(int[] nums, int t) {
        if (t == nums.length) {
            List<Integer> list = new ArrayList<>();
            for (int i = 0; i < nums.length; i++) {
                list.add(nums[i]);
            }
            res.add(list);
            return;
        }
        for (int i = t; i < nums.length; i++) {
            swap(nums, i, t);
            permute(nums, t + 1);
            swap(nums, i, t);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int tmp = nums[i];
        nums[i] = nums[j];
        nums[j] = tmp;
    }
}

全排列II

leetcode-cn.com/problems/pe…

class Solution {
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<List<Integer>> res = new ArrayList<>();
        dfs(nums, 0, res);
        return res;
    }

    private void dfs(int[] nums, int t, List<List<Integer>> res) {
        if (t == nums.length) {
            List<Integer> list = new ArrayList<>();
            for (int i : nums) {
                list.add(i);
            }
            res.add(new ArrayList<>(list));
            return;
        }
        // 每个元素轮流换到t位置上,包括t上本身的元素
        for (int i = t; i < nums.length; i++) {
            // 如果前面某个元素去过一次t位置,后面相等的元素就不再去t上了
            // 例如1,1*,2, 1与1“交换”过,1*不再与1交换
            boolean b = false;
            for (int j = t; j < i; j++) {
                if (nums[j] == nums[i]) {
                    b = true;
                }
            }
            if (b) {
                continue;
            }
            swap(nums, i, t);
            dfs(nums, t + 1, res);
            swap(nums, i, t);
        }
    }

    private void swap(int[] nums, int i, int j) {
        int t = nums[i];
        nums[i] = nums[j];
        nums[j] = t;
    }
}