[Backtracking] [leetcode] Return all possible combinations of k numbers in 1 ... n

topic:

Given two integers  n  and  k , return all possible  combinations of  k numbers in 1...  .

Example:

Input:  n = 4, k = 2
 Output: 
[ 
  [2,4], 
  [3,4], 
  [2,3], 
  [1,2], 
  [1,3], 
  [1,4], 
]

source:

77. Combination

Problem-solving ideas: backtracking

Want to use brute force search to solve, but still can't start, then we use retrospective method to solve.

Backtracking is used in conjunction with recursion, access before recursion, and backtracking after recursion. Access and backtracking are a pair of opposite operations.

Code one:

class Solution {
public:
    vector< vector<int> > result;
    vector<int> path;

    vector< vector<int> > combine(int n, int k) {
        go(n, k, 1);
        return result;
    }

    void go(int n, int k, int start) {
        if (path.size() == k) {
            // 保存结果
            result.push_back(path);
            return;
        }
        for (int i = start; i <= n; i++) {
            path.push_back(i); // 访问
            go(n, k, i+1); // 递归
            path.pop_back(); // 回溯
        }
    }
};

Code a brute force search for all possible combinations, including path.size()<k. In this case, pruning is needed to improve code efficiency.

For example, if the set [1,2,3,4], k=3, when start points to 3, there is only a number 4 left behind, which is already insufficient. At this time, there is no need to recursively call this situation.

The remaining number: n-start

The number already in the path: path.size()

Current start: 1, (the number pointed to by start has not entered the path, nor has it been counted in the remaining)

So when the remaining number + the number already in the path + the current start <k, exit the loop. This is the pruning condition: n-i + path.size() + 1 <k,

Optimized code two:

class Solution {
public:
    vector< vector<int> > result;
    vector<int> path;

    vector< vector<int> > combine(int n, int k) {
        go(n, k, 1);
        return result;
    }

    void go(int n, int k, int start) {
        if (path.size() == k) {
            result.push_back(path);
            return;
        }
        for (int i = start; i <= n; i++) {
            if (n - i + path.size() + 1 < k) break;
            path.push_back(i);
            go(n, k, i+1);
            path.pop_back();
        }
    }
};

Backtracking + recursion, the code written is simple and easy to understand. I wrote the code once using violence before, but now I can’t read it by myself. I posted it and compared it. See the code below.

It means to specify the first result first, and each subsequent result adds +1 to the previous result, just as there is such a +1 operator: current = prev + 1. Note that the number must be rounded if it exceeds.

/**
 * Return an array of arrays of size *returnSize.
 * The sizes of the arrays are returned as *returnColumnSizes array.
 * Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
 */
int combine_size(int n, int k) {
    if (k > n / 2) k = n - k;
    int s = 1;
    for (int i = 1; i <= k; i++) {
        s *= n--;
        s /= i;
    }
    return s;
}

int** combine(int n, int k, int* returnSize, int** returnColumnSizes){
    int sz = combine_size(n, k); // 先计算空间大小
    *returnSize = sz;

    // 申请空间
    int **ret = (int**)malloc(sizeof(int*) * sz);
    int *sizes = (int*)malloc(sizeof(int) * sz);
    for (int i = 0; i < sz; i++) {
        int *t = (int*)malloc(sizeof(int) * (k+1));
        t[k] = n + 1;
        ret[i] = t;
        sizes[i] = k;
    }
    *returnColumnSizes = sizes;

    // 初始第一个结果
    for (int i = 0; i < k; i++) {
        ret[0][i] = i + 1;
    }
    int p = 1;
    while (p < sz) {
        // ret[p] = ret[p-1] + 1
        int *pre = ret[p-1];
        int *cur = ret[p];

        // p指向行,而pos指向列,从最后一列算起
        // n=6 k=3, 1,2,3,6 -> 1,2,4,6
        int pos = k - 1;
        while (pos >= 0) {
            if (pre[pos] < pre[pos+1] - 1) {
                break;
            }
            pos--;
        }
        for (int i = 0; i < pos; i++) {
            cur[i] = pre[i];
        }
        cur[pos] = pre[pos] + 1;
        for (int i = pos + 1; i < k; i++) {
            cur[i] = cur[i-1] + 1;
        }
        p++;
    }
    return ret;
}

 

Guess you like

Origin blog.csdn.net/hbuxiaoshe/article/details/114704584