Skip to content

Commit 2a2da47

Browse files
committed
chapter_backtracking
1 parent 051c750 commit 2a2da47

3 files changed

Lines changed: 200 additions & 10 deletions

File tree

codes/c/.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Ignore all
22
*
33
# Unignore all with extensions
4-
#!*.*
4+
!*.*
55
# Unignore all dirs
66
!*/
77
*.dSYM/
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#include "../utils/common.h"
2+
3+
#define MAX_SIZE 100
4+
5+
/**
6+
* backtrack - 回溯算法解决N皇后问题的核心函数
7+
* @row: 当前正在处理的行号(从0开始)
8+
* @n: 棋盘的总行数/列数(即N皇后问题中的N)
9+
* @state: 当前棋盘状态的二维数组,'Q'表示皇后,'#'表示空
10+
* @res: 用于存储所有合法解的指针数组(需预先分配内存)
11+
* @resSize: 当前已找到的解的数量(指针,用于修改外部变量)
12+
* @cols: 布尔数组,标记各列是否已被皇后占用
13+
* @diags1: 布尔数组,标记各主对角线是否已被占用(索引公式:row - col + n - 1)
14+
* @diags2: 布尔数组,标记各次对角线是否已被占用(索引公式:row + col)
15+
*
16+
* 功能描述:
17+
* 1. 递归尝试在棋盘的每一行放置皇后,确保不互相攻击。
18+
* 2. 使用列、主对角线、次对角线的标记数组进行剪枝。
19+
* 3. 当找到一个合法解时,将其深拷贝到res中。
20+
*/
21+
/* 回溯算法:n 皇后 */
22+
void backtrack(int row, int n, char state[MAX_SIZE][MAX_SIZE], char ***res, int *resSize, bool cols[MAX_SIZE],
23+
bool diags1[2 * MAX_SIZE - 1], bool diags2[2 * MAX_SIZE - 1]) {
24+
// 当放置完所有行时,记录解
25+
if(row == n) {
26+
res[*resSize] = (char **)malloc(sizeof(char *) * n);
27+
for (int i = 0; i < n; ++i) {
28+
res[*resSize][i] = (char *)malloc(sizeof(char) * (n + 1));
29+
strcpy(res[*resSize][i], state[i]);
30+
}
31+
(*resSize)++;
32+
return;
33+
}
34+
// 遍历所有列
35+
for (int col = 0; col < n; col++) {
36+
// 计算该格子对应的主对角线和次对角线
37+
int diag1 = row - col + n - 1;
38+
int diag2 = row + col;
39+
// 剪枝:不允许该格子所在列、主对角线、次对角线上存在皇后
40+
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
41+
// 尝试:将皇后放置在该格子
42+
state[row][col] = 'Q';
43+
cols[col] = diags1[diag1] = diags2[diag2] = true;
44+
// 放置下一行
45+
backtrack(row + 1, n, state, res, resSize, cols, diags1, diags2);
46+
// 回退:将该格子恢复为空
47+
state[row][col] = '#';
48+
cols[col] = diags1[diag1] = diags2[diag2] = false;
49+
}
50+
}
51+
}
52+
53+
/**
54+
* nQueens - 求解N皇后问题的入口函数
55+
* @n: 棋盘的大小(N x N)及皇后的数量
56+
* @returnSize: 返回解的数量的指针(输出参数)
57+
*
58+
* 功能描述:
59+
* 1. 初始化棋盘状态和标记数组
60+
* 2. 分配结果存储空间
61+
* 3. 调用回溯算法求解所有合法布局
62+
* 4. 返回解数组
63+
*
64+
* 返回值:
65+
* 三维字符数组指针,存储所有合法的棋盘布局。
66+
* 每个解是一个二维字符数组,表示一个棋盘状态。
67+
* 调用者需负责释放返回的内存。
68+
*
69+
* 注意:
70+
* - 使用MAX_SIZE宏限制最大棋盘尺寸
71+
* - 棋盘初始化为全'#'(空位)
72+
* - 对角线数组大小为2n-1以覆盖所有可能对角线
73+
*/
74+
/* 求解 n 皇后 */
75+
char ***nQueens(int n, int *returnSize) {
76+
char state[MAX_SIZE][MAX_SIZE];
77+
// 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
78+
for (int i = 0; i < n; ++i) {
79+
for (int j = 0; j < n; ++j) {
80+
state[i][j] = '#';
81+
}
82+
state[i][n] = '\0';
83+
}
84+
85+
bool cols[MAX_SIZE] = {false}; // 记录列是否有皇后
86+
bool diags1[2 * MAX_SIZE - 1] = {false}; // 记录主对角线上是否有皇后
87+
bool diags2[2 * MAX_SIZE - 1] = {false}; // 记录次对角线上是否有皇后
88+
89+
char ***res = (char ***)malloc(sizeof(char **) * MAX_SIZE);
90+
*returnSize = 0;
91+
backtrack(0, n, state, res, returnSize, cols, diags1, diags2);
92+
return res;
93+
}
94+
95+
/* Driver Code */
96+
int main(int argc, char **argv) {
97+
// int n = 4;
98+
if (argc != 2) {
99+
fprintf(stderr, "Usage: %s <n>\n", argv[0]); // 使用 argv[0] 显示程序名
100+
return 1;
101+
}
102+
103+
int n = atoi(argv[1]); // 使用 argv[1] 获取用户输入
104+
if (n <= 0) {
105+
fprintf(stderr, "Error: n must be positive\n");
106+
return 1;
107+
}
108+
109+
int returnSize;
110+
char ***res = nQueens(n, &returnSize);
111+
112+
printf("输入棋盘长宽为%d\n", n);
113+
printf("皇后放置方案共有 %d 种\n", returnSize);
114+
115+
for(int i = 0; i < returnSize; ++i) {
116+
for (int j = 0; j < n; ++j){
117+
printf("[");
118+
for (int k = 0; res[i][j][k] != '\0'; ++k) {
119+
printf("%c", res[i][j][k]);
120+
if (res[i][j][k + 1] != '\0') {
121+
printf(", ");
122+
}
123+
}
124+
printf("]\n");
125+
}
126+
printf("---------------------\n");
127+
}
128+
129+
// 释放内存
130+
for (int i = 0; i < returnSize; ++i) {
131+
for (int j = 0; j < n; ++j) {
132+
free(res[i][j]);
133+
}
134+
free(res[i]);
135+
}
136+
free(res);
137+
138+
return 0;
139+
}
Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,65 @@
1-
void backtrack(State *state, Choice *choices, int numChoices, State *res, int numRes) {
2-
if (isSolution(state)) {
3-
recordSolution(state, res, numRes);
1+
/**
2+
* backtrack - 回溯算法实现全排列 I
3+
* @state: 当前排列状态数组
4+
* @stateSize: 当前排列状态的长度
5+
* @choices: 可选的数字数组
6+
* @choicesSize: 可选数字的数量
7+
* @selected: 标记数组,记录数字是否已被选择
8+
* @res: 存储所有排列结果的二维数组
9+
* @resSize: 当前已找到的排列数量(指针,用于修改外部变量)
10+
*
11+
* 该函数使用回溯算法生成所有可能的排列组合,通过递归和剪枝避免重复选择。
12+
* 当排列长度等于输入数组长度时,记录当前排列。
13+
*/
14+
/* 回溯算法:全排列 I */
15+
void backtrack(int *state, int stateSize, int *choices, int choicesSize, bool *selected, int **res, int *resSize) {
16+
// 当状态长度等于元素数量时,记录解
17+
if (stateSize == choicesSize) {
18+
res[*resSize] = (int *)malloc(choicesSize * sizeof(int));
19+
for (int i = 0; i < choicesSize; i++) {
20+
res[*resSize][i] = state[i];
21+
}
22+
(*resSize)++;
423
return;
524
}
6-
7-
for (int i = 0; i < numChoices; i++) {
8-
if (isValid(state, &chioces[i])) {
9-
makeChoice(state, &chioces[i]);
10-
backtrack(state, choices, numChoices, res, numRes);
11-
undoChoice(state, &choices[i]);
25+
// 遍历所有选择
26+
for (int i = 0; i <choicesSize; i++) {
27+
int choice = choices[i];
28+
// 剪枝:不允许重复选择元素
29+
if (!selected[i]){
30+
// 尝试:做出选择,更新状态
31+
selected[i] = true;
32+
state[stateSize] = choice;
33+
// 进行下一轮选择
34+
backtrack(state, stateSize + 1, choices, choicesSize, selected, res, resSize);
35+
// 回退:撤销选择,恢复到之前的状态
36+
selected[i] = false;
1237
}
1338
}
1439
}
40+
41+
/**
42+
* permutationsI - 生成输入数组的所有全排列
43+
* @nums: 输入数字数组
44+
* @numsSize: 输入数组的长度
45+
* @returnSize: 返回结果的数量(指针,用于修改外部变量)
46+
*
47+
* 返回值: 存储所有排列结果的二维数组,需由调用者释放内存。
48+
* 使用回溯算法生成全排列,避免重复排列。
49+
*/
50+
/* 全排列 I */
51+
int **permutationsI(int *nums, int numsSize, int *returnSize) {
52+
int *state = (int *)malloc(numsSize * sizeof(int));
53+
bool *selected = (bool *)malloc(numsSize * sizeof(bool));
54+
for (int i = 0; i < numsSize; i++) {
55+
selected[i] = false;
56+
}
57+
int **res = (int **)malloc(MAX_SIZE * sizeof(int *));
58+
*resSize = 0;
59+
backtrack(state, 0, nums, numsSize, selected, res, returnSize);
60+
61+
free(state);
62+
free(selected);
63+
64+
return res;
65+
}

0 commit comments

Comments
 (0)