力扣78题、77题、90题、491题(回溯算法、子集组合)

78、子集

基本思想:

回溯算法

具体实现:

这道题没有if判断递归是否结束

因为这个是每递归一下就加入到结果数组中,

代码:

python

class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        track = []
        result = []
        self.backtrack(nums,track,result,0)
        return result
    def backtrack(self,nums,track,result,start):
        result.append(track[:])
        for i in range(start,len(nums)):
            track.append(nums[i])
            self.backtrack(nums,track,result,i+1)
            track.pop()
class Solution:
    def subsets(self, nums: List[int]) -> List[List[int]]:
        res = []
        n = len(nums)
        
        def backtrack(i, tmp):
            #注意这里一定要用列表的引用,否者后面更新temp会改变res中元素的值
            res.append(tmp[:])
            for j in range(i, n):
                tmp.append(nums[j])
                backtrack(j + 1,tmp)
                tmp.pop()
        backtrack(0, [])
        return res  

java

class Solution {
    List<List<Integer>> result = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> subsets(int[] nums) {
        if (nums.length == 0){
            result.add(new ArrayList<>());
            return result;
        }
        subsetsHelper(nums, 0);
        return result;
    }
    private void subsetsHelper(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));
        for (int i = startIndex; i < nums.length; i++){
            path.add(nums[i]);
            subsetsHelper(nums,i+1);
            path.removeLast();
        }
    }
}

77、组合

基本思想:

回溯算法

具体实现:

1、组合是不能有重复的

递归函数的参数每次就有起始数,

for循环从start开始递增

上述两个操作保证了不会有重复

2、递归结束条件是到达叶子节点

代码:

class Solution:
   
    def combine(self, n: int, k: int) -> List[List[int]]:
        track = []
        result = []
        self.backtrack(n,k,1,track,result)
        return result
    def backtrack(self,n,k,start,track,result):
        if k == len(track):
            result.append(track[:]) #必须是track[:] track的话会报错
            return #没有这个return也能执行出来,不知道为什么
        for i in range(start,n+1):
            track.append(i)
            self.backtrack(n,k,i+1,track,result)
            track.pop()

剪枝:

 具体实现:

k-len(track)是track中还差的元素个数

n-(k-len(track))+1是要达到组合中元素为k,至多开始的位置

比如:n=4,k=3 ,track里面啥也没    4-3+1=2

          意思是最多从2开始,组合可以是2,3,4

          再循环到3不可能满足组合个数

python写到代码里n-(k-len(track))+2 因为range的第二个参数代表第一个不取的数

代码:

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        track = []
        result = []
        self.backtrack(n,k,1,track,result)
        return result
    def backtrack(self,n,k,start,track,result):
        if k == len(track):
            result.append(track[:]) 
            return
        for i in range(start,n-(k-len(track))+2):
            track.append(i)
            self.backtrack(n,k,i+1,track,result)
            track.pop()

 

class Solution {
    List<List<Integer>> result  = new ArrayList<>();
    LinkedList<Integer> path = new LinkedList<>();
    public List<List<Integer>> combine(int n, int k) {
        combineHelper(n, k, 1);
        return result;
    }
    private void combineHelper(int n, int k, int startIndex){
        if (path.size() == k){
            result.add(new ArrayList<>(path));
            return;
        }
        for (int i = startIndex; i <= n - (k - path.size()) + 1; i++){
            path.add(i);
            combineHelper(n, k, i + 1);
            path.removeLast();
        }
    }
}

 90、子集II

基本思想:

回溯算法

与78题的区别是,子集需要去重

具体实现:

代码:

class Solution {
    List<List<Integer>> result = new ArrayList<>();// 存放符合条件结果的集合
    LinkedList<Integer> path = new LinkedList<>();// 用来存放符合条件结果   
    public List<List<Integer>> subsetsWithDup(int[] nums) {
        if (nums.length == 0){
            result.add(path);
            return result;
        }
        Arrays.sort(nums);//去重需要排序
        subsetsWithDupHelper(nums,0);
        return result;
    }
    private void subsetsWithDupHelper(int[] nums, int startIndex){
        result.add(new ArrayList<>(path));
        if (startIndex >= nums.length){
            return;
        }
        for (int i = startIndex; i < nums.length; i++){
            if (i > startIndex && nums[i] == nums[i - 1]){
                //我们要对同一树层使用过的元素进行跳过
                continue;
            }
            path.add(nums[i]);
            subsetsWithDupHelper(nums, i + 1);
            path.removeLast();
        }
    }
}

 491、递增子序列

基本思想:

回溯算法

与90题不同,90题需要排序,这道题不需要

90题判断是否重复是用nums[i] == nums[i-1],这道题不行

具体实现:

代码:

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

    private void backtracking (int[] nums, int start) {
        if (path.size() > 1) {
            res.add(new ArrayList<>(path));//注意这里不要加return,要取树上的节点
        }
    //记录[-100,100]这些数字出现的个数
    //[-100,100]对应索引[0,201]
        int[] used = new int[201];
        for (int i = start; i < nums.length; i++) {
            if (used[nums[i] + 100] == 1) continue;
            if (!path.isEmpty() && nums[i] < path.get(path.size() - 1)) continue;
            used[nums[i] + 100] = 1;//记录这个元素在本层用过了,本层后面不能再用了
            path.add(nums[i]);
            backtracking(nums, i + 1);
            path.remove(path.size() - 1);
        }
    }
}

 

原文地址:https://www.cnblogs.com/zhaojiayu/p/14602141.html