C++回溯问题框架(N皇后例子+决策树)

本文来自labuladong算法小抄书中。
回溯算法问题框架:
解决一个回溯问题实际上是一个决策树的遍历过程。

文章目录

    • 1.决策树
    • 2.回溯算法框架
    • 3.回溯算法框架应用_N皇后问题(皇后数量和棋盘的行列相同)
    • N皇后排除不合法选择isAvalid选择函数
    • 4.C++N皇后源码
    • 5.代码连接

1.决策树

eg:数字1,2,3进行全排列。
先固定数字1,再固定数字2,最后只能是数字3。之后回到选择数字2的位置改变选择选择3,最后选择2…
树状图如下:
C++回溯问题框架(N皇后例子+决策树)_第1张图片

每个节点都要经历一次决策,我们称这棵树为回溯算法的决策树

2.回溯算法框架

解决回溯问题要思考三个问题
1.已经做出的选择:对应上图为已经做过的选择,cur节点上的选择1
2.选择列表:还可以做的选择。对应上图为每个节点还剩下的数字,cur节点还有两个选择2和3
3.结束条件:到达决策树的底层,无法再做选择的条件。
下面用伪代码的形式写出回溯算法框架

vector<T>result//存放结果的数组
void backtrack(路径,选择列表)
	if 结束条件
	{
     
		result.push(路径)
		return
	}		
	for 选择(在选择列表中)
	{
     
	 	排除不合法选择
		做选择
		backtrack(路径,选择列表)//进入下一行决策树
		撤销选择
	}

核心在for里面的循环,在递归前做选择,递归后撤销选择

3.回溯算法框架应用_N皇后问题(皇后数量和棋盘的行列相同)

八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n。当且仅当n = 1或n ≥ 4时问题有解。——维基百科

这里选择套用回溯问题框架解决
1.首先定义一个棋盘:这里选择用数组存放,数组每个元素为一个字符串,一个字符串代表一行。‘_’代表空 ‘*’代表一个皇后。vector
2.定义一个存放结果的数组,数组的每个元素为一个棋盘类型vector>ret
3.确定路径和选择列表:首先每一行只能有一个皇后,即已经放了n个皇后代表已经到了n行。所以我们路径可以选择已经放了皇后的个数。每一行上有n列,每一个皇后都有n个选择列表。所以根据上述分析,将行数作为路径,将列数作为选择列表
4.确认不合法选择,即与上一次皇后在同一列和对角线。一定不可能在同一行,因为我们在程序设计的时候已经避免了这种情况了
5.结束条件:路径数==棋盘大小,即所有的皇后都放到了棋盘上了。

选择树图C++回溯问题框架(N皇后例子+决策树)_第2张图片
根据上图可知当遍历到决策树底端时就是一种解情况,这时后插入到结果数组中,退回上一层继续抉择。直到最后返回到节点board[0][0]上,但起始节点也有col中选择,以此类推这样就遍历完所有情况了,与全排列类似

基本框架:

//行数
void backtrack(vector<string>& board, int row)
{
     
	//结束条件
	if (row == board.size())
	{
     
		ret.push_back(board);
		return;
	}
	int size = board[row].size();//选择列表的大小
	for (int col = 0; col < size; col++)//在选择列表中做选择
	{
     
		if (!isAvalid(board, row, col))//排除不合法选择
		{
     
			continue;
		}
		//做选择
		board[row][col] = '*';
		//进入下一行决策树
		backtrack(board, row + 1);
		//撤销选择
		board[row][col] = '_';
	}
}

void CoutNQueen(const int n)
{
     
	//初始化棋盘'_'代表空 '*'代表皇后
	vector<string>board(n, string(n, '_'));
	backtrack(board, 0);//回溯算法,开始选择
}

N皇后排除不合法选择isAvalid选择函数

每次判断是否合法时都要传入当前要插入皇后的行列信息。

C++回溯问题框架(N皇后例子+决策树)_第3张图片

//检测列中是否有皇后冲突
bool isAvalid(const vector<string>& board, int row, int col)
{
     
	int n = board[row].size();
	//检测列中是否有皇后冲突
	for (int i = 0; i < row; i++)
	{
     
		if (board[i][col] == '*')
		{
     
			return false;
		}
	}
	//检测对角线是否合法
	for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++)
	{
     
		if (board[i][j] == '*')
		{
     
			return false;
		}
	}
	for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
	{
     
		if (board[i][j] == '*')
		{
     
			return false;
		}
	}
	return true;
}

4.C++N皇后源码

#include
#include
#include

using namespace std;

vector<vector<string>>ret;//存放结果的数组

bool isAvalid(const vector<string>& board, int row, int col)
{
     
	int n = board[row].size();
	//检测列中是否有皇后冲突
	for (int i = 0; i < row; i++)
	{
     
		if (board[i][col] == '*')
		{
     
			return false;
		}
	}
	//检测对角线是否合法
	for (int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++)
	{
     
		if (board[i][j] == '*')
		{
     
			return false;
		}
	}
	for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; i--, j--)
	{
     
		if (board[i][j] == '*')
		{
     
			return false;
		}
	}
	return true;
}

//行数
void backtrack(vector<string>& board, int row)
{
     
	//结束条件
	if (row == board.size())
	{
     
		ret.push_back(board);
		return;
	}
	int size = board[row].size();//选择列表的大小
	for (int col = 0; col < size; col++)//在选择列表中做选择
	{
     
		if (!isAvalid(board, row, col))//排除不合法选择
		{
     
			continue;
		}
		//做选择
		board[row][col] = '*';
		//进入下一行决策树
		backtrack(board, row + 1);
		//撤销选择
		board[row][col] = '_';
	}
}

void CoutNQueen(const int n)
{
     
	//初始化棋盘'_'代表空 '*'代表皇后
	vector<string>board(n, string(n, '_'));
	backtrack(board, 0);//回溯算法
}

int main()
{
     
	int n = 0;
	cout << "请输入皇后数量,皇后数量和棋盘的行列相同" << endl;
	cin >> n;
	CoutNQueen(n);
	//打印所有情况
	for (int i = 0; i < ret.size(); i++)
	{
     
		for (int j = 0; j < ret[i].size(); j++)
		{
     
			for (int k = 0; k < ret[i][j].size(); k++)
			{
     
				cout << ret[i][j][k] << "|";
			}
			cout << endl;
		}
		cout << endl;
	}
	cout << "一共有" << ret.size() << "种情况" << endl;
	return 0;
}

运行结果:
C++回溯问题框架(N皇后例子+决策树)_第4张图片

5.代码连接

GithUb链接
Gitee链接

你可能感兴趣的:(C++,数据结构与算法,LeetCode,c++,开发语言,后端)