您的位置:首页 > 编程语言 > C语言/C++

216. Combination Sum III

2017-03-26 21:50 211 查看
Combination Sum III
题目解析

代码实现

radix sort

216. Combination Sum III

题目解析

Find all possible combinations of k numbers that add up to a number n, given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers.

Example 1:

Input: k = 3, n = 7

Output:

[[1,2,4]]

Example 2:

Input: k = 3, n = 9

Output:

[[1,2,6], [1,3,5], [2,3,4]]


就是在1~9中取k个数字,不能重复。使得k个数的和为n的组合存进数组并返回。

这里我使用的是暴力破解,根据最后一个数字的索引来迭代求和,如果到了尽头那么就把前一位的索引加一。如果前一位还是有进位,那么就继续进位,直到没有进位。然后把最后一位进位的索引后面的索引排在它的后面,然后往复进行。

代码实现

暴力破解:

class Solution {
public:
__inline int calculateSum(vector<int> ind, int num) {
int res = 0;
for(int i = 1; i <= num; i++) res += ind[i];
return res;
}

vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> ind(10, 0);
if(k > 9 || n > 45) return res;
for(int i = 1; i <= 9; i++) ind[i] = i;

while(ind[1] <= 10 - k) {
while(ind[k] <= 9) {
int cur_sum = calculateSum(ind, k);
if(cur_sum == n)  res.push_back(vector<int>(ind.begin()+1, ind.begin()+k+1));
else if(cur_sum > n) { ind[k] = 10;  break; }
ind[k]++;
}
if(ind[1] == 10 - k) break;
int stt = k-1;
while(stt >= 1) {
ind[stt] += 1;
if(ind[stt] <= 10 + stt - k) break;
else {
stt--;
}
}
for(int i = stt+1; i <= k; i++) ind[i] = ind[i-1] + 1;
}
return res;
}
};


改进前面的做法,使用夹逼来做这道题目,前后两个部分的数字数目一样,然后遇到条件一样的时候就把两个都做计算。使用这种方法结果是超时,当然这种做法确实有bug。

class Solution {
public:
__inline int calculateSum(vector<int> ind, int num) {
int res = 0;
for(int i = 1; i <= num; i++) res += ind[i];
return res;
}

vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
set<vector<int>> tmp;
vector<int> ind(10, 0);
vector<int> ind2(10, 0);
if(k > 9 || n > 45) return res;
int half = k >> 1, cnt = 1, cur_sum = 0;
for(cnt = 1; cnt <= half + (1&k); cnt++) ind[cnt] = cnt;
for(int i = 10 - half; i <= 9; i++)  ind[cnt++] = i;

queue<vector<int>> stk;
stk.push(ind);

while(!stk.empty()) {
ind = stk.front();
stk.pop();

while(ind[half] < ind[half+1]) {
int h1 = ind[half], h2 = ind[half+1];
ind2 = ind;
while(ind[half] < ind[half+1]) {
cur_sum = calculateSum(ind, k);
if(cur_sum == n)  { tmp.insert(vector<int>(ind.begin()+1, ind.begin()+k+1)); ind[half]++; ind[half+1]--; }
else if(cur_sum > n)  ind[half+1]--;
else ind[half]++;
}

if(ind[k] - ind[1] <= k - 1) break;

int stt = 0;
if(cur_sum < n) {
stt = half - 1;
ind[half+1] = h2;
while(stt >= 1) {
ind[stt]++;
if(ind[half+1] - ind[stt] >= half + 1 - stt) break;
else  stt--;
}
for(int i = stt+1; i <= half; i++) ind[i] = ind[i-1] + 1;
}
else if(cur_sum > n){
stt = half + 2;
ind[half] = h1;
while(stt <= k) {
ind[stt]--;
if(ind[stt] - ind[half] >= stt - half) break;
else  stt++;
}
for(int i = stt-1; i >= half+1; i--) ind[i] = ind[i+1] - 1;
}
else {
// part 1
stt = half - 1;
ind[half+1] = h2;
while(stt >= 1) {
ind[stt]++;
if(ind[half+1] - ind[stt] >= half + 1 - stt) break;
else  stt--;
}
for(int i = stt+1; i <= half; i++) ind[i] = ind[i-1] + 1;

// part 2
stt = half + 2;
while(stt <= k) {
ind2[stt]--;
if(ind2[stt] - ind2[half] >= stt - half) break;
else  stt++;
}
for(int i = stt-1; i >= half+1; i--) ind2[i] = ind2[i+1] - 1;
stk.push(ind2);
}
}
}
return vector<vector<int>>(tmp.begin(), tmp.end());
}
};


然后是DFS的做法,我们从9个数字中取k个数字,然后每个数字就是取或者没有取。这个代码的效率比第一种做法高了很多。

class Solution {
public:
void backTrack(vector<vector<int>>&res, int target, int num, vector<int> &tmp, int stt) {
int sz = tmp.size(), cur_sum = accumulate(tmp.begin(), tmp.end(), 0);
if(tmp.size() == num) {
if(cur_sum == target) res.push_back(tmp);
return;
}
if(cur_sum >= target) return;

if(9 - stt + 1 >= num - sz) {
tmp.push_back(stt);
backTrack(res, target, num, tmp, stt+1);
tmp.pop_back();
backTrack(res, target, num, tmp, stt+1);
}
}

vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> ind;
if(k > 9 || n > 45) return res;

backTrack(res, n, k, ind, 1);

return res;
}
};


使用回溯,可以更加节约空间,但是速度上没有比DFS快,会差一点点。

class Solution {
public:
void backTrack(vector<vector<int>>&res, int target, int num, vector<int> &tmp, int stt) {
int sz = tmp.size(), cur_sum = accumulate(tmp.begin(), tmp.end(), 0);
if(tmp.size() == num) {
if(cur_sum == target) res.push_back(tmp);
return;
}
if(cur_sum >= target) return;

if(9 - stt + 1 >= num - sz) {
for(int i = stt; i <= 9; i++) {
tmp.push_back(i);
backTrack(res, target, num, tmp, i+1);
tmp.pop_back();
}
}
}

vector<vector<int>> combinationSum3(int k, int n) {
vector<vector<int>> res;
vector<int> ind;
if(k > 9 || n > 45) return res;

backTrack(res, n, k, ind, 1);

return res;
}
};


radix sort

经典排序算法 - 基数排序Radix sort

原理类似桶排序,这里总是需要10个桶,多次使用

首先以个位数的值进行装桶,即个位数为1则放入1号桶,为9则放入9号桶,暂时忽视十位数

例如:

待排序数组[62,14,59,88,16]简单点五个数字

分配10个桶,桶编号为0-9,以个位数数字为桶编号依次入桶,变成下边这样

| 0 | 0 | 62 | 0 | 14 | 0 | 16 | 0 | 88 | 59 |

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶编号

将桶里的数字顺序取出来,

输出结果:[62,14,16,88,59]

再次入桶,不过这次以十位数的数字为准,进入相应的桶,变成下边这样:

由于前边做了个位数的排序,所以当十位数相等时,个位数字是由小到大的顺序入桶的,就是说,入完桶还是有序

| 0 | 14,16 | 0 | 0 | 0 | 59 | 62 | 0 | 88 | 0 |

| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |桶编号

因为没有大过100的数字,没有百位数,所以到这排序完毕,顺序取出即可

最后输出结果:[14,16,59,62,88]

参考链接:http://www.cnblogs.com/kkun/archive/2011/11/23/2260275.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  216 leetcode c++