算法回溯:如何在不存储状态的情况下进行递归
Posted
技术标签:
【中文标题】算法回溯:如何在不存储状态的情况下进行递归【英文标题】:Algorithm Backtracking: How to do recursion without storing state 【发布时间】:2018-04-02 22:15:11 【问题描述】:通常在回溯中,我们采用一个辅助函数,该函数接受初始状态,每个递归调用负责自己的计算并将结果传递给下一个递归调用。理论上,我们通过不可见和可见变量来表示这一点。
例如,在一个字符串的排列中,我们将使用这个程序:
def permute(str)
return str if str.length < 2
permute_helper(str, "")
end
def permute_helper(unseen, seen)
#base case
if unseen.length <= 0
p seen
return
else
(0..unseen.length-1).each do |i|
buffer = unseen
buffer = buffer.split('')
buffer.delete_at(i)
buffer = buffer.join('')
permute_helper(buffer, seen+unseen[i])
end
end
end
permute('abc')
Thie 将打印出所需的结果。
在最近的一次采访中,我被要求在不使用两个变量的情况下这样做。没有将状态存储在 seen 变量中。我当时想不通,但是想问下不存储状态怎么回溯?
【问题讨论】:
这是一大堆代码,归结为简单的子字符串操作。unseen[i,1] = ""
将删除1
位置i
处的字符。对于其他场合,更喜欢unseen.chars
到unseen.split('')
。
【参考方案1】:
字符串"cd"
的排列是["cd", "dc"]
。如果我们现在希望获得字符串"bcd"
的排列,我们只需将该数组的每个元素替换为三个字符串,每个字符串的"b"
在不同的位置。 "cd"
变为 "bcd"
、"cbd"
和 "cdb"
和 "dc"
变为 "bdc"
、"dbc"
和 "dba"
。因此"bcd"
的排列是
["bcd", "cbd", "cdb", "bdc", "dbc", "dba"]
如果我们现在希望获得"abcd"
的排列,我们将上述六元素数组的每个元素替换为四个字符串,每个字符串在不同的位置使用"a"
。例如,"bcd"
变为 "abcd"
、"bacd"
、"bcad"
和 "bcda"
。递归的结构现在应该很明显了。
def permute(str)
case str.length
when 0, 1
str
when 2
[str, str.reverse]
else
first = str[0]
sz = str.size-1
permute(str[1..-1]).flat_map |s| (0..sz).map |i| s.dup.insert(i,first)
end
end
permute('')
#=> ""
permute('a')
#=> "a"
permute('ab')
#=> ["ab", "ba"]
permute('abc')
#=> ["abc", "bac", "bca", "acb", "cab", "cba"]
permute('abcd')
#=> ["abcd", "bacd", "bcad", "bcda", "acbd", "cabd", "cbad", "cbda",
# "acdb", "cadb", "cdab", "cdba", "abdc", "badc", "bdac", "bdca",
# "adbc", "dabc", "dbac", "dbca", "adcb", "dacb", "dcab", "dcba"]
str
当然是“看不见”的变量。
【讨论】:
非常好的编码,卡里【参考方案2】:@CarySwoveland 的回答通常非常棒。对于那些希望置换 array 的人,可以考虑这种函数式方法。虽然这使用了辅助 lambda all_pos
,但没有使用额外的状态参数来累积结果。
def permute ((x, *xs))
all_pos = lambda do |(y,*ys)|
if y.nil?
[[ x ]]
else
[[ x, y, *ys ]] + (all_pos.call ys) .map |rest| [ y, *rest ]
end
end
if x.nil? or xs.empty?
[[x]]
else
(permute xs) .flat_map &all_pos
end
end
permute [1,2,3,4]
# [ [1, 2, 3, 4]
# , [2, 1, 3, 4]
# , [2, 3, 1, 4]
# , [2, 3, 4, 1]
# , [1, 3, 2, 4]
# , [3, 1, 2, 4]
# , [3, 2, 1, 4]
# , [3, 2, 4, 1]
# , [1, 3, 4, 2]
# , [3, 1, 4, 2]
# , [3, 4, 1, 2]
# , [3, 4, 2, 1]
# , [1, 2, 4, 3]
# , [2, 1, 4, 3]
# , [2, 4, 1, 3]
# , [2, 4, 3, 1]
# , [1, 4, 2, 3]
# , [4, 1, 2, 3]
# , [4, 2, 1, 3]
# , [4, 2, 3, 1]
# , [1, 4, 3, 2]
# , [4, 1, 3, 2]
# , [4, 3, 1, 2]
# , [4, 3, 2, 1]
# ]
【讨论】:
以上是关于算法回溯:如何在不存储状态的情况下进行递归的主要内容,如果未能解决你的问题,请参考以下文章