力扣46.全排列(回溯法)

题目:

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例一:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例二:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例三:

输入:nums = [1]
输出:[[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

思路:

首先还是先来看题目,题目很简单明了,就是找齐全排列的情况,我们可以从提示中得到两点信息。1.nums数组长度最小为1,所以我们不需要对长度进行额外的判断。2.nums数组中的所有整数互不相同,这是很重要的一定。

题目看完了,我们很容易看出用回溯法来做这道题是最合适的,下面是我写的代码。

代码:

class Solution {
public:
    vector<vector<int>> res;
    vector<int> vec;
    set<int> _set;//标记集合

    void BackTracking(vector<int>& nums){
        if(vec.size() == nums.size()){//退出条件
            res.push_back(vec);
            return;
        }
        for(int i = 0; i < nums.size(); i++){//每一层选择
            if(_set.find(nums[i]) == _set.end()){//判断是否可以选择
                vec.push_back(nums[i]);
                _set.insert(nums[i]);
                BackTracking(nums);
                vec.pop_back();//回退到上一个状态
                _set.erase(nums[i]);
            }
        }
    }

    vector<vector<int>> permute(vector<int>& nums) {
        BackTracking(nums);
        return res;
    }
};

代码讲解:

    vector<vector<int>> res;

    vector<int> vec;

    set<int> _set;//标记集合

首先这三个是我们在类中设置的变量,第一个res是一个二维数组,用来保存你最终要返回的答案的。第二个是一个一维数组,是我们用来存储每一个全排列的情况来使用的(这里我们使用vector数组的大容器与小容器的关系,我们直接将小容器(vec)装入大容器(res)中,就完成了二维数组的值的添加)。第三个是一个标记集合,标记集合我们在学习图的时候用到的非常多,这里的含义和在图中的含义是一样的,就是用力啊标记这个树是否已经加入到vec中。

    vector<vector<int>> permute(vector<int>& nums) {

        BackTracking(nums);

        return res;

    }

这就是主函数,没什么好说的,先调用了我们写的回溯函数,然后返回res数组。

 

    void BackTracking(vector<int>& nums){

        if(vec.size() == nums.size()){//退出条件

            res.push_back(vec);

            return;

        }

        for(int i = 0; i < nums.size(); i++){//每一层选择

            if(_set.find(nums[i]) == _set.end()){//判断是否可以选择

                vec.push_back(nums[i]);

                _set.insert(nums[i]);

                BackTracking(nums);

                vec.pop_back();//回退到上一个状态

                _set.erase(nums[i]);

            }

        }

    }

接下来就是我们回溯的主要代码,首先我们先来讲一下代码,具体的流程我会放在下面。

首先先进来的if判断,用处就是回退的,和递归当中的一样,只不过这时我们完成小容器装入到大容器中这一步。

下面的for循环就是我们在写回溯时要用到的选择系统,我们如何去完成每一次选哪个数字技术通过for循环来完成的。

进入for循环当中,首先先从标记集合中判断当前数字是否已经放入vec中(学过图的同学应该对这个印象会很深),若没有放过,则进入if。

进入if,首先我们讲当前数字放入vec和标记集合当中,然后再次调用回溯函数 ,这里大体上和递归时一模一样的,区别就在于当回退到这里再向下运行函数时,我们完成的状态的回退,就是将vec和标记集合中的当前数字弹出,在弹出之后for循环i++,当前数字改变。

接下来我来给大家描述一下整个流程

这里我们拿示例一【1,2,3】来给大家讲

首先 permute函数调用了我们写的回溯函数,此时推出条件不成立,继续进行下面的代码,进入for循环当中,我们将1放入vec数组和_set标记集合中,然后再次调用回溯函数,(之后的回溯函数我们只说推出条件成立时的情况,不成立我们将不会赘述)进入for循环,此时i=0,nums[i]=1,标记集合当中查到了1已经存在了,所以i++,nums[i]=2,我们将2放入vec和标记集合中,再次调用回溯函数,1和2都在标记集合中存在了,所以3加入到vec和标记集合当中,再次调用回溯函数,此时满足退出条件,【1,2,3】的集合被装入到res中,回到上一层回溯

                vec.pop_back();//回退到上一个状态

                _set.erase(nums[i]);

我们使用这两条语句完成了状态的回退,将3从vec和标记集合中弹出,for循环结束,回退到上一层回溯,上一层回溯又将2从vec和标记集合中弹出,for循环的i++,此时nums[i]=3,将3加入到vec和标记集合当中,然后再次调用回溯函数,进入for循环发现1在标记集合中,所以将2加入vec和标记集合中,然后再次调用回溯函数,此时满足退出条件,【1,3,2】被装入到res当中,然后回退到上一层回溯,将2从vec和标记集合中弹出,for循环i++,nums[i] = 3,发现3已经在标记集合中了,for循环结束,回退到上一层循环,将3从vec和标记集合中弹出,for循环结束,将1从vec和标记集合中弹出,for循环i++,nums[i]=2,接下来就是和之前流程大差不差了,之不过是以2卡头了。 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

puzell

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

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

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

打赏作者

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

抵扣说明:

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

余额充值