【递归、搜索、回溯】专题三:全排列、子集、组合

C++搜索与回溯算法之全排列问题 全排列问题:设有n个整数的集合{1,2,…,n},从中任意取出r个数进行排列(r),试列出所有的排列。 可以用枚举,就像这样(当r=5时):   #include int n,i[5],r=5; bool mark(int x[],int y) { for(int j=0;j<y-1;j++) for(int k=j+1;k<y;k++) if(x[j]==x[k]) r 阅读详情

目录

一、全排列

二、子集

三、找出所有子集的异或总和再求和

四*、全排列II

五、电话号码的字母组合

六*、括号生成

七、组合

八、目标和

九、组合总和

十、组合总和II

十一、组合总和III

十二、组合总和IV

总结排列、子集、总和

前言

全排列问题细节:

无重复元素全排列

有重复元素全排列

子集问题细节:

求全部子集

找固定大小子集

组合总和问题细节:

无重复元素组合总和

有重复元素组合总和

有元素可以无限选择的组合总和


全排列问题不要被模板限制思路,画出决策树后,模拟思路。

每次分支选择之后继续dfs,然后代码紧接回溯,以保证能够回到原来的状态。

一、全排列

46. 全排列 - 力扣(LeetCode)

全局变量path记录路径,check用于记录使用过的数字 以便 将未使用数字插入路径,for循环保证全部遍历到,条件是小于numssize,然后for循环里写dfs递归,这样就能实现有多少数字就进行多少次for循环。

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    bool check[7];
public:
    vector<vector<int>> permute(vector<int>& nums) {
        dfs(nums);
        return ret;
    }
    void dfs(vector<int>& nums)
    {
        if(nums.size()==path.size())
        {
            ret.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();++i)
        {
            if(check[i]==false)
            {
                path.push_back(nums[i]);
                check[i]=true;
                dfs(nums);
                //回溯
                path.pop_back();
                check[i]=false;
            }
        }
    }
};

二、子集

78. 子集 - 力扣(LeetCode)

对于子集问题,因为子集不像全排列那样可以按照最终元素个数和原数组元素个数相等就返回。每个元素可以选或者不选,所以子集元素个数不是固定的,具体就是个数0~n都有。按照这个想法,我们可以按照以下两种解法来解决这个问题。

法一:判断选还是不选分层,子集的决策树的话每次分布就是判断要选还是不选,dfs函数设计时多传一个参数表示第几次选择.

注意选完之后记得回溯

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(nums,0);
        return ret;
    }
    void dfs(vector<int>& nums,int pos)
    {
        if(pos==nums.size())
        {
            ret.push_back(path);
            return;
        }

        //选
        path.push_back(nums[pos]);
        dfs(nums,pos+1);
        //回溯
        path.pop_back();
        
        //不选
        dfs(nums,pos+1);
    }
};

法二:根据元素个数分层

i,和pos实现按顺序往后迭代,按元素个数分层,每次dfs开头就push path->ret

实际的push顺序: 空 1 12 123 13 2 23 3

可以发现i和pos的使用实现了按顺序不重复地往后遍历

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        dfs(nums,0);
        return ret;
    }
    void dfs(vector<int>& nums,int pos)
    {
        ret.push_back(path);
        for(int i=pos;i<nums.size();++i)
        {
            path.push_back(nums[i]);
            //注意是i+1
            dfs(nums,i+1);
            path.pop_back();
        }
    }
};

三、找出所有子集的异或总和再求和

1863. 找出所有子集的异或总和再求和 - 力扣(LeetCode)

找出所有子集,在最终返回的时候计算异或和即可

法一:按照选或者不选

class Solution {
    int ret;
    vector<int> path;
public:
    int subsetXORSum(vector<int>& nums) {
        dfs(nums,0);
        return ret;
    }
    void dfs(vector<int>&nums,int pos)
    {
        int tmp=0;
        //完成子集筛选,计算结果
        if(pos==nums.size())
        {
            for(auto&x:path)tmp^=x;
            ret+=tmp;
            return;
        }
        //选
        path.push_back(nums[pos]);
        dfs(nums,pos+1);
        //回溯
        path.pop_back();
        //不选
        dfs(nums,pos+1);
    }
};

法二:分层,因为每一步都得到了子集,不同于法一在最终的时候才能得到子集,所以path设置成int类型就行。

class Solution {
    int path;
    int ret;
public:
    int subsetXORSum(vector<int>& nums) {
        dfs(nums,0);
        return ret;
    }
    void dfs(vector<int>& nums,int pos)
    {
        ret+=path;
        for(int i=pos;i<nums.size();++i)
        {
            path^=nums[i];
            dfs(nums,i+1);
            path^=nums[i];      //回溯
        }
    }
};

四*、全排列II

47. 全排列 II - 力扣(LeetCode)

画出决策树进行模拟很重要,去重->设置一个check数组判重?错误的,check用于检查该数是否使用,去重我们可以利用排序。

画出决策树后暴力模拟,发现同一节点的所有分支中,相同元素只能选择一次,但在下一个节点后这个元素仍然可以选择

具体思路如下:

当数组有相同元素时,之前的代码实现全排列会导致出现重复的子数组。

先将整个数组进行排序,模拟去除重复子数组的全排列,一共有nums.size()这么多个格子,从前往后填格子,在填同一个格子时,从数组元素里面选数,那么就有nums。size个分支,怎么选的就分成两类思考,一种是已经选过的不选,这个多设计一个bool数组就能实现;另一种就是前边分支的元素相同的(所以要提前排序),不用选,因为选了结果都一样会重复,筛选出分支(剪枝)又另外分两种思路:

1.只关心“不合法”的分支,

check[i]==true(结论2.)||

(

i!=0 (因为后边可能会越界,提前判断)

&&

nums[i]==nums[i-1] (同一分支重复,但不同格子时,也就是在填时间上 下一个格子的时候,元素相同是允许的,所以我们还得加一个判断条件)&&

check[i-1]==false(和前一个条件结合后达成真正的同一分支(填同一个格子)重复,如果check[i-1]是true代表i-1填的是之前的格子,是上一次分支的选择,和我们这次不相干)

)(结论1.)

由此,不合法分支判断结束

2.也可以只关心合法的分支

check[i]==false&&

(

i==0(i=0,还在填第一个格子,填啥都可以,不用判断越界)

||

nums[i]!=nums[i-1](和前面分支不相等,当然可以当作填格的一种可能)

||

check[i-1]==true(由于这里运算同级,于是代码从左往右运行,运行到这里已经默认了【i】==【i-1】,如果前面一个check为真,那么代表不同格的同一元素,这是允许的。

)

结合代码体会思路

法一:只关心不合法,不合法直接剪枝

class Solution {
    vector<int> path;
    vector<vector<int>> ret;
    bool check[9];
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        dfs(nums,0);
        return ret;
    }
    //pos表示填格位数
    void dfs(vector<int>& nums,int pos)
    {
        if(nums.size()==pos)
        {
            ret.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();++i)
        {
            if(check[i]==true||(i!=0&&nums[i]==nums[i-1]&&check[i-1]==false))
            continue;

            path.push_back(nums[i]);
            check[i]=true;
            dfs(nums,pos+1);
            path.pop_back();
            check[i]=false;
        }
    }
};

法二:只关心合法的

class Solution {
    vector<int> path;
    vector<vector<int>> ret;
    bool check[9];
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
        dfs(nums,0);
        return ret;
    }
    //pos表示填格位数
    void dfs(vector<int>& nums,int pos)
    {
        if(nums.size()==pos)
        {
            ret.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();++i)
        {
            if(check[i]==false)
            {
                if(i==0||nums[i]!=nums[i-1]||check[i-1])
                {
                    path.push_back(nums[i]);
                    check[i]=true;
                    dfs(nums,pos+1);
                    path.pop_back();
                    check[i]=false;
                }
            }
        }
    }
};

五、电话号码的字母组合

17. 电话号码的字母组合 - 力扣(LeetCode)

思路如下:

建立映射关系然后遍历,+=后进入dfs进入下一个字符串的遍历,完事pop回溯

这里映射关系可以用string【】代替哈希表

代码实现如下:

string【】代替哈希表,找字符串的时候取digits【pos】-‘0’就拿到下标了。

在pos==digits.size的时候push path->ret

class Solution {
    string hash[10]={"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    vector<string> ret;
    string path;
public:
    vector<string> letterCombinations(string digits) {
        if(digits=="")return ret;
        dfs(digits,0);
        return ret;
    }
    void dfs(string& digits,int pos)
    {
        if(pos==digits.size())
        {
            ret.push_back(path);
            return;
        }
        for(auto&ch:hash[digits[pos]-'0'])
        {
            path+=ch;
            dfs(digits,pos+1);
            path.pop_back();
        }
    }
};

直接用map的做法

class Solution {
    vector<string> ret;
    string path;
public:
    vector<string> letterCombinations(string digits) {
        if(digits=="")return ret;
        unordered_map<char,string>table{
            {'2',"abc"},{'3',"def"},{'4',"ghi"},
            {'5',"jkl"},{'6',"mno"},{'7',"pqrs"},
            {'8',"tuv"},{'9',"wxyz"}
        };
        dfs(table,digits,0);
        return ret;
    }
    //table得到要遍历的字符串,digits提供key值还有结束判断,pos表示填第几个位置
    void dfs(unordered_map<char,string>&table,string&digits,int pos)
    {
        if(pos==digits.size())
        {
            ret.push_back(path);
            return;
        }
        string tmp=table[digits[pos]];
        for(auto&ch:tmp)
        {
            path+=ch;
            dfs(table,digits,pos+1);
            path.pop_back();
        }
    }
};

六*、括号生成

22. 括号生成 - 力扣(LeetCode)

思考许久是不是只要最左边是左括号,最右边是右括号,里边不管咋样都行。不过思考无果。。。

判断括号组合是否有效只需判断数量上:左==右,且从头开始的任意子串,左括号数量>=右括号数量,否则一定会有一个右边括号匹配不上。

于是,最开始的思路是可行的,那么决策树就是一颗从最开始选择“("最终选择“)”,中间分叉有左有右的一棵树。

这里发现因为第一条原则,中间并不是随意的分叉,比如当左括号数量达到n时,后续的分叉就不能再选择左括号了。

那么在添加路径的时候就要判断左括号,右括号是否合法,如果合法,那么就要进行对应的dfs,然后回溯,

剪枝:left>=n,right>=left剪掉!

递归结束条件为right==n。完美的思路

代码实现如下:

class Solution {
    vector<string> ret;
    string path;
public:
    vector<string> generateParenthesis(int n) {
        dfs(0,0,n);
        return ret;
    }
    void dfs(int left,int right,int n)
    {
        if(right==n)
        {
            ret.push_back(path);
            return;
        }
        //有效时就添加
        if(left<n)
        {
            path+='(';
            dfs(left+1,right,n);
            path.pop_back();
        }
        //right>=left的时候一定不添加),否则一定会有右括号匹配不上
        if(right<left)
        {
            path+=')';
            dfs(left,right+1,n);
            path.pop_back();
        }
    }
};

七、组合

77. 组合 - 力扣(LeetCode)

该题目相当于找元素数量为k的子集

找子集就得避免重复,那么就要多创建一个pos,变量,for循环每次从pos位置往后遍历,dfs下一层时给pos位置传i+1.

class Solution {
    vector<vector<int>> ret;
    vector<int> path;

public:
    vector<vector<int>> combine(int n, int k) {
        dfs(n, k, 1);
        return ret;
    }
    void dfs(int n, int k, int pos) {
        //当k的值为numssize的时候可以用pos,因为这样可以保证全部都遍历到
        //这里判断size而不是pos,不然数字k之后的根本遍历不到就结束了
        if (path.size() == k) {
            ret.push_back(path);
            return;
        }
        for (int i = pos; i <= n; ++i) {
            path.push_back(i);
            dfs(n, k, i + 1);
            path.pop_back();
        }
    }
};

八、目标和

494. 目标和 - 力扣(LeetCode)

该题可以联系到子集题目中,找子集的思路一,选还是不选,这里就转化成+还是-。

class Solution {
    int sum,ret,aim;
public:
    int findTargetSumWays(vector<int>& nums, int target) {
        //target传给全局变量,这样每次函数调用传参的时候就能少传一个参数
        aim=target;
        dfs(nums,0,0);
        return ret;
    }
    void dfs(vector<int>& nums,int sum,int pos)
    {
        //到达终点,才判断
        if(pos==nums.size())
        {
            if(sum==aim)++ret;
            return;
        }
        //+
        dfs(nums,sum+nums[pos],pos+1);
        //-
        dfs(nums,sum-nums[pos],pos+1);
    }
};

九、组合总和

39. 组合总和 - 力扣(LeetCode)

解法一:暴力

依次遍历数组,每次都选数组里的一个数,相当于嵌套for循环暴搜

该题数组元素可以重复选取,这只需要for循环遍历整个数组就行,

画出决策树后发现细节如下:

虽然是for循环遍历整个数组,但是你选择2 3 和选择3 2是算作一种结果的,那么我们就要多传pos,每次for循环起点是这个pos,从该位置开始往后遍历,又因为可以重复选取,dfs下一层时,不是从i+1位置开始往后,而是从当前位置也就是i开始往后;

当sum>target时,后边就不用再遍历了。

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    int aim;
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        aim=target;
        dfs(candidates,0,0);
        return ret;
    }
    void dfs(vector<int>& candidates,int pos,int sum)
    {
        if(sum==aim)
        {
            ret.push_back(path);return;
        }
        if(sum>aim||pos==candidates.size())return;
        for(int i=pos;i<candidates.size();++i)
        {
            path.push_back(candidates[i]);
            dfs(candidates,i,sum+candidates[i]);
            path.pop_back();
        }
    }
};

