全排列—回溯剪枝的决策树艺术

全排列是一道经典的回溯剪枝算法问题:

给定一个可包含重复数字的序列nums,按任意顺序返回所有不重复的全排列。题目要求返回所有不重复的全排列,因此需要在排列的基础上进行去重。

排列可以采用回溯决策树来进行求解,去重则是在回溯的基础上对决策树进行剪枝。

整体思路:

1.排序预处理:将数组排序,让相同元素相邻。

2.回溯搜索:构建决策树,逐层选择元素。

3.剪枝去重:在决策树中进行有序选择,确保重复元素只以一种固定顺序被选取。

例如,有三个重复元素a1、a2、a3,如果不加剪枝约束,那么[a1、a2、a3]和[a2、a1、a3]等排列虽然在数值上完全一样,但都会被当做不同的排列生成。

剪枝策略:要求相同元素在排列中必须按照它们在数组中的顺序出现。即,只有前一个相同元素已经被选取时,才允许选择当前元素。

代码实现:

class Solution {
    vector<vector<int>> res;
    vector<int> path;
    bool visited[9];
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(),nums.end());
         dfs(nums,0);
         return res;
    }
    void dfs(vector<int>& nums,int pos)
    {
        if(pos==nums.size())
        {
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(visited[i] || ((i!=0)&&(nums[i]==nums[i-1])&& !visited[i-1])) continue;
            path.push_back(nums[i]);
            visited[i]=true;
            dfs(nums,pos+1);
            path.pop_back();
            visited[i]=false;
        }
    }
};

vector<vector<int>> res,res用于存储所有最终排列结果,vector<int> path,path为路径数组,用于存储一个正在构建的排列,bool visited[9],visited为标记数组,用于标记数组中元素是否已被使用,res、path、visited均为全局变量。sort(nums.begin(),nums.end()),调用sort对数组进行排序,让相同元素靠在一起,dfs(nums,0),通过dfs递归构建所有排列,在每一层递归中,通过for循环遍历所有元素,if(visited[i] || ((i!=0)&&(nums[i]==nums[i-1])&&!visited[i-1])),通过两个条件进行剪枝,visited[i],表示nums[i]已被使用,则continue跳过已经被使用的元素,(i!=0)&&(nums[i]==nums[i-1]&&!visited[i-1]),nums[i]==nums[i-1],遇到重复元素时,!visited[i-1],若前一个相同元素没被使用过,也continue跳过该元素,!visited[i-1]保证了相同数值的元素在排列中必须按照从左往右的固定顺序出现,从而完成去重。path.push_back(nums[i]),visited[i]=true,将nums[i]加入path中,并标记为已访问。dfs(nums,pos+1),继续向下递归。

if(pos==nums.size()),当递归深度达到数组长度时,当前路径就是一个完整的排列,res.push_back(path),将其加入结果集,return;,之后进行标准的回溯操作,path.pop_back(),visited[i]=false,弹出末尾元素,并重置其标志状态,为递归其他分支做准备。最终,res即为所有去重排列的集合,return res即可。

全排列这道算法题的核心在于:

1.通过排序让相同的元素彼此相邻

2.剪枝通过固定重复元素的选取顺序,避免生成重复排列

3.回溯确保能搜索到所有可能的排列组合

回溯的核心在于用最简单的递归结构,承载了最复杂的决策逻辑,而排序、剪枝则是在回溯基础上的进一步优化,实现去重。这套方法可以解决包含重复元素的排列组合问题,例如字符串排列、子集、组合问题,背后的方法都一脉相承,掌握排序+回溯+剪枝这套方法论,就能真正实现举一反三!

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值