• 力扣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);
            }
        }
    }

     

  • 相关阅读:
    博客样式备份
    2018年终总结
    技术博客的太监
    LeetCode 日常填坑
    互联网之父
    TotoiseSVN的使用方法
    常用CMD命令
    量化策略
    浏览器加载js的阻塞与非阻塞
    Vue核心之数据劫持
  • 原文地址:https://www.cnblogs.com/zhaojiayu/p/14602141.html
Copyright © 2020-2023  润新知