解法二:

对于数组的每一个元素,我们都设置选法为选几次,最大次数取决于选多少次刚好不大于这个target,看中途会不会有sum==target,大于target直接返回

dfs每层操作是在对数组的某个元素的进行选择

注意:回溯得我走完该层全部枚举后才开始回溯,因为我们每层考虑的是选择的次数,不是是否选择这个数。

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    int aim;
public:
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        aim=target;
        dfs(candidates,0,0);
        return ret;
    }
    void dfs(vector<int>& candidates,int pos,int sum)
    {
       if(sum==aim)
       {
            ret.push_back(path);return;
       }
       if(sum>aim||pos==candidates.size())return;

       //枚举次数
       for(int k=0;sum+k*candidates[pos]<=aim;++k)
       {
            if(k)path.push_back(candidates[pos]);
            dfs(candidates,pos+1,sum+k*candidates[pos]);
       }
       //回溯,从1开始是得选了才能删
       for(int k=1;sum+k*candidates[pos]<=aim;++k)
       {
            path.pop_back();
       }
    }
};

十、组合总和II

40. 组合总和 II - 力扣(LeetCode)

该题与No.15 全排列II 相同,因为存在重复元素,按常规方法pos位置往后搜的思路会导致相同数字以不同顺序作为path传进ret。

解法一:每层都是遍历整个数组,因为避免重复,我们要从pos位置往后遍历,同时开始时对数组进行排序,然后再创建一个check数字检查,避免重复的组合。

这里如果不从pos开始往后遍历就是全排列II。

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    int aim,sum;
    bool check[101];
public:
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        aim=target;
        sort(candidates.begin(),candidates.end());
        dfs(candidates,0,0);
        return ret;
    }
    void dfs(vector<int>& candidates,int pos,int sum)
    {
        if(sum==aim)
        {
            ret.push_back(path);return;
        }
        if(sum>aim||pos==candidates.size())return;
        for(int i=pos;i<candidates.size();++i)
        {
            if(check[i]==false)
            {
                if(i==0||candidates[i]!=candidates[i-1]||check[i-1])
                {
                    path.push_back(candidates[i]);
                    check[i]=true;
                    dfs(candidates,i+1,sum+candidates[i]);
                    path.pop_back();
                    check[i]=false;
                }
            }
        }
    }
};

十一、组合总和III

216. 组合总和 III - 力扣(LeetCode)

该题与No.18 找元素个数为k的子集 思路相似,好吧其实可以说是一模一样,不过这题可以帮忙复习怎么判断 怎么处理指定个数组合 的结束条件

class Solution {
    vector<vector<int>> ret;
    vector<int> path;
    int target,sum;
public:
    vector<vector<int>> combinationSum3(int k, int n) {
        target=n;
        dfs(0,k,1);
        return ret;
    }
    void dfs(int sum,int k,int pos)
    {
        if(path.size()==k)
        {
            if(sum==target)
            {
                ret.push_back(path);return;
            }
        }
        for(int i=pos;i<=9;++i)
        {
            path.push_back(i);
            dfs(sum+i,k,i+1);
            path.pop_back();
        }
    }
};

十二、组合总和IV

377. 组合总和 Ⅳ - 力扣(LeetCode)

原始dfs必定会导致超时,这里引入记忆化搜索,也就是创建一个数组,【index】储存target为index时的方法数,从而起到空间换时间的作用,为了得到最终数据,我们采用从顶向下的思路,将memo初始化(target+1,-1)

memo[0]起点设置为1,

递归终止条件就是target

