mirror of
https://github.com/krahets/hello-algo.git
synced 2024-12-27 00:16:30 +08:00
build
This commit is contained in:
parent
7f43facfd9
commit
749918677f
5 changed files with 649 additions and 52 deletions
|
@ -63,19 +63,52 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="preorder_traversal_i_compact.go"
|
```go title="preorder_traversal_i_compact.go"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题一 */
|
||||||
|
func preOrderI(root *TreeNode, res *[]*TreeNode) {
|
||||||
|
if root == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if int(root.Val) == 7 {
|
||||||
|
// 记录解
|
||||||
|
*res = append(*res, root)
|
||||||
|
}
|
||||||
|
preOrderI(root.Left, res)
|
||||||
|
preOrderI(root.Right, res)
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="preorder_traversal_i_compact.js"
|
```javascript title="preorder_traversal_i_compact.js"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题一 */
|
||||||
|
function preOrder(root, res) {
|
||||||
|
if (root === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (root.val === 7) {
|
||||||
|
// 记录解
|
||||||
|
res.push(root);
|
||||||
|
}
|
||||||
|
preOrder(root.left, res);
|
||||||
|
preOrder(root.right, res);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="preorder_traversal_i_compact.ts"
|
```typescript title="preorder_traversal_i_compact.ts"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题一 */
|
||||||
|
function preOrder(root: TreeNode | null, res: TreeNode[]): void {
|
||||||
|
if (root === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (root.val === 7) {
|
||||||
|
// 记录解
|
||||||
|
res.push(root);
|
||||||
|
}
|
||||||
|
preOrder(root.left, res);
|
||||||
|
preOrder(root.right, res);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
@ -203,19 +236,68 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="preorder_traversal_ii_compact.go"
|
```go title="preorder_traversal_ii_compact.go"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题二 */
|
||||||
|
func preOrderII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
|
||||||
|
if root == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 尝试
|
||||||
|
*path = append(*path, root)
|
||||||
|
if int(root.Val) == 7 {
|
||||||
|
// 记录解
|
||||||
|
*res = append(*res, *path)
|
||||||
|
}
|
||||||
|
preOrderII(root.Left, res, path)
|
||||||
|
preOrderII(root.Right, res, path)
|
||||||
|
// 回退
|
||||||
|
*path = (*path)[:len(*path)-1]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="preorder_traversal_ii_compact.js"
|
```javascript title="preorder_traversal_ii_compact.js"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题二 */
|
||||||
|
function preOrder(root, path, res) {
|
||||||
|
if (root === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 尝试
|
||||||
|
path.push(root);
|
||||||
|
if (root.val === 7) {
|
||||||
|
// 记录解
|
||||||
|
res.push([...path]);
|
||||||
|
}
|
||||||
|
preOrder(root.left, path, res);
|
||||||
|
preOrder(root.right, path, res);
|
||||||
|
// 回退
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="preorder_traversal_ii_compact.ts"
|
```typescript title="preorder_traversal_ii_compact.ts"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题二 */
|
||||||
|
function preOrder(
|
||||||
|
root: TreeNode | null,
|
||||||
|
path: TreeNode[],
|
||||||
|
res: TreeNode[][]
|
||||||
|
): void {
|
||||||
|
if (root === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 尝试
|
||||||
|
path.push(root);
|
||||||
|
if (root.val === 7) {
|
||||||
|
// 记录解
|
||||||
|
res.push([...path]);
|
||||||
|
}
|
||||||
|
preOrder(root.left, path, res);
|
||||||
|
preOrder(root.right, path, res);
|
||||||
|
// 回退
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
@ -381,19 +463,71 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="preorder_traversal_iii_compact.go"
|
```go title="preorder_traversal_iii_compact.go"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题三 */
|
||||||
|
func preOrderIII(root *TreeNode, res *[][]*TreeNode, path *[]*TreeNode) {
|
||||||
|
// 剪枝
|
||||||
|
if root == nil || root.Val == 3 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 尝试
|
||||||
|
*path = append(*path, root)
|
||||||
|
if int(root.Val) == 7 {
|
||||||
|
// 记录解
|
||||||
|
*res = append(*res, *path)
|
||||||
|
}
|
||||||
|
preOrderIII(root.Left, res, path)
|
||||||
|
preOrderIII(root.Right, res, path)
|
||||||
|
// 回退
|
||||||
|
*path = (*path)[:len(*path)-1]
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="preorder_traversal_iii_compact.js"
|
```javascript title="preorder_traversal_iii_compact.js"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题三 */
|
||||||
|
function preOrder(root, path, res) {
|
||||||
|
// 剪枝
|
||||||
|
if (root === null || root.val === 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 尝试
|
||||||
|
path.push(root);
|
||||||
|
if (root.val === 7) {
|
||||||
|
// 记录解
|
||||||
|
res.push([...path]);
|
||||||
|
}
|
||||||
|
preOrder(root.left, path, res);
|
||||||
|
preOrder(root.right, path, res);
|
||||||
|
// 回退
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="preorder_traversal_iii_compact.ts"
|
```typescript title="preorder_traversal_iii_compact.ts"
|
||||||
[class]{}-[func]{preOrder}
|
/* 前序遍历:例题三 */
|
||||||
|
function preOrder(
|
||||||
|
root: TreeNode | null,
|
||||||
|
path: TreeNode[],
|
||||||
|
res: TreeNode[][]
|
||||||
|
): void {
|
||||||
|
// 剪枝
|
||||||
|
if (root === null || root.val === 3) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 尝试
|
||||||
|
path.push(root);
|
||||||
|
if (root.val === 7) {
|
||||||
|
// 记录解
|
||||||
|
res.push([...path]);
|
||||||
|
}
|
||||||
|
preOrder(root.left, path, res);
|
||||||
|
preOrder(root.right, path, res);
|
||||||
|
// 回退
|
||||||
|
path.pop();
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
@ -858,15 +992,30 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="preorder_traversal_iii_template.go"
|
```go title="preorder_traversal_iii_template.go"
|
||||||
[class]{}-[func]{isSolution}
|
/* 判断当前状态是否为解 */
|
||||||
|
func isSolution(state *[]*TreeNode) bool {
|
||||||
|
return len(*state) != 0 && (*state)[len(*state)-1].Val == 7
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{recordSolution}
|
/* 记录解 */
|
||||||
|
func recordSolution(state *[]*TreeNode, res *[][]*TreeNode) {
|
||||||
|
*res = append(*res, *state)
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{isValid}
|
/* 判断在当前状态下,该选择是否合法 */
|
||||||
|
func isValid(state *[]*TreeNode, choice *TreeNode) bool {
|
||||||
|
return choice != nil && choice.Val != 3
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{makeChoice}
|
/* 更新状态 */
|
||||||
|
func makeChoice(state *[]*TreeNode, choice *TreeNode) {
|
||||||
|
*state = append(*state, choice)
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{undoChoice}
|
/* 恢复状态 */
|
||||||
|
func undoChoice(state *[]*TreeNode, choice *TreeNode) {
|
||||||
|
*state = (*state)[:len(*state)-1]
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{backtrack}
|
[class]{}-[func]{backtrack}
|
||||||
```
|
```
|
||||||
|
@ -874,33 +1023,107 @@ comments: true
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="preorder_traversal_iii_template.js"
|
```javascript title="preorder_traversal_iii_template.js"
|
||||||
[class]{}-[func]{isSolution}
|
/* 判断当前状态是否为解 */
|
||||||
|
function isSolution(state) {
|
||||||
|
return state && state[state.length - 1]?.val === 7;
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{recordSolution}
|
/* 记录解 */
|
||||||
|
function recordSolution(state, res) {
|
||||||
|
res.push([...state]);
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{isValid}
|
/* 判断在当前状态下,该选择是否合法 */
|
||||||
|
function isValid(state, choice) {
|
||||||
|
return choice !== null && choice.val !== 3;
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{makeChoice}
|
/* 更新状态 */
|
||||||
|
function makeChoice(state, choice) {
|
||||||
|
state.push(choice);
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{undoChoice}
|
/* 恢复状态 */
|
||||||
|
function undoChoice(state) {
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:例题三 */
|
||||||
|
function backtrack(state, choices, res) {
|
||||||
|
// 检查是否为解
|
||||||
|
if (isSolution(state)) {
|
||||||
|
// 记录解
|
||||||
|
recordSolution(state, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
for (const choice of choices) {
|
||||||
|
// 剪枝:检查选择是否合法
|
||||||
|
if (isValid(state, choice)) {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
makeChoice(state, choice);
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrack(state, [choice.left, choice.right], res);
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
undoChoice(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="preorder_traversal_iii_template.ts"
|
```typescript title="preorder_traversal_iii_template.ts"
|
||||||
[class]{}-[func]{isSolution}
|
/* 判断当前状态是否为解 */
|
||||||
|
function isSolution(state: TreeNode[]): boolean {
|
||||||
|
return state && state[state.length - 1]?.val === 7;
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{recordSolution}
|
/* 记录解 */
|
||||||
|
function recordSolution(state: TreeNode[], res: TreeNode[][]): void {
|
||||||
|
res.push([...state]);
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{isValid}
|
/* 判断在当前状态下,该选择是否合法 */
|
||||||
|
function isValid(state: TreeNode[], choice: TreeNode): boolean {
|
||||||
|
return choice !== null && choice.val !== 3;
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{makeChoice}
|
/* 更新状态 */
|
||||||
|
function makeChoice(state: TreeNode[], choice: TreeNode): void {
|
||||||
|
state.push(choice);
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{undoChoice}
|
/* 恢复状态 */
|
||||||
|
function undoChoice(state: TreeNode[]): void {
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:例题三 */
|
||||||
|
function backtrack(
|
||||||
|
state: TreeNode[],
|
||||||
|
choices: TreeNode[],
|
||||||
|
res: TreeNode[][]
|
||||||
|
): void {
|
||||||
|
// 检查是否为解
|
||||||
|
if (isSolution(state)) {
|
||||||
|
// 记录解
|
||||||
|
recordSolution(state, res);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
for (const choice of choices) {
|
||||||
|
// 剪枝:检查选择是否合法
|
||||||
|
if (isValid(state, choice)) {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
makeChoice(state, choice);
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrack(state, [choice.left, choice.right], res);
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
undoChoice(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
|
|
@ -190,25 +190,180 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="n_queens.go"
|
```go title="n_queens.go"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:N 皇后 */
|
||||||
|
func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) {
|
||||||
|
// 当放置完所有行时,记录解
|
||||||
|
if row == n {
|
||||||
|
newState := make([][]string, len(*state))
|
||||||
|
for i, _ := range newState {
|
||||||
|
newState[i] = make([]string, len((*state)[0]))
|
||||||
|
copy(newState[i], (*state)[i])
|
||||||
|
|
||||||
[class]{}-[func]{nQueens}
|
}
|
||||||
|
*res = append(*res, newState)
|
||||||
|
}
|
||||||
|
// 遍历所有列
|
||||||
|
for col := 0; col < n; col++ {
|
||||||
|
// 计算该格子对应的主对角线和副对角线
|
||||||
|
diag1 := row - col + n - 1
|
||||||
|
diag2 := row + col
|
||||||
|
// 剪枝:不允许该格子所在 (列 或 主对角线 或 副对角线) 包含皇后
|
||||||
|
if !((*cols)[col] || (*diags1)[diag1] || (*diags2)[diag2]) {
|
||||||
|
// 尝试:将皇后放置在该格子
|
||||||
|
(*state)[row][col] = "Q"
|
||||||
|
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true
|
||||||
|
// 放置下一行
|
||||||
|
backtrack(row+1, n, state, res, cols, diags1, diags2)
|
||||||
|
// 回退:将该格子恢复为空位
|
||||||
|
(*state)[row][col] = "#"
|
||||||
|
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 回溯算法:N 皇后 */
|
||||||
|
func backtrack(row, n int, state *[][]string, res *[][][]string, cols, diags1, diags2 *[]bool) {
|
||||||
|
// 当放置完所有行时,记录解
|
||||||
|
if row == n {
|
||||||
|
newState := make([][]string, len(*state))
|
||||||
|
for i, _ := range newState {
|
||||||
|
newState[i] = make([]string, len((*state)[0]))
|
||||||
|
copy(newState[i], (*state)[i])
|
||||||
|
|
||||||
|
}
|
||||||
|
*res = append(*res, newState)
|
||||||
|
}
|
||||||
|
// 遍历所有列
|
||||||
|
for col := 0; col < n; col++ {
|
||||||
|
// 计算该格子对应的主对角线和副对角线
|
||||||
|
diag1 := row - col + n - 1
|
||||||
|
diag2 := row + col
|
||||||
|
// 剪枝:不允许该格子所在 (列 或 主对角线 或 副对角线) 包含皇后
|
||||||
|
if !((*cols)[col] || (*diags1)[diag1] || (*diags2)[diag2]) {
|
||||||
|
// 尝试:将皇后放置在该格子
|
||||||
|
(*state)[row][col] = "Q"
|
||||||
|
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = true, true, true
|
||||||
|
// 放置下一行
|
||||||
|
backtrack(row+1, n, state, res, cols, diags1, diags2)
|
||||||
|
// 回退:将该格子恢复为空位
|
||||||
|
(*state)[row][col] = "#"
|
||||||
|
(*cols)[col], (*diags1)[diag1], (*diags2)[diag2] = false, false, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func nQueens(n int) [][][]string {
|
||||||
|
// 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
|
||||||
|
state := make([][]string, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
row := make([]string, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
row[i] = "#"
|
||||||
|
}
|
||||||
|
state[i] = row
|
||||||
|
}
|
||||||
|
// 记录列是否有皇后
|
||||||
|
cols := make([]bool, n)
|
||||||
|
diags1 := make([]bool, 2*n-1)
|
||||||
|
diags2 := make([]bool, 2*n-1)
|
||||||
|
res := make([][][]string, 0)
|
||||||
|
backtrack(0, n, &state, &res, &cols, &diags1, &diags2)
|
||||||
|
return res
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="n_queens.js"
|
```javascript title="n_queens.js"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:N 皇后 */
|
||||||
|
function backtrack(row, n, state, res, cols, diags1, diags2) {
|
||||||
|
// 当放置完所有行时,记录解
|
||||||
|
if (row === n) {
|
||||||
|
res.push(state.map((row) => row.slice()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有列
|
||||||
|
for (let col = 0; col < n; col++) {
|
||||||
|
// 计算该格子对应的主对角线和副对角线
|
||||||
|
const diag1 = row - col + n - 1;
|
||||||
|
const 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{nQueens}
|
/* 求解 N 皇后 */
|
||||||
|
function nQueens(n) {
|
||||||
|
// 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
|
||||||
|
const state = Array.from({ length: n }, () => Array(n).fill('#'));
|
||||||
|
const cols = Array(n).fill(false); // 记录列是否有皇后
|
||||||
|
const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线是否有皇后
|
||||||
|
const diags2 = Array(2 * n - 1).fill(false); // 记录副对角线是否有皇后
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="n_queens.ts"
|
```typescript title="n_queens.ts"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:N 皇后 */
|
||||||
|
function backtrack(
|
||||||
|
row: number,
|
||||||
|
n: number,
|
||||||
|
state: string[][],
|
||||||
|
res: string[][][],
|
||||||
|
cols: boolean[],
|
||||||
|
diags1: boolean[],
|
||||||
|
diags2: boolean[]
|
||||||
|
): void {
|
||||||
|
// 当放置完所有行时,记录解
|
||||||
|
if (row === n) {
|
||||||
|
res.push(state.map((row) => row.slice()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有列
|
||||||
|
for (let col = 0; col < n; col++) {
|
||||||
|
// 计算该格子对应的主对角线和副对角线
|
||||||
|
const diag1 = row - col + n - 1;
|
||||||
|
const 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{nQueens}
|
/* 求解 N 皇后 */
|
||||||
|
function nQueens(n: number): string[][][] {
|
||||||
|
// 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
|
||||||
|
const state = Array.from({ length: n }, () => Array(n).fill('#'));
|
||||||
|
const cols = Array(n).fill(false); // 记录列是否有皇后
|
||||||
|
const diags1 = Array(2 * n - 1).fill(false); // 记录主对角线是否有皇后
|
||||||
|
const diags2 = Array(2 * n - 1).fill(false); // 记录副对角线是否有皇后
|
||||||
|
const res: string[][][] = [];
|
||||||
|
|
||||||
|
backtrack(0, n, state, res, cols, diags1, diags2);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
@ -278,9 +433,49 @@ comments: true
|
||||||
=== "Swift"
|
=== "Swift"
|
||||||
|
|
||||||
```swift title="n_queens.swift"
|
```swift title="n_queens.swift"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:N 皇后 */
|
||||||
|
func backtrack(row: Int, n: Int, state: inout [[String]], res: inout [[[String]]], cols: inout [Bool], diags1: inout [Bool], diags2: inout [Bool]) {
|
||||||
|
// 当放置完所有行时,记录解
|
||||||
|
if row == n {
|
||||||
|
res.append(state)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 遍历所有列
|
||||||
|
for col in 0 ..< n {
|
||||||
|
// 计算该格子对应的主对角线和副对角线
|
||||||
|
let diag1 = row - col + n - 1
|
||||||
|
let diag2 = row + col
|
||||||
|
// 剪枝:不允许该格子所在 (列 或 主对角线 或 副对角线) 包含皇后
|
||||||
|
if !(cols[col] || diags1[diag1] || diags2[diag2]) {
|
||||||
|
// 尝试:将皇后放置在该格子
|
||||||
|
state[row][col] = "Q"
|
||||||
|
cols[col] = true
|
||||||
|
diags1[diag1] = true
|
||||||
|
diags2[diag2] = true
|
||||||
|
// 放置下一行
|
||||||
|
backtrack(row: row + 1, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
|
||||||
|
// 回退:将该格子恢复为空位
|
||||||
|
state[row][col] = "#"
|
||||||
|
cols[col] = false
|
||||||
|
diags1[diag1] = false
|
||||||
|
diags2[diag2] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{nQueens}
|
/* 求解 N 皇后 */
|
||||||
|
func nQueens(n: Int) -> [[[String]]] {
|
||||||
|
// 初始化 n*n 大小的棋盘,其中 'Q' 代表皇后,'#' 代表空位
|
||||||
|
var state = Array(repeating: Array(repeating: "#", count: n), count: n)
|
||||||
|
var cols = Array(repeating: false, count: n) // 记录列是否有皇后
|
||||||
|
var diags1 = Array(repeating: false, count: 2 * n - 1) // 记录主对角线是否有皇后
|
||||||
|
var diags2 = Array(repeating: false, count: 2 * n - 1) // 记录副对角线是否有皇后
|
||||||
|
var res: [[[String]]] = []
|
||||||
|
|
||||||
|
backtrack(row: 0, n: n, state: &state, res: &res, cols: &cols, diags1: &diags1, diags2: &diags2)
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "Zig"
|
=== "Zig"
|
||||||
|
|
|
@ -140,25 +140,111 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="permutations_i.go"
|
```go title="permutations_i.go"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:全排列 I */
|
||||||
|
func backtrackI(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
|
||||||
|
// 当状态长度等于元素数量时,记录解
|
||||||
|
if len(*state) == len(*choices) {
|
||||||
|
newState := append([]int{}, *state...)
|
||||||
|
*res = append(*res, newState)
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
for i := 0; i < len(*choices); i++ {
|
||||||
|
choice := (*choices)[i]
|
||||||
|
// 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||||
|
if !(*selected)[i] {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
(*selected)[i] = true
|
||||||
|
*state = append(*state, choice)
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrackI(state, choices, selected, res)
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
(*selected)[i] = false
|
||||||
|
*state = (*state)[:len(*state)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{permutationsI}
|
/* 全排列 I */
|
||||||
|
func permutationsI(nums []int) [][]int {
|
||||||
|
res := make([][]int, 0)
|
||||||
|
state := make([]int, 0)
|
||||||
|
selected := make([]bool, len(nums))
|
||||||
|
backtrackI(&state, &nums, &selected, &res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="permutations_i.js"
|
```javascript title="permutations_i.js"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:全排列 I */
|
||||||
|
function backtrack(state, choices, selected, res) {
|
||||||
|
// 当状态长度等于元素数量时,记录解
|
||||||
|
if (state.length === choices.length) {
|
||||||
|
res.push([...state]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
choices.forEach((choice, i) => {
|
||||||
|
// 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||||
|
if (!selected[i]) {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
selected[i] = true;
|
||||||
|
state.push(choice);
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrack(state, choices, selected, res);
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
selected[i] = false;
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{permutationsI}
|
/* 全排列 I */
|
||||||
|
function permutationsI(nums) {
|
||||||
|
const res = [];
|
||||||
|
backtrack([], nums, Array(nums.length).fill(false), res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="permutations_i.ts"
|
```typescript title="permutations_i.ts"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:全排列 I */
|
||||||
|
function backtrack(
|
||||||
|
state: number[],
|
||||||
|
choices: number[],
|
||||||
|
selected: boolean[],
|
||||||
|
res: number[][]
|
||||||
|
): void {
|
||||||
|
// 当状态长度等于元素数量时,记录解
|
||||||
|
if (state.length === choices.length) {
|
||||||
|
res.push([...state]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
choices.forEach((choice, i) => {
|
||||||
|
// 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||||
|
if (!selected[i]) {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
selected[i] = true;
|
||||||
|
state.push(choice);
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrack(state, choices, selected, res);
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
selected[i] = false;
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{permutationsI}
|
/* 全排列 I */
|
||||||
|
function permutationsI(nums: number[]): number[][] {
|
||||||
|
const res: number[][] = [];
|
||||||
|
backtrack([], nums, Array(nums.length).fill(false), res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
@ -388,25 +474,118 @@ comments: true
|
||||||
=== "Go"
|
=== "Go"
|
||||||
|
|
||||||
```go title="permutations_ii.go"
|
```go title="permutations_ii.go"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:全排列 II */
|
||||||
|
func backtrackII(state *[]int, choices *[]int, selected *[]bool, res *[][]int) {
|
||||||
|
// 当状态长度等于元素数量时,记录解
|
||||||
|
if len(*state) == len(*choices) {
|
||||||
|
newState := append([]int{}, *state...)
|
||||||
|
*res = append(*res, newState)
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
duplicated := make(map[int]struct{}, 0)
|
||||||
|
for i := 0; i < len(*choices); i++ {
|
||||||
|
choice := (*choices)[i]
|
||||||
|
// 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||||
|
if _, ok := duplicated[choice]; !ok && !(*selected)[i] {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
// 记录选择过的元素值
|
||||||
|
duplicated[choice] = struct{}{}
|
||||||
|
(*selected)[i] = true
|
||||||
|
*state = append(*state, choice)
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrackI(state, choices, selected, res)
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
(*selected)[i] = false
|
||||||
|
*state = (*state)[:len(*state)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{permutationsII}
|
/* 全排列 II */
|
||||||
|
func permutationsII(nums []int) [][]int {
|
||||||
|
res := make([][]int, 0)
|
||||||
|
state := make([]int, 0)
|
||||||
|
selected := make([]bool, len(nums))
|
||||||
|
backtrackII(&state, &nums, &selected, &res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "JavaScript"
|
=== "JavaScript"
|
||||||
|
|
||||||
```javascript title="permutations_ii.js"
|
```javascript title="permutations_ii.js"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:全排列 II */
|
||||||
|
function backtrack(state, choices, selected, res) {
|
||||||
|
// 当状态长度等于元素数量时,记录解
|
||||||
|
if (state.length === choices.length) {
|
||||||
|
res.push([...state]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
const duplicated = new Set();
|
||||||
|
choices.forEach((choice, i) => {
|
||||||
|
// 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||||
|
if (!selected[i] && !duplicated.has(choice)) {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
duplicated.add(choice); // 记录选择过的元素值
|
||||||
|
selected[i] = true;
|
||||||
|
state.push(choice);
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrack(state, choices, selected, res);
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
selected[i] = false;
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{permutationsII}
|
/* 全排列 II */
|
||||||
|
function permutationsII(nums) {
|
||||||
|
const res = [];
|
||||||
|
backtrack([], nums, Array(nums.length).fill(false), res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "TypeScript"
|
=== "TypeScript"
|
||||||
|
|
||||||
```typescript title="permutations_ii.ts"
|
```typescript title="permutations_ii.ts"
|
||||||
[class]{}-[func]{backtrack}
|
/* 回溯算法:全排列 II */
|
||||||
|
function backtrack(
|
||||||
|
state: number[],
|
||||||
|
choices: number[],
|
||||||
|
selected: boolean[],
|
||||||
|
res: number[][]
|
||||||
|
): void {
|
||||||
|
// 当状态长度等于元素数量时,记录解
|
||||||
|
if (state.length === choices.length) {
|
||||||
|
res.push([...state]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 遍历所有选择
|
||||||
|
const duplicated = new Set();
|
||||||
|
choices.forEach((choice, i) => {
|
||||||
|
// 剪枝:不允许重复选择元素 且 不允许重复选择相等元素
|
||||||
|
if (!selected[i] && !duplicated.has(choice)) {
|
||||||
|
// 尝试:做出选择,更新状态
|
||||||
|
duplicated.add(choice); // 记录选择过的元素值
|
||||||
|
selected[i] = true;
|
||||||
|
state.push(choice);
|
||||||
|
// 进行下一轮选择
|
||||||
|
backtrack(state, choices, selected, res);
|
||||||
|
// 回退:撤销选择,恢复到之前的状态
|
||||||
|
selected[i] = false;
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[class]{}-[func]{permutationsII}
|
/* 全排列 II */
|
||||||
|
function permutationsII(nums: number[]): number[][] {
|
||||||
|
const res: number[][] = [];
|
||||||
|
backtrack([], nums, Array(nums.length).fill(false), res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "C"
|
=== "C"
|
||||||
|
|
|
@ -34,7 +34,7 @@ comments: true
|
||||||
// 1. 将数组元素分配到各个桶中
|
// 1. 将数组元素分配到各个桶中
|
||||||
for (float num : nums) {
|
for (float num : nums) {
|
||||||
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||||
int i = (int) num * k;
|
int i = (int) (num * k);
|
||||||
// 将 num 添加进桶 i
|
// 将 num 添加进桶 i
|
||||||
buckets.get(i).add(num);
|
buckets.get(i).add(num);
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ comments: true
|
||||||
// 1. 将数组元素分配到各个桶中
|
// 1. 将数组元素分配到各个桶中
|
||||||
for _, num := range nums {
|
for _, num := range nums {
|
||||||
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||||
i := int(num) * k
|
i := int(num * float64(k))
|
||||||
// 将 num 添加进桶 i
|
// 将 num 添加进桶 i
|
||||||
buckets[i] = append(buckets[i], num)
|
buckets[i] = append(buckets[i], num)
|
||||||
}
|
}
|
||||||
|
@ -229,7 +229,7 @@ comments: true
|
||||||
// 1. 将数组元素分配到各个桶中
|
// 1. 将数组元素分配到各个桶中
|
||||||
foreach (float num in nums) {
|
foreach (float num in nums) {
|
||||||
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||||
int i = (int)num * k;
|
int i = (int) (num * k);
|
||||||
// 将 num 添加进桶 i
|
// 将 num 添加进桶 i
|
||||||
buckets[i].Add(num);
|
buckets[i].Add(num);
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ comments: true
|
||||||
// 1. 将数组元素分配到各个桶中
|
// 1. 将数组元素分配到各个桶中
|
||||||
for num in nums {
|
for num in nums {
|
||||||
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
// 输入数据范围 [0, 1),使用 num * k 映射到索引范围 [0, k-1]
|
||||||
let i = Int(num) * k
|
let i = Int(num * k)
|
||||||
// 将 num 添加进桶 i
|
// 将 num 添加进桶 i
|
||||||
buckets[i].append(num)
|
buckets[i].append(num)
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ comments: true
|
||||||
func countingSortNaive(nums []int) {
|
func countingSortNaive(nums []int) {
|
||||||
// 1. 统计数组最大元素 m
|
// 1. 统计数组最大元素 m
|
||||||
m := 0
|
m := 0
|
||||||
for num := range nums {
|
for _, num := range nums {
|
||||||
if num > m {
|
if num > m {
|
||||||
m = num
|
m = num
|
||||||
}
|
}
|
||||||
|
@ -424,7 +424,7 @@ $$
|
||||||
func countingSort(nums []int) {
|
func countingSort(nums []int) {
|
||||||
// 1. 统计数组最大元素 m
|
// 1. 统计数组最大元素 m
|
||||||
m := 0
|
m := 0
|
||||||
for num := range nums {
|
for _, num := range nums {
|
||||||
if num > m {
|
if num > m {
|
||||||
m = num
|
m = num
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue