算法回溯:如何在不存储状态的情况下进行递归

时间:2018-04-02 22:15:11

标签: ruby algorithm recursion backtracking

通常在回溯中,我们采用一个辅助函数,它接受一个初始状态,每个递归调用都会处理自己的计算并将结果传递给下一个递归调用。从理论上讲,我们通过看不见的变量来表示这一点。

例如,在字符串的排列中,我们将使用此程序:

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')

您将打印出所需的结果。

在最近的一次采访中,我被要求不使用两个变量。没有在看到的变量中存储状态。当时我无法想到整体,但我想问一下如何在不存储状态的情况下进行回溯?

2 个答案:

答案 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]
# ]