diff --git a/codes/kotlin/chapter_sorting/merge_sort.kt b/codes/kotlin/chapter_sorting/merge_sort.kt index 40ca9ca97..d08d3d925 100644 --- a/codes/kotlin/chapter_sorting/merge_sort.kt +++ b/codes/kotlin/chapter_sorting/merge_sort.kt @@ -19,7 +19,7 @@ fun merge(nums: IntArray, left: Int, mid: Int, right: Int) { while (i <= mid && j <= right) { if (nums[i] <= nums[j]) tmp[k++] = nums[i++] - else + else tmp[k++] = nums[j++] } // 将左子数组和右子数组的剩余元素复制到临时数组中 diff --git a/zh-hant/codes/python/chapter_sorting/merge_sort.py b/zh-hant/codes/python/chapter_sorting/merge_sort.py index 48d4f706d..b0d93ba73 100644 --- a/zh-hant/codes/python/chapter_sorting/merge_sort.py +++ b/zh-hant/codes/python/chapter_sorting/merge_sort.py @@ -41,7 +41,7 @@ def merge_sort(nums: list[int], left: int, right: int): if left >= right: return # 當子陣列長度為 1 時終止遞迴 # 劃分階段 - mid = left + (right - left) // 2 # 計算中點 + mid = (left + right) // 2 # 計算中點 merge_sort(nums, left, mid) # 遞迴左子陣列 merge_sort(nums, mid + 1, right) # 遞迴右子陣列 # 合併階段 diff --git a/zh-hant/codes/ruby/chapter_backtracking/n_queens.rb b/zh-hant/codes/ruby/chapter_backtracking/n_queens.rb new file mode 100644 index 000000000..c4111e2ae --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/n_queens.rb @@ -0,0 +1,61 @@ +=begin +File: n_queens.rb +Created Time: 2024-05-21 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 回溯演算法:n 皇后 ### +def backtrack(row, n, state, res, cols, diags1, diags2) + # 當放置完所有行時,記錄解 + if row == n + res << state.map { |row| row.dup } + return + end + + # 走訪所有列 + for col in 0...n + # 計算該格子對應的主對角線和次對角線 + 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 + # 放置下一行 + backtrack(row + 1, n, state, res, cols, diags1, diags2) + # 回退:將該格子恢復為空位 + state[row][col] = "#" + cols[col] = diags1[diag1] = diags2[diag2] = false + end + end +end + +### 求解 n 皇后 ### +def n_queens(n) + # 初始化 n*n 大小的棋盤,其中 'Q' 代表皇后,'#' 代表空位 + state = Array.new(n) { Array.new(n, "#") } + cols = Array.new(n, false) # 記錄列是否有皇后 + diags1 = Array.new(2 * n - 1, false) # 記錄主對角線上是否有皇后 + diags2 = Array.new(2 * n - 1, false) # 記錄次對角線上是否有皇后 + res = [] + backtrack(0, n, state, res, cols, diags1, diags2) + + res +end + +### Driver Code ### +if __FILE__ == $0 + n = 4 + res = n_queens(n) + + puts "輸入棋盤長寬為 #{n}" + puts "皇后放置方案共有 #{res.length} 種" + + for state in res + puts "--------------------" + for row in state + p row + end + end +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/permutations_i.rb b/zh-hant/codes/ruby/chapter_backtracking/permutations_i.rb new file mode 100644 index 000000000..dd5a7ec13 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/permutations_i.rb @@ -0,0 +1,46 @@ +=begin +File: permutations_i.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 回溯演算法:全排列 I ### +def backtrack(state, choices, selected, res) + # 當狀態長度等於元素數量時,記錄解 + if state.length == choices.length + res << state.dup + return + end + + # 走訪所有選擇 + choices.each_with_index do |choice, i| + # 剪枝:不允許重複選擇元素 + unless selected[i] + # 嘗試:做出選擇,更新狀態 + selected[i] = true + state << choice + # 進行下一輪選擇 + backtrack(state, choices, selected, res) + # 回退:撤銷選擇,恢復到之前的狀態 + selected[i] = false + state.pop + end + end +end + +### 全排列 I ### +def permutations_i(nums) + res = [] + backtrack([], nums, Array.new(nums.length, false), res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [1, 2, 3] + + res = permutations_i(nums) + + puts "輸入陣列 nums = #{nums}" + puts "所有排列 res = #{res}" +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb b/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb new file mode 100644 index 000000000..682fc7162 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/permutations_ii.rb @@ -0,0 +1,48 @@ +=begin +File: permutations_ii.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 回溯演算法:全排列 II ### +def backtrack(state, choices, selected, res) + # 當狀態長度等於元素數量時,記錄解 + if state.length == choices.length + res << state.dup + return + end + + # 走訪所有選擇 + duplicated = Set.new + choices.each_with_index do |choice, i| + # 剪枝:不允許重複選擇元素 且 不允許重複選擇相等元素 + if !selected[i] && !duplicated.include?(choice) + # 嘗試:做出選擇,更新狀態 + duplicated.add(choice) + selected[i] = true + state << choice + # 進行下一輪選擇 + backtrack(state, choices, selected, res) + # 回退:撤銷選擇,恢復到之前的狀態 + selected[i] = false + state.pop + end + end +end + +### 全排列 II ### +def permutations_ii(nums) + res = [] + backtrack([], nums, Array.new(nums.length, false), res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [1, 2, 2] + + res = permutations_ii(nums) + + puts "輸入陣列 nums = #{nums}" + puts "所有排列 res = #{res}" +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb new file mode 100644 index 000000000..b0de96d91 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_i_compact.rb @@ -0,0 +1,33 @@ +=begin +File: preorder_traversal_i_compact.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 前序走訪:例題一 ### +def pre_order(root) + return unless root + + # 記錄解 + $res << root if root.val == 7 + + pre_order(root.left) + pre_order(root.right) +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\n初始化二元樹" + print_tree(root) + + # 前序走訪 + $res = [] + pre_order(root) + + puts "\n輸出所有值為 7 的節點" + p $res.map { |node| node.val } +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb new file mode 100644 index 000000000..de25757cd --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_ii_compact.rb @@ -0,0 +1,41 @@ +=begin +File: preorder_traversal_ii_compact.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 前序走訪:例題二 ### +def pre_order(root) + return unless root + + # 嘗試 + $path << root + + # 記錄解 + $res << $path.dup if root.val == 7 + + pre_order(root.left) + pre_order(root.right) + + # 回退 + $path.pop +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\n初始化二元樹" + print_tree(root) + + # 前序走訪 + $path, $res = [], [] + pre_order(root) + + puts "\n輸出所有根節點到節點 7 的路徑" + for path in $res + p path.map { |node| node.val } + end +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb new file mode 100644 index 000000000..44d12956c --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_iii_compact.rb @@ -0,0 +1,42 @@ +=begin +File: preorder_traversal_iii_compact.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 前序走訪:例題三 ### +def pre_order(root) + # 剪枝 + return if !root || root.val == 3 + + # 嘗試 + $path.append(root) + + # 記錄解 + $res << $path.dup if root.val == 7 + + pre_order(root.left) + pre_order(root.right) + + # 回退 + $path.pop +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\n初始化二元樹" + print_tree(root) + + # 前序走訪 + $path, $res = [], [] + pre_order(root) + + puts "\n輸出所有根節點到節點 7 的路徑,路徑中不包含值為 3 的節點" + for path in $res + p path.map { |node| node.val } + end +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb new file mode 100644 index 000000000..cf770b512 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/preorder_traversal_iii_template.rb @@ -0,0 +1,68 @@ +=begin +File: preorder_traversal_iii_template.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 判斷當前狀態是否為解 ### +def is_solution?(state) + !state.empty? && state.last.val == 7 +end + +### 記錄解 ### +def record_solution(state, res) + res << state.dup +end + +### 判斷在當前狀態下,該選擇是否合法 ### +def is_valid?(state, choice) + choice && choice.val != 3 +end + +### 更新狀態 ### +def make_choice(state, choice) + state << choice +end + +### 恢復狀態 ### +def undo_choice(state, choice) + state.pop +end + +### 回溯演算法:例題三 ### +def backtrack(state, choices, res) + # 檢查是否為解 + record_solution(state, res) if is_solution?(state) + + # 走訪所有選擇 + for choice in choices + # 剪枝:檢查選擇是否合法 + if is_valid?(state, choice) + # 嘗試:做出選擇,更新狀態 + make_choice(state, choice) + # 進行下一輪選擇 + backtrack(state, [choice.left, choice.right], res) + # 回退:撤銷選擇,恢復到之前的狀態 + undo_choice(state, choice) + end + end +end + +### Driver Code ### +if __FILE__ == $0 + root = arr_to_tree([1, 7, 3, 4, 5, 6, 7]) + puts "\n初始化二元樹" + print_tree(root) + + # 回溯演算法 + res = [] + backtrack([], [root], res) + + puts "\n輸出所有根節點到節點 7 的路徑,要求路徑中不包含值為 3 的節點" + for path in res + p path.map { |node| node.val } + end +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/subset_sum_i.rb b/zh-hant/codes/ruby/chapter_backtracking/subset_sum_i.rb new file mode 100644 index 000000000..3390e98b8 --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/subset_sum_i.rb @@ -0,0 +1,47 @@ +=begin +File: subset_sum_i.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 回溯演算法:子集和 I ### +def backtrack(state, target, choices, start, res) + # 子集和等於 target 時,記錄解 + if target.zero? + res << state.dup + return + end + # 走訪所有選擇 + # 剪枝二:從 start 開始走訪,避免生成重複子集 + for i in start...choices.length + # 剪枝一:若子集和超過 target ,則直接結束迴圈 + # 這是因為陣列已排序,後邊元素更大,子集和一定超過 target + break if target - choices[i] < 0 + # 嘗試:做出選擇,更新 target, start + state << choices[i] + # 進行下一輪選擇 + backtrack(state, target - choices[i], choices, i, res) + # 回退:撤銷選擇,恢復到之前的狀態 + state.pop + end +end + +### 求解子集和 I ### +def subset_sum_i(nums, target) + state = [] # 狀態(子集) + nums.sort! # 對 nums 進行排序 + start = 0 # 走訪起始點 + res = [] # 結果串列(子集串列) + backtrack(state, target, nums, start, res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [3, 4, 5] + target = 9 + res = subset_sum_i(nums, target) + + puts "輸入陣列 = #{nums}, target = #{target}" + puts "所有和等於 #{target} 的子集 res = #{res}" +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb b/zh-hant/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb new file mode 100644 index 000000000..d5b3ac06b --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/subset_sum_i_naive.rb @@ -0,0 +1,46 @@ +=begin +File: subset_sum_i_naive.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 回溯演算法:子集和 I ### +def backtrack(state, target, total, choices, res) + # 子集和等於 target 時,記錄解 + if total == target + res << state.dup + return + end + + # 走訪所有選擇 + for i in 0...choices.length + # 剪枝:若子集和超過 target ,則跳過該選擇 + next if total + choices[i] > target + # 嘗試:做出選擇,更新元素和 total + state << choices[i] + # 進行下一輪選擇 + backtrack(state, target, total + choices[i], choices, res) + # 回退:撤銷選擇,恢復到之前的狀態 + state.pop + end +end + +### 求解子集和 I(包含重複子集)### +def subset_sum_i_naive(nums, target) + state = [] # 狀態(子集) + total = 0 # 子集和 + res = [] # 結果串列(子集串列) + backtrack(state, target, total, nums, res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [3, 4, 5] + target = 9 + res = subset_sum_i_naive(nums, target) + + puts "輸入陣列 nums = #{nums}, target = #{target}" + puts "所有和等於 #{target} 的子集 res = #{res}" + puts "請注意,該方法輸出的結果包含重複集合" +end diff --git a/zh-hant/codes/ruby/chapter_backtracking/subset_sum_ii.rb b/zh-hant/codes/ruby/chapter_backtracking/subset_sum_ii.rb new file mode 100644 index 000000000..343437f6b --- /dev/null +++ b/zh-hant/codes/ruby/chapter_backtracking/subset_sum_ii.rb @@ -0,0 +1,51 @@ +=begin +File: subset_sum_ii.rb +Created Time: 2024-05-22 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +### 回溯演算法:子集和 II ### +def backtrack(state, target, choices, start, res) + # 子集和等於 target 時,記錄解 + if target.zero? + res << state.dup + return + end + + # 走訪所有選擇 + # 剪枝二:從 start 開始走訪,避免生成重複子集 + # 剪枝三:從 start 開始走訪,避免重複選擇同一元素 + for i in start...choices.length + # 剪枝一:若子集和超過 target ,則直接結束迴圈 + # 這是因為陣列已排序,後邊元素更大,子集和一定超過 target + break if target - choices[i] < 0 + # 剪枝四:如果該元素與左邊元素相等,說明該搜尋分支重複,直接跳過 + next if i > start && choices[i] == choices[i - 1] + # 嘗試:做出選擇,更新 target, start + state << choices[i] + # 進行下一輪選擇 + backtrack(state, target - choices[i], choices, i + 1, res) + # 回退:撤銷選擇,恢復到之前的狀態 + state.pop + end +end + +### 求解子集和 II ### +def subset_sum_ii(nums, target) + state = [] # 狀態(子集) + nums.sort! # 對 nums 進行排序 + start = 0 # 走訪起始點 + res = [] # 結果串列(子集串列) + backtrack(state, target, nums, start, res) + res +end + +### Driver Code ### +if __FILE__ == $0 + nums = [4, 4, 5] + target = 9 + res = subset_sum_ii(nums, target) + + puts "輸入陣列 nums = #{nums}, target = #{target}" + puts "所有和等於 #{target} 的子集 res = #{res}" +end diff --git a/zh-hant/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs b/zh-hant/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs index 85d638ea3..4ac7a9fb6 100644 --- a/zh-hant/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs +++ b/zh-hant/codes/rust/chapter_divide_and_conquer/binary_search_recur.rs @@ -10,7 +10,7 @@ fn dfs(nums: &[i32], target: i32, i: i32, j: i32) -> i32 { if i > j { return -1; } - let m: i32 = (i + j) / 2; + let m: i32 = i + (j - i) / 2; if nums[m as usize] < target { // 遞迴子問題 f(m+1, j) return dfs(nums, target, m + 1, j); diff --git a/zh-hant/codes/rust/chapter_sorting/radix_sort.rs b/zh-hant/codes/rust/chapter_sorting/radix_sort.rs index 71689908f..d818a58f1 100644 --- a/zh-hant/codes/rust/chapter_sorting/radix_sort.rs +++ b/zh-hant/codes/rust/chapter_sorting/radix_sort.rs @@ -35,9 +35,7 @@ fn counting_sort_digit(nums: &mut [i32], exp: i32) { counter[d] -= 1; // 將 d 的數量減 1 } // 使用結果覆蓋原陣列 nums - for i in 0..n { - nums[i] = res[i]; - } + nums.copy_from_slice(&res); } /* 基數排序 */ diff --git a/zh-hant/codes/zig/chapter_sorting/merge_sort.zig b/zh-hant/codes/zig/chapter_sorting/merge_sort.zig index e0a12d11a..c60434fef 100644 --- a/zh-hant/codes/zig/chapter_sorting/merge_sort.zig +++ b/zh-hant/codes/zig/chapter_sorting/merge_sort.zig @@ -48,7 +48,7 @@ fn mergeSort(nums: []i32, left: usize, right: usize) !void { // 終止條件 if (left >= right) return; // 當子陣列長度為 1 時終止遞迴 // 劃分階段 - var mid = left + (right - left) / 2; // 計算中點 + var mid = left + (right - left) / 2; // 計算中點 try mergeSort(nums, left, mid); // 遞迴左子陣列 try mergeSort(nums, mid + 1, right); // 遞迴右子陣列 // 合併階段 diff --git a/zh-hant/docs/chapter_backtracking/backtracking_algorithm.md b/zh-hant/docs/chapter_backtracking/backtracking_algorithm.md index f4c2bda29..56cbc9cc7 100644 --- a/zh-hant/docs/chapter_backtracking/backtracking_algorithm.md +++ b/zh-hant/docs/chapter_backtracking/backtracking_algorithm.md @@ -406,7 +406,27 @@ === "Ruby" ```ruby title="" + ### 回溯演算法框架 ### + def backtrack(state, choices, res) + # 判斷是否為解 + if is_solution?(state) + # 記錄解 + record_solution(state, res) + return + end + # 走訪所有選擇 + for choice in choices + # 剪枝:判斷選擇是否合法 + if is_valid?(state, choice) + # 嘗試:做出選擇,更新狀態 + make_choice(state, choice) + backtrack(state, choices, res) + # 回退:撤銷選擇,恢復到之前的狀態 + undo_choice(state, choice) + end + end + end ``` === "Zig"