トピック:
グレイコードは、2つの連続する値が1桁だけ異なる2進数システムです。
コードの合計桁数を表す負でない整数nが与えられた場合、そのグレイコードシーケンスを出力します。複数の異なる回答がある場合でも、そのうちの1つを返すだけで済みます。
グレイコードシーケンスは0で始まる必要があります。
例1:
入力:2
出力:[0,1,3,2]
説明:
00から0
。01から1
。11から3
10から2
与えられたnに対して、そのグレイコードシーケンスは一意ではありません。
たとえば、[0,2,3,1]も有効なグレイコードシーケンスです。
00から0
10から2
11 - 3
01から1
ソース:
問題解決のアイデア1:バックトラック
中間結果を保存するパスを定義し、条件が満たされるとすぐに出力します。
- 結果は条件を満たしています:パス内の数に達しました
- 再帰呼び出し条件:前の番号のグレイコードがパスにない場合、それらは再帰的です。
現在の番号が000の場合、次のグレイコードは001,010,100になります。コード内の灰色の関数に注目し、p番目の桁を変更して戻ります。最初にp番目の桁が0か1かを判別します。0の場合、OR演算を1に変更できます。1の場合。 AND演算は0に変更できます。
class Solution {
public:
vector<int> result;
vector<int> path;
bool ok;
vector<int> grayCode(int n) {
vector<bool> flag(1<<n, false); // 记录数字是否已在结果列表中
ok = false;
path.push_back(0);
flag[0] = true;
back(n, flag);
return result;
}
void back(int n, vector<bool>& flag) {
if (path.size() == flag.size()) {
result = path;
ok = true;
return;
}
int pre = path[path.size() - 1]; // 结果列表中最后一个数字
for (int i = 0; i < n; i++) {
if (ok) break;
int k = gray(pre, i); // 最后一个数字的改变第i位后的数字
if (!flag[k]) {
flag[k] = true;
path.push_back(k);
back(n, flag);
path.pop_back();
flag[k] = false;
}
}
}
int gray(int v, int p) {
int a = (0x1 << p);
if ((a & v) == 0) {
return v | a;
} else {
return v & (~a);
}
}
};
問題解決のアイデア2:法を見つける
遡及的なコード提出により、運用効率が非常に低いことがわかりました。実行時間:20ミリ秒で、すべてのC ++提出でユーザーの12.37%を打ち負かしました。
不思議なことに、私は解決策を読みに行きましたが、この質問にはまだ従うべきパターンがあることがわかりました。コードを書きませんでした。以下に見つけたパターンをリストしました。
n = 1の場合、結果は[0,1]、
n = 2の場合、次の3つのステップに分けられます。
- n = 1の結果を取得し、[0,1]を取得します。
- [0,1]を取り、それをミラーにコピーして[1,0]を取得します。2つは[0,1 | 1、0]にマージされ、中央に記号|が付いて、それをaと呼びます。今のところミラー。
- ミラーの左側の各番号の先頭に0を追加し、右側に1を追加して、[00、01 | 11、10]を取得します。これが結果です。
n = 3,4,5 ...の場合、上記の3つの手順を順番に繰り返します。
n = 3の場合、
- n = 2の結果を取得し、[00、01、11、10]を取得します。
- [00、01、11、10]を見て、コピーを作成して[10、11、01、00]を取得します。2つを組み合わせて[00、01、11、10 | 10、11、01、 00]。
- ミラーの左側の各数値の先頭に0を追加し、右側に1を追加して、[000、001、011、010 | 110、111、101、100]を取得します。これが結果です。