トピック:
2つの整数n と kが与えられた 場合、1 ... nの k個の可能なすべての組み合わせを返し ます 。
例:
入力: n = 4、k = 2 出力: [ [2,4]、 [3,4]、 [2,3]、 [1,2]、 [1,3]、 [1,4]、 ]
ソース:
問題解決のアイデア:バックトラック
ブルートフォース検索を使用して解決したいが、それでも開始できない場合は、遡及的方法を使用して解決します。
バックトラッキングは、再帰、再帰前のアクセス、および再帰後のバックトラッキングと組み合わせて使用されます。アクセスとバックトラッキングは、反対の操作のペアです。
コード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;
}