排列组合问题
排列问题
给定一个不含重复数字的数组nums,返回所有的全排列。
nums = [1,2,3]
[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
每次我们都要遍历nums,然后选择一个nums[i]加入到排列的结果当中,每次选择需要判断当前数字是否已经被选择过,排除掉[1,1,1]这种结果。
vector<vector<int>> permute(vector<int>& nums) {
vector<int> temp;
if(nums.size()==0) return res;
back(nums,temp);
return res;
}
void back(vector<int>& nums,vector<int> temp){
if(temp.size()==nums.size())
{
res.push_back(temp);
return;
}
for(int i=0;i<nums.size();i++){
vector<int>::iterator it;
it=find(temp.begin(),temp.end(),nums[i]);
if(it==temp.end()){
temp.push_back(nums[i]);
back(nums,temp);
temp.pop_back();
}
}
}
排列问题的时间复杂度是O(n!)
组合问题
给定整数n和k,返回范围[1,n]中所有可能的k个数的组合。
对于排列问题,每次中nums中选择一个数字,并且保证这个数字在之前没有选择过。对于组合问题,每次都是在[1,n]中选择一个数,满足个数等于k,但是这种会带来重复问题,比如[1,2]和[2,1]其实是一种组合,所以为了排除掉重复问题,选择nums[i]之后,需要从nums[i+1,n]中选择。
组合求子集的时间复杂度是O(n*2^n)
对子集进行划分
- 划分为k个相等的子集
给定一个整数数组 nums 和一个正整数 k,找出是否有可能把这个数组分成 k 个非空子集,其总和都相等。
思考:该问题可以转化为sum(nums)/k=target.从nums中划分k个桶,每个桶的和为target。对于每个桶,从头枚举每个数,使得桶里面和为target。
class Solution {
public:
bool backTracking(vector<int>& nums, vector<bool>& used, int k, int sum, int& target, int startIndex) {
if (k == 0) return true; //如果已经划分完毕了,返回true
if (sum == target) return backTracking(nums, used, k - 1, 0, target, 0); //此次划分等于目标值
for (int i = startIndex; i < nums.size(); ++i) {
if (used[i]) continue;
else if (sum + nums[i] > target) continue;
sum += nums[i];
used[i] = true;
if (backTracking(nums, used, k, sum, target, i + 1)) return true;
used[i] = false;
sum -= nums[i];
}
return false;
}
bool canPartitionKSubsets(vector<int>& nums, int k) {
int numsSum = accumulate(nums.begin(), nums.end(), 0);
if (numsSum % k != 0) return false;
int target = numsSum / k;
int maxNum = *max_element(nums.begin(), nums.end());
if (maxNum > target) return false;
vector<bool> used(nums.size(), false);
return backTracking(nums, used, k, 0, target, 0);
}
};