feat: Add Ruby code - chapter "Backtracking" (#1373)
* [feat] add ruby code - chapter backtracking * feat: add ruby code block - chapter backtrackingpull/1378/head
parent
21be3fdaf8
commit
aa818945f0
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
Loading…
Reference in new issue