[バックトラッキング] [leetcode] k個の可能なすべての組み合わせを1 ... nで返します

トピック:

2つの整数n と kが与えられた  場合、1 ... nの k個の可能なすべての組み合わせ返し ます 

例:

入力:  n = 4、k = 2
出力: 
[ 
  [2,4]、
  [3,4]、
  [2,3]、
  [1,2]、
  [1,3]、
  [1,4]、
]

ソース:

77.組み合わせ

問題解決のアイデア:バックトラック

ブルートフォース検索を使用して解決したいが、それでも開始できない場合は、遡及的方法を使用して解決します。

バックトラッキングは、再帰、再帰前のアクセス、および再帰後のバックトラッキングと組み合わせて使用​​されます。アクセスとバックトラッキングは、反対の操作のペアです。

コード1:

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(); // 回溯
        }
    }
};

path.size()<kを含む、考えられるすべての組み合わせのブルートフォース検索をコーディングします。この場合、コードの効率を向上させるためにプルーニングが必要です。

たとえば、セット[1,2,3,4]、k = 3の場合、開始点が3の場合、残りの数は4のみであり、すでに不十分です。この時点では、再帰的に行う必要はありません。この状況を呼び出します。

残りの数:n-start

すでにパスにある番号:path.size()

現在の開始:1(開始が指す数はパスに入力されておらず、残りの数にもカウントされていません)

したがって、残りの数+すでにパスにある数+現在の開始<kの場合、ループを終了します。これがプルーニング条件です:n-i + path.size()+ 1 <k、

最適化されたコード2:

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();
        }
    }
};

バックトラック+再帰、記述されたコードはシンプルで理解しやすいです。以前は暴力を使ってコードを書いたことがありますが、今では自分で読むことができません。投稿して比較しました。以下のコードをご覧ください。

これは、最初の結果を最初に指定することを意味し、そのような+1演算子があるのと同じように、後続の各結果は前の結果に+1を追加します:current = prev +1。を超える場合は、数値を四捨五入する必要があることに注意してください。

/**
 * 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;
}

 

おすすめ

転載: blog.csdn.net/hbuxiaoshe/article/details/114704584