Many bug fixes and improvements (#1270)

* Add Ruby and Kotlin icons
Add the avatar of @curtishd

* Update README

* Synchronize zh-hant and zh versions.

* Translate the pythontutor blocks to traditional Chinese

* Fix en/mkdocs.yml

* Update the landing page of the en version.

* Fix the Dockerfile

* Refine the en landingpage

* Fix en landing page

* Reset the README.md
This commit is contained in:
Yudong Jin
2024-04-11 20:18:19 +08:00
committed by GitHub
parent 07977184ad
commit b2f0d4603d
192 changed files with 2382 additions and 1196 deletions

View File

@@ -8,13 +8,14 @@ package chapter_dynamic_programming
/* 回溯 */
fun backtrack(
choices: List<Int>,
choices: MutableList<Int>,
state: Int,
n: Int,
res: MutableList<Int>
) {
// 當爬到第 n 階時,方案數量加 1
if (state == n) res[0] = res[0] + 1
if (state == n)
res[0] = res[0] + 1
// 走訪所有選擇
for (choice in choices) {
// 剪枝:不允許越過第 n 階
@@ -29,7 +30,7 @@ fun backtrack(
fun climbingStairsBacktrack(n: Int): Int {
val choices = mutableListOf(1, 2) // 可選擇向上爬 1 階或 2 階
val state = 0 // 從第 0 階開始爬
val res = ArrayList<Int>()
val res = mutableListOf<Int>()
res.add(0) // 使用 res[0] 記錄方案數量
backtrack(choices, state, n, res)
return res[0]

View File

@@ -6,8 +6,6 @@
package chapter_dynamic_programming
import java.util.*
/* 記憶化搜尋 */
fun dfs(i: Int, mem: IntArray): Int {
// 已知 dp[1] 和 dp[2] ,返回之
@@ -25,7 +23,7 @@ fun dfs(i: Int, mem: IntArray): Int {
fun climbingStairsDFSMem(n: Int): Int {
// mem[i] 記錄爬到第 i 階的方案總數,-1 代表無記錄
val mem = IntArray(n + 1)
Arrays.fill(mem, -1)
mem.fill(-1)
return dfs(n, mem)
}
@@ -33,6 +31,6 @@ fun climbingStairsDFSMem(n: Int): Int {
fun main() {
val n = 9
val res: Int = climbingStairsDFSMem(n)
val res = climbingStairsDFSMem(n)
println("$n 階樓梯共有 $res 種方案")
}

View File

@@ -27,9 +27,7 @@ fun climbingStairsDPComp(n: Int): Int {
var a = 1
var b = 2
for (i in 3..n) {
val tmp = b
b += a
a = tmp
b += a.also { a = b }
}
return b
}

View File

@@ -6,7 +6,6 @@
package chapter_dynamic_programming
import java.util.*
import kotlin.math.min
/* 零錢兌換:動態規劃 */
@@ -27,8 +26,7 @@ fun coinChangeDP(coins: IntArray, amt: Int): Int {
dp[i][a] = dp[i - 1][a]
} else {
// 不選和選硬幣 i 這兩種方案的較小值
dp[i][a] = min(dp[i - 1][a].toDouble(), (dp[i][a - coins[i - 1]] + 1).toDouble())
.toInt()
dp[i][a] = min(dp[i - 1][a], dp[i][a - coins[i - 1]] + 1)
}
}
}
@@ -41,7 +39,7 @@ fun coinChangeDPComp(coins: IntArray, amt: Int): Int {
val MAX = amt + 1
// 初始化 dp 表
val dp = IntArray(amt + 1)
Arrays.fill(dp, MAX)
dp.fill(MAX)
dp[0] = 0
// 狀態轉移
for (i in 1..n) {
@@ -51,7 +49,7 @@ fun coinChangeDPComp(coins: IntArray, amt: Int): Int {
dp[a] = dp[a]
} else {
// 不選和選硬幣 i 這兩種方案的較小值
dp[a] = min(dp[a].toDouble(), (dp[a - coins[i - 1]] + 1).toDouble()).toInt()
dp[a] = min(dp[a], dp[a - coins[i - 1]] + 1)
}
}
}

View File

@@ -6,7 +6,6 @@
package chapter_dynamic_programming
import java.util.*
import kotlin.math.min
/* 編輯距離:暴力搜尋 */
@@ -29,7 +28,7 @@ fun editDistanceDFS(
val delete = editDistanceDFS(s, t, i - 1, j)
val replace = editDistanceDFS(s, t, i - 1, j - 1)
// 返回最少編輯步數
return (min(min(insert.toDouble(), delete.toDouble()), replace.toDouble()) + 1).toInt()
return min(min(insert, delete), replace) + 1
}
/* 編輯距離:記憶化搜尋 */
@@ -55,7 +54,7 @@ fun editDistanceDFSMem(
val delete = editDistanceDFSMem(s, t, mem, i - 1, j)
val replace = editDistanceDFSMem(s, t, mem, i - 1, j - 1)
// 記錄並返回最少編輯步數
mem[i][j] = (min(min(insert.toDouble(), delete.toDouble()), replace.toDouble()) + 1).toInt()
mem[i][j] = min(min(insert, delete), replace) + 1
return mem[i][j]
}
@@ -79,11 +78,7 @@ fun editDistanceDP(s: String, t: String): Int {
dp[i][j] = dp[i - 1][j - 1]
} else {
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
dp[i][j] =
(min(
min(dp[i][j - 1].toDouble(), dp[i - 1][j].toDouble()),
dp[i - 1][j - 1].toDouble()
) + 1).toInt()
dp[i][j] = min(min(dp[i][j - 1], dp[i - 1][j]), dp[i - 1][j - 1]) + 1
}
}
}
@@ -112,7 +107,7 @@ fun editDistanceDPComp(s: String, t: String): Int {
dp[j] = leftup
} else {
// 最少編輯步數 = 插入、刪除、替換這三種操作的最少編輯步數 + 1
dp[j] = (min(min(dp[j - 1].toDouble(), dp[j].toDouble()), leftup.toDouble()) + 1).toInt()
dp[j] = min(min(dp[j - 1], dp[j]), leftup) + 1
}
leftup = temp // 更新為下一輪的 dp[i-1, j-1]
}
@@ -133,7 +128,8 @@ fun main() {
// 記憶化搜尋
val mem = Array(n + 1) { IntArray(m + 1) }
for (row in mem) Arrays.fill(row, -1)
for (row in mem)
row.fill(-1)
res = editDistanceDFSMem(s, t, mem, n, m)
println("$s 更改為 $t 最少需要編輯 $res")

View File

@@ -6,13 +6,12 @@
package chapter_dynamic_programming
import java.util.*
import kotlin.math.max
/* 0-1 背包:暴力搜尋 */
fun knapsackDFS(
wgt: IntArray,
value: IntArray,
_val: IntArray,
i: Int,
c: Int
): Int {
@@ -22,19 +21,19 @@ fun knapsackDFS(
}
// 若超過背包容量,則只能選擇不放入背包
if (wgt[i - 1] > c) {
return knapsackDFS(wgt, value, i - 1, c)
return knapsackDFS(wgt, _val, i - 1, c)
}
// 計算不放入和放入物品 i 的最大價值
val no = knapsackDFS(wgt, value, i - 1, c)
val yes = knapsackDFS(wgt, value, i - 1, c - wgt[i - 1]) + value[i - 1]
val no = knapsackDFS(wgt, _val, i - 1, c)
val yes = knapsackDFS(wgt, _val, i - 1, c - wgt[i - 1]) + _val[i - 1]
// 返回兩種方案中價值更大的那一個
return max(no.toDouble(), yes.toDouble()).toInt()
return max(no, yes)
}
/* 0-1 背包:記憶化搜尋 */
fun knapsackDFSMem(
wgt: IntArray,
value: IntArray,
_val: IntArray,
mem: Array<IntArray>,
i: Int,
c: Int
@@ -49,20 +48,20 @@ fun knapsackDFSMem(
}
// 若超過背包容量,則只能選擇不放入背包
if (wgt[i - 1] > c) {
return knapsackDFSMem(wgt, value, mem, i - 1, c)
return knapsackDFSMem(wgt, _val, mem, i - 1, c)
}
// 計算不放入和放入物品 i 的最大價值
val no = knapsackDFSMem(wgt, value, mem, i - 1, c)
val yes = knapsackDFSMem(wgt, value, mem, i - 1, c - wgt[i - 1]) + value[i - 1]
val no = knapsackDFSMem(wgt, _val, mem, i - 1, c)
val yes = knapsackDFSMem(wgt, _val, mem, i - 1, c - wgt[i - 1]) + _val[i - 1]
// 記錄並返回兩種方案中價值更大的那一個
mem[i][c] = max(no.toDouble(), yes.toDouble()).toInt()
mem[i][c] = max(no, yes)
return mem[i][c]
}
/* 0-1 背包:動態規劃 */
fun knapsackDP(
wgt: IntArray,
value: IntArray,
_val: IntArray,
cap: Int
): Int {
val n = wgt.size
@@ -76,8 +75,7 @@ fun knapsackDP(
dp[i][c] = dp[i - 1][c]
} else {
// 不選和選物品 i 這兩種方案的較大值
dp[i][c] = max(dp[i - 1][c].toDouble(), (dp[i - 1][c - wgt[i - 1]] + value[i - 1]).toDouble())
.toInt()
dp[i][c] = max(dp[i - 1][c], dp[i - 1][c - wgt[i - 1]] + _val[i - 1])
}
}
}
@@ -87,7 +85,7 @@ fun knapsackDP(
/* 0-1 背包:空間最佳化後的動態規劃 */
fun knapsackDPComp(
wgt: IntArray,
value: IntArray,
_val: IntArray,
cap: Int
): Int {
val n = wgt.size
@@ -100,7 +98,7 @@ fun knapsackDPComp(
if (wgt[i - 1] <= c) {
// 不選和選物品 i 這兩種方案的較大值
dp[c] =
max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt()
max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
}
}
}
@@ -110,27 +108,27 @@ fun knapsackDPComp(
/* Driver Code */
fun main() {
val wgt = intArrayOf(10, 20, 30, 40, 50)
val value = intArrayOf(50, 120, 150, 210, 240)
val _val = intArrayOf(50, 120, 150, 210, 240)
val cap = 50
val n = wgt.size
// 暴力搜尋
var res = knapsackDFS(wgt, value, n, cap)
var res = knapsackDFS(wgt, _val, n, cap)
println("不超過背包容量的最大物品價值為 $res")
// 記憶化搜尋
val mem = Array(n + 1) { IntArray(cap + 1) }
for (row in mem) {
Arrays.fill(row, -1)
row.fill(-1)
}
res = knapsackDFSMem(wgt, value, mem, n, cap)
res = knapsackDFSMem(wgt, _val, mem, n, cap)
println("不超過背包容量的最大物品價值為 $res")
// 動態規劃
res = knapsackDP(wgt, value, cap)
res = knapsackDP(wgt, _val, cap)
println("不超過背包容量的最大物品價值為 $res")
// 空間最佳化後的動態規劃
res = knapsackDPComp(wgt, value, cap)
res = knapsackDPComp(wgt, _val, cap)
println("不超過背包容量的最大物品價值為 $res")
}
}

View File

@@ -19,7 +19,7 @@ fun minCostClimbingStairsDP(cost: IntArray): Int {
dp[2] = cost[2]
// 狀態轉移:從較小子問題逐步求解較大子問題
for (i in 3..n) {
dp[i] = (min(dp[i - 1].toDouble(), dp[i - 2].toDouble()) + cost[i]).toInt()
dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]
}
return dp[n]
}
@@ -32,7 +32,7 @@ fun minCostClimbingStairsDPComp(cost: IntArray): Int {
var b = cost[2]
for (i in 3..n) {
val tmp = b
b = (min(a.toDouble(), tmp.toDouble()) + cost[i]).toInt()
b = min(a, tmp) + cost[i]
a = tmp
}
return b

View File

@@ -6,15 +6,10 @@
package chapter_dynamic_programming
import java.util.*
import kotlin.math.min
/* 最小路徑和:暴力搜尋 */
fun minPathSumDFS(
grid: Array<Array<Int>>,
i: Int,
j: Int
): Int {
fun minPathSumDFS(grid: Array<IntArray>, i: Int, j: Int): Int {
// 若為左上角單元格,則終止搜尋
if (i == 0 && j == 0) {
return grid[0][0]
@@ -27,13 +22,13 @@ fun minPathSumDFS(
val up = minPathSumDFS(grid, i - 1, j)
val left = minPathSumDFS(grid, i, j - 1)
// 返回從左上角到 (i, j) 的最小路徑代價
return (min(left.toDouble(), up.toDouble()) + grid[i][j]).toInt()
return min(left, up) + grid[i][j]
}
/* 最小路徑和:記憶化搜尋 */
fun minPathSumDFSMem(
grid: Array<Array<Int>>,
mem: Array<Array<Int>>,
grid: Array<IntArray>,
mem: Array<IntArray>,
i: Int,
j: Int
): Int {
@@ -53,12 +48,12 @@ fun minPathSumDFSMem(
val up = minPathSumDFSMem(grid, mem, i - 1, j)
val left = minPathSumDFSMem(grid, mem, i, j - 1)
// 記錄並返回左上角到 (i, j) 的最小路徑代價
mem[i][j] = (min(left.toDouble(), up.toDouble()) + grid[i][j]).toInt()
mem[i][j] = min(left, up) + grid[i][j]
return mem[i][j]
}
/* 最小路徑和:動態規劃 */
fun minPathSumDP(grid: Array<Array<Int>>): Int {
fun minPathSumDP(grid: Array<IntArray>): Int {
val n = grid.size
val m = grid[0].size
// 初始化 dp 表
@@ -75,15 +70,14 @@ fun minPathSumDP(grid: Array<Array<Int>>): Int {
// 狀態轉移:其餘行和列
for (i in 1..<n) {
for (j in 1..<m) {
dp[i][j] =
(min(dp[i][j - 1].toDouble(), dp[i - 1][j].toDouble()) + grid[i][j]).toInt()
dp[i][j] = min(dp[i][j - 1], dp[i - 1][j]) + grid[i][j]
}
}
return dp[n - 1][m - 1]
}
/* 最小路徑和:空間最佳化後的動態規劃 */
fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
fun minPathSumDPComp(grid: Array<IntArray>): Int {
val n = grid.size
val m = grid[0].size
// 初始化 dp 表
@@ -99,7 +93,7 @@ fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
dp[0] = dp[0] + grid[i][0]
// 狀態轉移:其餘列
for (j in 1..<m) {
dp[j] = (min(dp[j - 1].toDouble(), dp[j].toDouble()) + grid[i][j]).toInt()
dp[j] = min(dp[j - 1], dp[j]) + grid[i][j]
}
}
return dp[m - 1]
@@ -108,10 +102,10 @@ fun minPathSumDPComp(grid: Array<Array<Int>>): Int {
/* Driver Code */
fun main() {
val grid = arrayOf(
arrayOf(1, 3, 1, 5),
arrayOf(2, 2, 4, 2),
arrayOf(5, 3, 2, 1),
arrayOf(4, 3, 5, 2)
intArrayOf(1, 3, 1, 5),
intArrayOf(2, 2, 4, 2),
intArrayOf(5, 3, 2, 1),
intArrayOf(4, 3, 5, 2)
)
val n = grid.size
val m = grid[0].size
@@ -121,9 +115,9 @@ fun main() {
println("從左上角到右下角的最小路徑和為 $res")
// 記憶化搜尋
val mem = Array(n) { Array(m) { 0 } }
val mem = Array(n) { IntArray(m) }
for (row in mem) {
Arrays.fill(row, -1)
row.fill(-1)
}
res = minPathSumDFSMem(grid, mem, n - 1, m - 1)
println("從左上角到右下角的最小路徑和為 $res")

View File

@@ -9,11 +9,7 @@ package chapter_dynamic_programming
import kotlin.math.max
/* 完全背包:動態規劃 */
fun unboundedKnapsackDP(
wgt: IntArray,
value: IntArray,
cap: Int
): Int {
fun unboundedKnapsackDP(wgt: IntArray, _val: IntArray, cap: Int): Int {
val n = wgt.size
// 初始化 dp 表
val dp = Array(n + 1) { IntArray(cap + 1) }
@@ -25,8 +21,7 @@ fun unboundedKnapsackDP(
dp[i][c] = dp[i - 1][c]
} else {
// 不選和選物品 i 這兩種方案的較大值
dp[i][c] = max(dp[i - 1][c].toDouble(), (dp[i][c - wgt[i - 1]] + value[i - 1]).toDouble())
.toInt()
dp[i][c] = max(dp[i - 1][c], dp[i][c - wgt[i - 1]] + _val[i - 1])
}
}
}
@@ -36,7 +31,7 @@ fun unboundedKnapsackDP(
/* 完全背包:空間最佳化後的動態規劃 */
fun unboundedKnapsackDPComp(
wgt: IntArray,
value: IntArray,
_val: IntArray,
cap: Int
): Int {
val n = wgt.size
@@ -50,8 +45,7 @@ fun unboundedKnapsackDPComp(
dp[c] = dp[c]
} else {
// 不選和選物品 i 這兩種方案的較大值
dp[c] =
max(dp[c].toDouble(), (dp[c - wgt[i - 1]] + value[i - 1]).toDouble()).toInt()
dp[c] = max(dp[c], dp[c - wgt[i - 1]] + _val[i - 1])
}
}
}
@@ -61,14 +55,14 @@ fun unboundedKnapsackDPComp(
/* Driver Code */
fun main() {
val wgt = intArrayOf(1, 2, 3)
val value = intArrayOf(5, 11, 15)
val _val = intArrayOf(5, 11, 15)
val cap = 4
// 動態規劃
var res = unboundedKnapsackDP(wgt, value, cap)
var res = unboundedKnapsackDP(wgt, _val, cap)
println("不超過背包容量的最大物品價值為 $res")
// 空間最佳化後的動態規劃
res = unboundedKnapsackDPComp(wgt, value, cap)
res = unboundedKnapsackDPComp(wgt, _val, cap)
println("不超過背包容量的最大物品價值為 $res")
}