当memo[target]!=-1时,返回memo[target]

然后我们遍历数组,记录当下target的memo【】值,初始int res=0;res+=所有target-数组元素的结果,最后更新memo【target】返回res即可,

class Solution {
public:
    int combinationSum4(vector<int>& nums, int target) {
        vector<int> memo(target+1,-1);
        memo[0]=1;      //target为0只有空数组
        return dfs(nums,target,memo);
    }
    int dfs(vector<int>& nums,int target,vector<int>& memo)
    {
        if(target<0)return 0;
        if(memo[target]!=-1)return memo[target];

        int res=0;
        for(int&x:nums)
        {
            res+=dfs(nums,target-x,memo);
        }
        //更新缓存数据
        memo[target]=res;
        return res;
    }
};

总结排列、子集、总和

sort+i,pos互传 解决无序性问题

sort+check 解决含有重复元素求组合,避免相同元素在不同时间加入路径得到相同组合 问题

固定大小子集结束条件

无重复元素,组合总和,两个思路,1.暴搜,有去重->i,pos 2.枚举元素选几个,在枚举结束才进行回溯

含有重复元素的组合总和,会导致相同的元素在不同时间内进入运算,得出相同的结果,也就是导致了重复的组合,解决方法和全排列II一样,重复元素,找组别,check特判

前言

解决此类问题不能被模板固定,画出决策树,模拟实现过程代码就好写了!

全排列问题细节:

刚需check数组->判断元素以不同顺序进入路径

无重复元素全排列

遍历整个数组,创建一个bool数组,进入路径的元素设为真,退出dfs后立马回溯。

有重复元素全排列

因为重复元素的问题,相同元素会在不同时间内被添加进路径,导致相同的排列结果出现,为了方便去重,我们首先要将数组进行排序。因为涉及全排列,check数组也是需要的,不过我们需要特殊判断

1.只关心“不合法”的分支,

check[i]==true(结论2.)||

(

i!=0 (因为后边可能会越界,提前判断)

&&

nums[i]==nums[i-1] (同一分支重复,但不同格子时,也就是在填时间上 下一个格子的时候,元素相同是允许的,所以我们还得加一个判断条件)&&

check[i-1]==false(和前一个条件结合后达成真正的同一分支(填同一个格子)重复,如果check[i-1]是true代表i-1填的是之前的格子,是上一次分支的选择,和我们这次不相干)

)(结论1.)

由此,不合法分支判断结束

2.也可以只关心合法的分支

check[i]==false&&

