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