、実際には、決定木のトラバーサルを問題を解決するためにバックトラック。あなただけの3つの質問を考える必要があります:
1、パス:で、選択を行っています。
リストを選択2.:で、あなたは現在の選択を行うことができます。
3、終了条件:決定木の底に到達するためには、条件を選択しないでください。
あなたはこれらの三つの言葉の説明を理解していない場合、我々は「完全な配列」と「あなたはこれらの言葉の意味を理解するためのアルゴリズムをバックトラックのNクイーンは、2つの古典的な問題、今最初のキープを使用するよう、それは、問題ではありません印象。
コード側、アルゴリズムのフレームワークをバックトラック:
result = []
def backtrack(路径, 选择列表):
if 满足结束条件:
result.add(路径)
return
for 选择 in 选择列表:
做选择
backtrack(路径, 选择列表)
撤销选择
その中核は、「メークの選択」、再帰呼び出しの後、「選択解除」への再帰呼び出しの前に、再帰的なループの内部用で、特に簡単。
このフレームワークの基本原理は、それ何です、それの選択と選択解除は何ですか?ここでは、この問題は秘密に関する詳細なお問合せを通じて解決される前に、「完全な配列は」困惑持っています!
まず、完全な配列の問題
私たちは、ポートフォリオを揃え、高校の数学の問題でそれをやった、我々はまた、知っているn
非繰り返しの数、!n番目の合計の全体配置。
PS:簡単明瞭にするために、我々はこの議論を配置し、すべての問題は、重複する番号が含まれていません。
我々はそれを網羅する方法のように完全な配列でしたか?3つの数字のためにしてみましょうと言うが[1,2,3]
、あなたは確かに不規則に網羅混沌、このような通常のものではないでしょう。
次いで、3二位になっても、第三の場所はわずか2アップであろう;それだけで変更することができ、第1の固定最初のビットは、第2の第三はわずか3であり、次に、2であることができる、1 ......最初の二つの後、それは、網羅次いで2なり、及び
長いツリートラバーサル、ルートから、実際には、すべての全体配置のパス上のデジタル録音と同じくらい。私たちは、「決定木」と呼ばれる木のバックトラッキングアルゴリズムを持つことを望みます。
さて、あなたはこのコアフレームワークのバックトラッキングアルゴリズムを理解していますか?
for 选择 in 选择列表:
# 做选择
将该选择从选择列表移除
路径.add(选择)
backtrack(路径, 选择列表)
# 撤销选择
路径.remove(选择)
将该选择再加入选择列表
限り、私たちは、再帰的なオプションの後に再帰的な撤退の前に選択をするよう、あなたは正しい道と、リストの各ノードを選択するために取得することができます。
次に、全体の配置は、直接コードを参照してください。
List<List<Integer>> res = new LinkedList<>();
/* 主函数,输入一组不重复的数字,返回它们的全排列 */
List<List<Integer>> permute(int[] nums) {
// 记录「路径」
LinkedList<Integer> track = new LinkedList<>();
backtrack(nums, track);
return res;
}
// 路径:记录在 track 中
// 选择列表:nums 中不存在于 track 的那些元素
// 结束条件:nums 中的元素全都在 track 中出现
void backtrack(int[] nums, LinkedList<Integer> track) {
// 触发结束条件
if (track.size() == nums.length) {
res.add(new LinkedList(track));
return;
}
for (int i = 0; i < nums.length; i++) {
// 排除不合法的选择
if (track.contains(nums[i]))
continue;
// 做选择
track.add(nums[i]);
// 进入下一层决策树
backtrack(nums, track);
// 取消选择
track.removeLast();
}
}
二、Nクイーン問題
あなたはN×Nのチェス盤を与えるために、あなたはNは、彼らがお互いを攻撃することはできませんので、クイーンズ置いてみましょう:これは説明するのは非常に古典的な、シンプルです。
PS:女王は、同じ列の同じ行を攻撃することができ、任意の単位で右上右下左上の4つの方向を左下。
ほぼ完全なパーミュテーション問題と問題の性質は、ツリーの各レイヤは、ボード上の各列を表し、各ノードは女王の行のいずれかに配置されるための選択を行うことができます。
フレームに直接適用されます。
vector<vector<string>> res;
/* 输入棋盘边长 n,返回所有合法的放置 */
vector<vector<string>> solveNQueens(int n) {
// '.' 表示空,'Q' 表示皇后,初始化空棋盘。
vector<string> board(n, string(n, '.'));
backtrack(board, 0);
return res;
}
// 路径:board 中小于 row 的那些行都已经成功放置了皇后
// 选择列表:第 row 行的所有列都是放置皇后的选择
// 结束条件:row 超过 board 的最后一行
void backtrack(vector<string>& board, int row) {
// 触发结束条件
if (row == board.size()) {
res.push_back(board);
return;
}
int n = board[row].size();
for (int col = 0; col < n; col++) {
// 排除不合法选择
if (!isValid(board, row, col))
continue;
// 做选择
board[row][col] = 'Q';
// 进入下一行决策
backtrack(board, row + 1);
// 撤销选择
board[row][col] = '.';
}
}
メインのコードのこの部分は、実際には、全体の配置に類似の問題点isValid
を達成するための機能は非常に簡単です:
/* 是否可以在 board[row][col] 放置皇后? */
bool isValid(vector<string>& board, int row, int col) {
int n = board.size();
// 检查列是否有皇后互相冲突
for (int i = 0; i < n; i++) {
if (board[i][col] == 'Q')
return false;
}
// 检查右上方是否有皇后互相冲突
for (int i = row - 1, j = col + 1;
i >= 0 && j < n; i--, j++) {
if (board[i][j] == 'Q')
return false;
}
// 检查左上方是否有皇后互相冲突
for (int i = row - 1, j = col - 1;
i >= 0 && j >= 0; i--, j--) {
if (board[i][j] == 'Q')
return false;
}
return true;
}