通常在回溯中,我们采用一个辅助函数,它接受一个初始状态,每个递归调用都会处理自己的计算并将结果传递给下一个递归调用。从理论上讲,我们通过看不见的变量来表示这一点。
例如,在字符串的排列中,我们将使用此程序:
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')
您将打印出所需的结果。
在最近的一次采访中,我被要求不使用两个变量。没有在看到的变量中存储状态。当时我无法想到整体,但我想问一下如何在不存储状态的情况下进行回溯?
答案 0 :(得分:7)
字符串"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
当然是“看不见的”变量。
答案 1 :(得分:2)
@ CarySwoveland的回答一般来说,解释很棒。对于那些希望置换数组的人,请考虑这种功能方法。虽然这使用辅助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]
# ]