(

i==0(i=0,还在填第一个格子,填啥都可以,不用判断越界)

||

nums[i]!=nums[i-1](和前面分支不相等,当然可以当作填格的一种可能)

||

check[i-1]==true(由于这里运算同级,于是代码从左往右运行,运行到这里已经默认了【i】==【i-1】,如果前面一个check为真,那么代表不同格的同一元素,这是允许的。

)

子集问题细节:

集合不具有顺序性,比如 2 1 3 和 1 2 3 是一个集合

求全部子集

求全部子集问题思路分为两种:

1.按顺序遍历每个元素,每次分支选择该元素进不进入路径,pos==nums.size()结束,将路径加入结果。

2.按元素个数分层,第一层dfs只加入元素个数为1的路径,以此类推,

因为数据已经排序好了,利用i,pos互传可以避免与集合的无序性冲突

每次dfs开始就先将路径加入结果,然后for循环i从pos位置开始往后遍历

即可。

找固定大小子集

递归结束条件应该是path.size()==指定大小,按题意操作后退出,不是pos==指定大小,此时pos不再可以充当边界判断了。

组合总和问题细节:

组合其实也是集合,也有无序性,那么也需要sort + i,pos互传

组合问题也有两个思路:

1.类似暴搜,sort后i,pos互传 为了无序性

2.判断每个元素选几次,先不选直接dfs到下一层,然后for循环依次枚举选几次,循环每次运行时都添加一次该元素进入路径,for循环退出条件sum+i*【】<=target,然后依次dfs到下一层,在枚举完所有情况后才回溯,for(int k=1;sum+k*【】<=target,++k){pop path;}

无重复元素组合总和

按以上两种思路处理即可

有重复元素组合总和

和以上两种思路一致,不过每种思路都要多加一步

思路1多加check特判

check保证相同的元素如果不在同一分支的话允许添加,特判剪枝掉了重复的情况

思路2多加一个pair类型的数组

思路2每层都在判断【pos】选择几次,这个几次要选rest/【pos】,与【pos】个数的较小值,为了得到这个较小值我们要多创建一个pair类型的数组,key是【pos】,value是次数。

有元素可以无限选择的组合总和

原始dfs必定会导致超时,引入记忆化搜索,也就是创建一个数组,空间换取时间,【index】储存target为index时的方法数,从而起到空间换时间的作用,为了得到最终数据,我们采用从顶向下的思路,将memo初始化(target+1,-1)

memo[0]起点设置为1,

递归终止条件就是target

当memo[target]!=-1时,返回memo[target]

然后我们遍历数组,记录当下target的memo【】值,初始int res=0;res+=所有target-数组元素的结果,最后更新memo【target】返回res即可。

此篇完,感谢浏览。

【LeetCode】回溯 JS经典题型(全排列、子集与组合)汇总 文章目录46. 全排列47. 全排列 II78. 子集77. 组合 回溯算法: 实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就 “回溯” 返回,尝试别的路径。 回溯算法的基本思想是:从一条路往前走,能进则进,不能进则退回来,换一条路再试。 ——LeetCode 46. 全排列 原题链接:46. 全排列 /** * @param {number[]} nums * @return {number[][]} */ var permute = fun 阅读详情

相关推荐

一文搞懂全排列、组合、子集问题

微信搜一搜:【bigsai】 获取更多肝货知识 春风十里,感谢有你 前言 Hello,大家好,我是bigsai,long time no see!在刷题和面试过程中,我们经常遇到一些排列组合类的问题,而全排列、组合、子集等问题更是非常经典问题。本篇文章就带你彻底搞懂全排列! 求全排列? 全排列即:n个元素取n个元素(所有元素)的所有排列组合情况。 求组合? 组合即:n个元素取m个元素的所有组合情况(非排列)。 求子集? 子集即:n个元素的所有子集(所有可能的组合情况)。 总的来说全排列数值个数是所有元素.

bigsai 4417

回溯法(深度搜索) 全排列的问题

其实深度搜索和回溯法的区别,在我这里感觉还是很模糊的 总感觉不一样,但是算法的思想是一样的,都是进行不断的尝试,来判断答案的正确性,如果不行的话,我们就回到上一个状态,再进行尝试,如果还是不行,再回去上一个状态…以此类推 最简单的问题就是全排列的问题了,例如leetcode的 46.全排列 给定一个没有重复数字的序列,返回其所有可能的全排列。 示例: 输入: [1,2,3] 输出: [ [1,2...

weixin_43878297的博客 574

OpenSceneGraph编译、安装、开发环境

详细描述WIndows 10下使用Visual Studio 2019编译OpenSceneGraph 3.6.5的操作流程

国产CAx(CAD/CAE/CAM)软件研发 8772

【搜索与回溯】 全排列问题

你谷友链 JZOJJZOJJZOJ 题目描述 输出自然数111到nnn所有不重复的排列,即nnn的全排列,要求所产生的任一数字序列中不允许出现重复的数字。 输入输出格式 输入格式: n(1≤n≤9)n(1≤n≤9)n(1≤n≤9) 输出格式: 由1~n1~n1~n组成的所有不重复的数字序列,每行一个序列。每个数字保留555个常宽。 输入输出样例 输入样例#111: 3 输出样例#111: 1 ...

lzx_真的好菜啊啊啊 1541

用KalmanNet搞定非线性状态估计:当卡尔曼滤波遇上神经网络

本文深入探讨了KalmanNet如何将神经网络与卡尔曼滤波结合,以解决非线性状态估计中的模型失配和噪声不确定性问题。通过保留经典滤波框架并用RNN学习卡尔曼增益,该方法在无人机导航、机器人定位等场景中实现了模型驱动与数据驱动的优势互补,提升了估计的鲁棒性和灵活性。

mango的博客 312

用C#语言实现记事本

本次程序设计作业为用C#语言设计一个记事本,设计的记事本应与电脑系统自带的记事本类似,最后实现的界面及功能应类似于下图。

qq_57738695的博客 2847

回溯与搜索 一 全排列问题

回溯是搜索算法中的一种控制策略。它的基本思想是:为了求得问题的解,先选择某一种可能情况向前探索,在探索过程中,一旦发现原来的选择是错误的,就退回一步重新选择,继续向前探索,如此反复进行,直至得到解或证明无解。 如迷宫问题:进入迷宫后,先随意选择一个前进方向,一步步向前试探前进,如果碰到死胡同,说明前进方向已无路可走,这时,首先看其它方向是否还有路可走,如果有路可走,则沿该...

lybc2019的博客 878

从零开始用TS开发babylonjs - 本机环境搭建

babylonjs,初始化本地安装,web3d引擎

han3527638的专栏 795

46. 全排列【搜索回溯】

给定一个 没有重复 数字的序列,返回其所有可能的全排列。 方法:搜索回溯 /** * @param {number[]} nums * @return {number[][]} */ var permute = function(nums) { let len = nums.length if(!nums)return [] let res = [] // res ...

Janejie_Zhou的博客 295

Lumerical官方案例、FDTD时域有限差分法仿真学习(三)——环形谐振器(Ring resonator)之第二&第三部分

二、使用 MODE 的参数提取和蒙特卡罗 三、最终参数提取

weixin_45719141的博客 1万+

相位偏折PMD全栈技术拆解 | 四步相移解算+梯度积分形貌重建,助力镜面工件微米级缺陷高精度稳定检测

工业精密检测领域中,高反光镜面类工件长期存在检测瓶颈,涵盖汽车高亮车漆、半导体抛光晶圆、光学透镜、精密抛光模具、玻璃盖板等主流产品。传统2D机器视觉仅能识别表面明显污渍、大尺寸破损,完全无法捕捉微米级划痕、凹坑、橘皮、曲面微形变等微观缺陷;常规线激光3D、普通结构光3D检测设备,面对镜面材质会产生严重镜面反射杂光,激光条纹、结构光图案完全失真,必须喷涂显影粉辅助检测,不仅破坏工件表面完整性,还无法适配自动化量产产线,检测效率与良率极低。

getusushu的博客 543

【算法专题】回溯算法

介绍回溯算法以及如何使用回溯算法解题!

YoungMLet的博客 3307

CDD数据库文件制作(二)——DTC配置

1.DTC库 2.新建DTC DTC相关介绍:https://blog.youkuaiyun.com/LOVE135149/article/details/122565443 ① 选择DTC数据库 ② 点击新建DTC ③ DTC参数编辑 3.将DTC库中的DTC加载到Variant中 找到Fault Memory的DTC Table,鼠标放在DTC Table区域,右键点击选择Copy … Reference from Available DTCs…,即可将DTC库中的故障码加载至Variant中。 虽然选择Co

LOVE135149的博客 1万+

【递归、搜索和回溯算法】专题三 :穷举VS暴搜VS深搜VS回溯VS剪枝

专题三 :穷举VS暴搜VS深搜VS回溯VS剪枝 全排列、子集

2402_84815218的博客 983

harbor x509: certificate signed by unknown authority【20251222】

本教程通过「合并证书链(服务器端)+ 预置根 CA(客户端)」彻底解决了自签名证书的 x509 错误,同时集成了 Trivy 漏洞扫描,实现了 Harbor 从搭建到实用的全流程。证书链合并是服务器端的关键,确保客户端获取完整信任路径;客户端需同时配置「系统信任库 + Docker 信任目录」,避免 Docker 独立信任机制导致的问题;所有 IP 需保持一致(Harbor 配置、证书绑定、客户端访问),是避免错误的基础。

老韩的Linux云计算架构师进阶之路 682

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

递归、搜索与回溯——回溯

回溯算法是一种经典的递归算法,通常用于解决组合问题、排列问题和搜索问题等。回溯算法的基本思想:从一个初始状态开始,按照一定的规则向前搜索,当搜索到某个状态无法前进时,回退到前一个状态,再按照其他的规则搜索。回溯算法在搜索过程中维护一个状态树,通过遍历状态树来实现对所有可能解的搜索。回溯算法的核心思想:“试错”,即在搜索过程中不断地做出选择,若选择正确,则继续向前搜索;否则,回退到上一个状态,重新作出选择。回溯算法通常用于解决具有多个解,且每个解都需要搜索才能找到的问题。

2501_93697341的博客 700

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

【LeetCode】﹝回溯法ி﹞ 全排列、子集、组合问题

一、全排列 1.1 无重复元素全排列 LeetCode全排列 给定一个没有重复数字的序列,返回其所有可能的全排列 //示例 输入[1, 2, 3] /*输出 [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ] */ 1.1.1 从左向右交换 观察元组**[1, 2, 3],以元素1为第一位的元素排列有[1, 2, 3]和[1, 3, 2],此排列可以由初始排列和交换2和3后得到。以元素2为第一位的元素排列有

像写诗一样优雅地写代码~ 1万+

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

[Algorithm][回溯][全排列][子集] + 回溯原理 详细讲解

[Algorithm][回溯][全排列][子集] + 回溯原理 详细讲解

SnowK博客 3127

【递归、搜索与回溯算法】穷举、暴搜、深搜、回溯、剪枝

本篇文章列举了两个经典的LeetCode算法题目,包括全排列和子集。我会在每道题中提到每道题的解题思路,并提供解决本题的C++代码。

是阿建吖! 1197

递归,搜索,回溯算法(3)之穷举,暴搜,深搜,回溯,剪枝

递归,搜索,回溯算法(3)之穷举,暴搜,深搜,回溯,剪枝, 感兴趣地宝子们千万不要错过~~

wer24_25的博客 1256

【算法修炼】回溯专题一——组合、排列、子集、搜索

学习自:https://programmercarl.com/0216.%E7%BB%84%E5%90%88%E6%80%BB%E5%92%8CIII.html 1、回溯模板 步骤: 确定递归函数的参数和返回值 回溯函数终止条件 单层搜索过程 2、基础题目 2.1组合(中等) 因为需要记录结果,所以必须得用回溯,如果只是统计个数可以用DP,题目中的限制条件很清楚就是1-n和k个数。 class Solution { // 存储最终答案 List<List<Integer&

Mxeron的博客 903

全排列、子集、组合、子序列

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。 给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 子集是指给定一组元素,从中选取0个或多个元素组成的集合。对于n个元素的集合,它将包含2^n个不同的子集,包括空集和全集。 全排列(Permutation):全排列是指给定一组元素,通过交换元素的位置,得到这组元素所有可能的排列方式。假设有n个元素,全排列将产生n!(n的阶乘)种不

qq_1763467007的博客 2312

子集与全排列问题(力扣78,90,46,47)

通过这三篇博客,我们可以看到回溯算法在解决组合、排列等问题时的应用。其基本思想是通过递归和回溯来探索所有可能的解空间,并及时剪枝以避免重复计算,从而高效地求解问题。可以总结出如下回溯的套路:去重:先对数组进行排序再判断同一层是否使用过相同元素排列和组合都是在叶子结点收集结果集,而子集则是在树的每一个结点都要进行收集结果集希望本文对你理解和解决组合总和问题有所帮助!🌹。

Huahua_1223的博客 1251

专题十一_递归_回溯_剪枝_综合练习_算法专题详细总结

这一题相对来说还是十分简单的,就是上一个专题的最后一题求子集,只要递归函数体能写对,那怎么写这题都能过。2.全排列 Ⅱ(medium)题意很简单就是对全排列后的所有数组添加到ret后去掉所有重复的数组。全排列II跟全排列I一样,思路是一模一样,就只是再剪枝的策略上有所不同,只需要画清楚决策树,就可以完美的解决剪枝的策略。3.电话号码的字⺟组合(medium)题目意思挺简单的,就是给你一个字符串数字,然后针对每个数字的按键的字符串中的字符进行组合,求出所有情况的子集。

2301_80636070的博客 1280

从零开始写算法——回溯篇1:全排列 + 子集

leetcode hot100 回溯两题 —— 子集 + 全排列

m0_59624833的博客 1129

Javascript算法——回溯算法(子集和全排列问题)

子集问题的回溯解法通过递归的方式枚举所有可能的子集。每个决策点有 2 种选择:选或不选当前元素,通过递归深入到下一个元素,直到遍历完所有元素后返回。回溯算法清晰地展示了递归思想和状态回溯的特性,是理解深度优先搜索(DFS)的一种非常好的实践。全排列问题是回溯算法的经典应用,要求从一组元素中生成所有可能的排列。回溯的基本思想是逐步构建排列,遇到选择时递归深入,遍历完所有可能的选择后通过回溯撤销选择,返回上一步继续尝试其他选择。由于全排列问题要求生成所有可能的排列,时间复杂度通常为O(n!,其中n为数组的大小。

警警的博客 1387
上一篇: Linux网络编程:进程间关系和守护进程
下一篇: 牛客101:堆/栈/队列
_dindong
博客等级 码龄2年 107粉丝 91原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_dindong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值