216. Combination Sum III

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值