build
This commit is contained in:
parent
2f761d9865
commit
ec3f58fb70
@ -601,9 +601,66 @@ comments: true
|
||||
=== "C"
|
||||
|
||||
```c title="n_queens.c"
|
||||
[class]{}-[func]{backtrack}
|
||||
/* 放置结果 */
|
||||
struct result {
|
||||
char ***data;
|
||||
int size;
|
||||
};
|
||||
|
||||
[class]{}-[func]{nQueens}
|
||||
typedef struct result Result;
|
||||
|
||||
/* 回溯算法:N 皇后 */
|
||||
void backtrack(int row, int n, char state[MAX_N][MAX_N], Result *res,
|
||||
bool cols[MAX_N], bool diags1[2 * MAX_N - 1], bool diags2[2 * MAX_N - 1]) {
|
||||
// 当放置完所有行时,记录解
|
||||
if (row == n) {
|
||||
res->data[res->size] = (char **)malloc(sizeof(char *) * n);
|
||||
for (int i = 0; i < n; ++i) {
|
||||
res->data[res->size][i] = (char *)malloc(sizeof(char) * (n + 1));
|
||||
strcpy(res->data[res->size][i], state[i]);
|
||||
}
|
||||
res->size++;
|
||||
return;
|
||||
}
|
||||
// 遍历所有列
|
||||
for (int col = 0; col < n; col++) {
|
||||
// 计算该格子对应的主对角线和副对角线
|
||||
int diag1 = row - col + n - 1;
|
||||
int diag2 = row + col;
|
||||
// 剪枝:不允许该格子所在列、主对角线、副对角线存在皇后
|
||||
if (!cols[col] && !diags1[diag1] && !diags2[diag2]) {
|
||||
// 尝试:将皇后放置在该格子
|
||||
state[row][col] = 'Q';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = true;
|
||||
// 放置下一行
|
||||
backtrack(row + 1, n, state, res, cols, diags1, diags2);
|
||||
// 回退:将该格子恢复为空位
|
||||
state[row][col] = '#';
|
||||
cols[col] = diags1[diag1] = diags2[diag2] = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 求解 N 皇后 */
|
||||
Result *nQueens(int n) {
|
||||
char state[MAX_N][MAX_N];
|
||||
// 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
|
||||
for (int i = 0; i < n; ++i) {
|
||||
for (int j = 0; j < n; ++j) {
|
||||
state[i][j] = '#';
|
||||
}
|
||||
state[i][n] = '\0';
|
||||
}
|
||||
bool cols[MAX_N] = {false}; // 记录列是否有皇后
|
||||
bool diags1[2 * MAX_N - 1] = {false}; // 记录主对角线是否有皇后
|
||||
bool diags2[2 * MAX_N - 1] = {false}; // 记录副对角线是否有皇后
|
||||
|
||||
Result *res = malloc(sizeof(Result));
|
||||
res->data = (char ***)malloc(sizeof(char **) * MAX_RES);
|
||||
res->size = 0;
|
||||
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||
return res;
|
||||
}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
|
@ -936,7 +936,7 @@ comments: true
|
||||
|
||||
为解决此问题,**我们需要限制相等元素在每一轮中只被选择一次**。实现方式比较巧妙:由于数组是已排序的,因此相等元素都是相邻的。这意味着在某轮选择中,若当前元素与其左边元素相等,则说明它已经被选择过,因此直接跳过当前元素。
|
||||
|
||||
与此同时,**本题规定中的每个数组元素只能被选择一次**。幸运的是,我们也可以利用变量 `start` 来满足该约束:当做出选择 $x_{i}$ 后,设定下一轮从索引 $i + 1$ 开始向后遍历。这样即能去除重复子集,也能避免重复选择元素。
|
||||
与此同时,**本题规定数组中的每个元素只能被选择一次**。幸运的是,我们也可以利用变量 `start` 来满足该约束:当做出选择 $x_{i}$ 后,设定下一轮从索引 $i + 1$ 开始向后遍历。这样即能去除重复子集,也能避免重复选择元素。
|
||||
|
||||
### 2. 代码实现
|
||||
|
||||
|
@ -443,7 +443,7 @@ $$
|
||||
相较于直接统计算法运行时间,时间复杂度分析有哪些特点呢?
|
||||
|
||||
- **时间复杂度能够有效评估算法效率**。例如,算法 `B` 的运行时间呈线性增长,在 $n > 1$ 时比算法 `A` 更慢,在 $n > 1000000$ 时比算法 `C` 更慢。事实上,只要输入数据大小 $n$ 足够大,复杂度为“常数阶”的算法一定优于“线性阶”的算法,这正是时间增长趋势所表达的含义。
|
||||
- **时间复杂度的推算方法更简便**。显然,运行平台和计算操作类型都与算法运行时间的增长趋势无关。因此在时间复杂度分析中,我们可以简单地将所有计算操作的执行时间视为相同的“单位时间”,从而将“计算操作的运行时间的统计”简化为“计算操作的数量的统计”,这样以来估算难度就大大降低了。
|
||||
- **时间复杂度的推算方法更简便**。显然,运行平台和计算操作类型都与算法运行时间的增长趋势无关。因此在时间复杂度分析中,我们可以简单地将所有计算操作的执行时间视为相同的“单位时间”,从而将“计算操作的运行时间的统计”简化为“计算操作的数量的统计”,这样一来估算难度就大大降低了。
|
||||
- **时间复杂度也存在一定的局限性**。例如,尽管算法 `A` 和 `C` 的时间复杂度相同,但实际运行时间差别很大。同样,尽管算法 `B` 的时间复杂度比 `C` 高,但在输入数据大小 $n$ 较小时,算法 `B` 明显优于算法 `C` 。在这些情况下,我们很难仅凭时间复杂度判断算法效率的高低。当然,尽管存在上述问题,复杂度分析仍然是评判算法效率最有效且常用的方法。
|
||||
|
||||
## 2.3.2 函数渐近上界
|
||||
@ -2597,8 +2597,7 @@ $$
|
||||
int linearLogRecur(float n) {
|
||||
if (n <= 1)
|
||||
return 1;
|
||||
int count = linearLogRecur(n / 2) +
|
||||
linearLogRecur(n / 2);
|
||||
int count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
|
||||
for (int i = 0; i < n; i++) {
|
||||
count++;
|
||||
}
|
||||
@ -2612,8 +2611,7 @@ $$
|
||||
/* 线性对数阶 */
|
||||
int LinearLogRecur(float n) {
|
||||
if (n <= 1) return 1;
|
||||
int count = LinearLogRecur(n / 2) +
|
||||
LinearLogRecur(n / 2);
|
||||
int count = LinearLogRecur(n / 2) + LinearLogRecur(n / 2);
|
||||
for (int i = 0; i < n; i++) {
|
||||
count++;
|
||||
}
|
||||
@ -2629,8 +2627,7 @@ $$
|
||||
if n <= 1 {
|
||||
return 1
|
||||
}
|
||||
count := linearLogRecur(n/2) +
|
||||
linearLogRecur(n/2)
|
||||
count := linearLogRecur(n/2) + linearLogRecur(n/2)
|
||||
for i := 0.0; i < n; i++ {
|
||||
count++
|
||||
}
|
||||
@ -2704,8 +2701,7 @@ $$
|
||||
if n <= 1.0 {
|
||||
return 1;
|
||||
}
|
||||
let mut count = linear_log_recur(n / 2.0) +
|
||||
linear_log_recur(n / 2.0);
|
||||
let mut count = linear_log_recur(n / 2.0) + linear_log_recur(n / 2.0);
|
||||
for _ in 0 ..n as i32 {
|
||||
count += 1;
|
||||
}
|
||||
@ -2734,8 +2730,7 @@ $$
|
||||
// 线性对数阶
|
||||
fn linearLogRecur(n: f32) i32 {
|
||||
if (n <= 1) return 1;
|
||||
var count: i32 = linearLogRecur(n / 2) +
|
||||
linearLogRecur(n / 2);
|
||||
var count: i32 = linearLogRecur(n / 2) + linearLogRecur(n / 2);
|
||||
var i: f32 = 0;
|
||||
while (i < n) : (i += 1) {
|
||||
count += 1;
|
||||
@ -3060,11 +3055,8 @@ $$
|
||||
|
||||
// 随机打乱数组元素
|
||||
for (int i = 0; i < nums.Length; i++) {
|
||||
var index = new Random().Next(i, nums.Length);
|
||||
var tmp = nums[i];
|
||||
var ran = nums[index];
|
||||
nums[i] = ran;
|
||||
nums[index] = tmp;
|
||||
int index = new Random().Next(i, nums.Length);
|
||||
(nums[i], nums[index]) = (nums[index], nums[i]);
|
||||
}
|
||||
return nums;
|
||||
}
|
||||
|
@ -809,7 +809,26 @@ $$
|
||||
=== "C"
|
||||
|
||||
```c title="climbing_stairs_constraint_dp.c"
|
||||
[class]{}-[func]{climbingStairsConstraintDP}
|
||||
/* 带约束爬楼梯:动态规划 */
|
||||
int climbingStairsConstraintDP(int n) {
|
||||
if (n == 1 || n == 2) {
|
||||
return 1;
|
||||
}
|
||||
// 初始化 dp 表,用于存储子问题的解
|
||||
int dp[n + 1][3];
|
||||
memset(dp, 0, sizeof(dp));
|
||||
// 初始状态:预设最小子问题的解
|
||||
dp[1][1] = 1;
|
||||
dp[1][2] = 0;
|
||||
dp[2][1] = 0;
|
||||
dp[2][2] = 1;
|
||||
// 状态转移:从较小子问题逐步求解较大子问题
|
||||
for (int i = 3; i <= n; i++) {
|
||||
dp[i][1] = dp[i - 1][2];
|
||||
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
|
||||
}
|
||||
return dp[n][1] + dp[n][2];
|
||||
}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
|
@ -391,7 +391,31 @@ $$
|
||||
=== "C"
|
||||
|
||||
```c title="edit_distance.c"
|
||||
[class]{}-[func]{editDistanceDP}
|
||||
/* 编辑距离:动态规划 */
|
||||
int editDistanceDP(char *s, char *t, int n, int m) {
|
||||
int dp[n + 1][m + 1];
|
||||
memset(dp, 0, sizeof(dp));
|
||||
// 状态转移:首行首列
|
||||
for (int i = 1; i <= n; i++) {
|
||||
dp[i][0] = i;
|
||||
}
|
||||
for (int j = 1; j <= m; j++) {
|
||||
dp[0][j] = j;
|
||||
}
|
||||
// 状态转移:其余行列
|
||||
for (int i = 1; i <= n; i++) {
|
||||
for (int j = 1; j <= m; j++) {
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 若两字符相等,则直接跳过此两字符
|
||||
dp[i][j] = dp[i - 1][j - 1];
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return dp[n][m];
|
||||
}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
@ -812,7 +836,34 @@ $$
|
||||
=== "C"
|
||||
|
||||
```c title="edit_distance.c"
|
||||
[class]{}-[func]{editDistanceDPComp}
|
||||
/* 编辑距离:空间优化后的动态规划 */
|
||||
int editDistanceDPComp(char *s, char *t, int n, int m) {
|
||||
int dp[m + 1];
|
||||
memset(dp, 0, sizeof(dp));
|
||||
// 状态转移:首行
|
||||
for (int j = 1; j <= m; j++) {
|
||||
dp[j] = j;
|
||||
}
|
||||
// 状态转移:其余行
|
||||
for (int i = 1; i <= n; i++) {
|
||||
// 状态转移:首列
|
||||
int leftup = dp[0]; // 暂存 dp[i-1, j-1]
|
||||
dp[0] = i;
|
||||
// 状态转移:其余列
|
||||
for (int j = 1; j <= m; j++) {
|
||||
int temp = dp[j];
|
||||
if (s[i - 1] == t[j - 1]) {
|
||||
// 若两字符相等,则直接跳过此两字符
|
||||
dp[j] = leftup;
|
||||
} else {
|
||||
// 最少编辑步数 = 插入、删除、替换这三种操作的最少编辑步数 + 1
|
||||
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1;
|
||||
}
|
||||
leftup = temp; // 更新为下一轮的 dp[i-1, j-1]
|
||||
}
|
||||
}
|
||||
return dp[m];
|
||||
}
|
||||
```
|
||||
|
||||
=== "Zig"
|
||||
|
@ -178,7 +178,7 @@ comments: true
|
||||
|
||||
```javascript title="hash_map.js"
|
||||
/* 初始化哈希表 */
|
||||
const map = new ArrayHashMap();
|
||||
const map = new Map();
|
||||
/* 添加操作 */
|
||||
// 在哈希表中添加键值对 (key, value)
|
||||
map.set(12836, '小哈');
|
||||
@ -349,7 +349,7 @@ comments: true
|
||||
Console.WriteLine(key);
|
||||
}
|
||||
// 单独遍历值 value
|
||||
foreach (String val in map.Values) {
|
||||
foreach (string val in map.Values) {
|
||||
Console.WriteLine(val);
|
||||
}
|
||||
```
|
||||
|
@ -10,7 +10,7 @@ comments: true
|
||||
- 对于二叉树中的某个节点,其左(右)子节点及其以下形成的树被称为该节点的左(右)子树。
|
||||
- 二叉树的相关术语包括根节点、叶节点、层、度、边、高度和深度等。
|
||||
- 二叉树的初始化、节点插入和节点删除操作与链表操作方法类似。
|
||||
- 常见的二叉树类型有完美二叉树、完全二叉树、满二叉树和平衡二叉树。完美二叉树是最理想的状态,而链表是退化后的最差状态。
|
||||
- 常见的二叉树类型有完美二叉树、完全二叉树、完满二叉树和平衡二叉树。完美二叉树是最理想的状态,而链表是退化后的最差状态。
|
||||
- 二叉树可以用数组表示,方法是将节点值和空位按层序遍历顺序排列,并根据父节点与子节点之间的索引映射关系来实现指针。
|
||||
- 二叉树的层序遍历是一种广度优先搜索方法,它体现了“一圈一圈向外”的分层遍历方式,通常通过队列来实现。
|
||||
- 前序、中序、后序遍历皆属于深度优先搜索,它们体现了“走到尽头,再回头继续”的回溯遍历方式,通常使用递归来实现。
|
||||
|
Loading…
Reference in New Issue
Block a user