【问题标题】:Print n choose k combination algorithm using recursion使用递归打印n选择k组合算法
【发布时间】:2017-07-04 21:32:20
【问题描述】:

这一定是一个经典的面试问题,但是,我在理解它时遇到了问题。

下面是我在 Python 中的实现,如果你运行它,它只会打印ab, ac, ad。它不会达到'b' (bc, bd) 级别。

def Print_nCk (the_list, k, str_builder, used):
    if len(str_builder) == k:
        print str_builder
        return 
    else:
        for i in xrange(len(the_list)):
            if used[i] !=True:
                str_builder+=the_list[i]
                used[i] = True
                Print_nCk(the_list, k, str_builder, used)
                str_builder = str_builder[:-1]


Print_nCk(['a','b','c','d'], 2, "",[False,False,False,False])

当上面一行通过时,正确答案是ab,ac,ad,bc,bd,cd。

我从这里知道正确的实现而不使用 used 参数 (http://www.geeksforgeeks.org/print-all-possible-combinations-of-r-elements-in-a-given-array-of-size-n/) 但是 我的问题是我的实现有什么问题?

你能解释一下吗?

为了调试,我每次都打印出“used”。 used 参数在打印“广告”后变为 (True, True, True, True) ,然后它不会比这更深。如果我坚持使用used,修复used 的聪明方法是什么?

【问题讨论】:

  • 顺便说一句,>>> [''.join(c) for c in combinations(['a','b','c','d'], 2)] ; ['ab', 'ac', 'ad', 'bc', 'bd', 'cd']

标签: python algorithm recursion combinations


【解决方案1】:

您回溯时忘记取消设置used[i]:

def Print_nCk (the_list, k, str_builder, used):
    if len(str_builder) == k:
        print str_builder
        return
    else:
        for i in xrange(len(the_list)):
            if used[i] != True:
                str_builder += the_list[i]
                used[i] = True
                Print_nCk(the_list, k, str_builder, used)
                str_builder = str_builder[:-1]
                used[i] = False


Print_nCk(['a','b','c','d'], 2, "",[False,False,False,False])

在您当前的实现中,从您选择i 作为值的那一刻起,您就将used[i] 设置为True。但是,如果稍后您决定选择另一个分支,则应正确记账,从而取消设置 used[i]。

请注意,现在将生成 "ab" 和 "ba"。因此,您生成具有对称性的组合。如果您不想这样,可以使用附加参数。这样可以确保您不会使用低于之前选择的索引:

def Print_nCk (the_list, k, str_builder, used, prev = 0):
    if len(str_builder) == k:
        print str_builder
        return
    else:
        for i in xrange(prev,len(the_list)):
            if used[i] != True:
                str_builder += the_list[i]
                used[i] = True
                Print_nCk(the_list, k, str_builder, used, i+1)
                str_builder = str_builder[:-1]
                used[i] = False


Print_nCk(['a','b','c','d'], 2, "",[False,False,False,False])

尽管如此,这或多或少违背了使用“已使用”数组的目的。您可以简单地使用prev:

def Print_nCk (the_list, k, str_builder, prev = 0):
    if len(str_builder) == k:
        print str_builder
        return
    else:
        for i in xrange(prev,len(the_list)):
            str_builder += the_list[i]
            Print_nCk(the_list, k, str_builder, i+1)
            str_builder = str_builder[:-1]


Print_nCk(['a','b','c','d'], 2, "")

然后打印:

>>> Print_nCk(['a','b','c','d'], 2, "")
ab
ac
ad
bc
bd
cd

【讨论】:

  • 这也会打印出da, db, dc。
  • @cᴏʟᴅsᴘᴇᴇᴅ:现在已经修复 :)
  • 非常感谢!看到这个 uv dv 根据你的编辑变化有多快很有趣:)
  • @ToussaintLouverture 您应该接受这个答案,因为它回答了您的问题。但是为了更清洁的方法,请查看我的答案。
  • 知道这里的时间复杂度是多少吗?不错的代码@WillemVanOnsem!
【解决方案2】:

聚会有点晚了,但我认为你应该更多地利用递归。无需传递任何多余的参数。

这里有一个更简单的方法:

def Print_nCk(the_list, size):
    combs = []
    if size == 1: # the base case
        return the_list

    for i, c in enumerate(the_list[:-size + 1]):
        for sub_comb in Print_nCk(the_list[i + 1:], size  - 1): # generate and return all sub-combos of size - 1
            combs.append(c + sub_comb) # for each sub-combo, add the sizeth (or n - sizeth) character

    return combs

此方法生成size - 1 的组合并将它们与sizeth 字符组合。

对于 size-2 组合:

>>> Print_nCk(['a','b','c','d'], 2)
['ab', 'ac', 'ad', 'bc', 'bd', 'cd']

对于尺寸 3 的组合:

>>> Print_nCk(['a','b','c','d'], 3)
['abc', 'abd', 'acd', 'bcd']

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-02
    • 1970-01-01
    • 2018-10-10
    • 2012-01-30
    • 1970-01-01
    • 2013-02-24
    相关资源
    最近更新 更多