失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 力扣46.全排列(回溯法)

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

时间:2019-03-25 00:59:33

相关推荐

力扣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] <= 10nums中的所有整数互不相同

思路:

首先还是先来看题目,题目很简单明了,就是找齐全排列的情况,我们可以从提示中得到两点信息。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);

returnres;

}

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

voidBackTracking(vector<int>&nums){

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

res.push_back(vec);

return;

}

for(inti=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卡头了。

如果觉得《力扣46.全排列(回溯法)》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。