【问题标题】:Recursive Linear Search - returns list index递归线性搜索 - 返回列表索引
【发布时间】:2010-11-28 05:57:44
【问题描述】:

我有一个函数 search,它在列表 ('l') 中搜索键,如果找到则返回 True,否则返回 False。如果找到,我希望它返回 key 的索引,如果没有找到,则返回 False,但我对我的 return 语句应该是什么感到困惑。这是我的代码:

def search(l,key):
    """
    locates key in list l.  if present, returns location as an index; 
    else returns False.
    PRE: l is a list.
    POST: l is unchanged; returns i such that l[i] == key; False otherwise.
    """

    if l:   # checks if list exists
        if l[0] == key:     # base case - first index is key
            return True

        s = search(l[1:], key)      # recursion
        if s is not False:          
            return s

    return False            # returns false if key not found

任何帮助将不胜感激,谢谢。

【问题讨论】:

  • 应该注意我不能使用内置的.index函数或者in操作符。我想我已经很接近了,虽然没有这些。
  • 没有看到这个不能使用索引的评论。但方法可以类似。试试看并在此处发布您的答案
  • 请确保你的导师知道(a)当这在一个足够长的列表中爆炸时你仍然通过(b)重递归的课程最好用支持它的语言教授。如果说教练试图让你使用 setter 或 getter,给他/她坚定但不是毁灭性的打击,告诉他们这是来自 aaronasterling。
  • @aaronasterling:+1 我想,这更像是一个介绍性练习,以限制他们使用 python 中可用的工具,并提出尊重这些限制的解决方案。当然,他们也应该告诉他们,为什么这个解决方案不适合/不如 Python 中的其他解决方案。在我发布答案后很久才看到他的评论,并且对此也投了反对票。
  • 这不是 Python!使用 for 循环而不使用递归遍历列表。

标签: python recursion


【解决方案1】:

对于您的基本情况,您刚刚在索引 0 处找到了该项目,对吧?返回 0。

if l[0] == key:     # base case - first index is key
    return 0

对于递归部分,让我们考虑返回什么。假设该项目的索引为 5。由于我们通过递归调用传递了一个移动了一个元素的列表,它会找到它并返回 4。(4,而不是 5。你明白为什么吗?)

我们需要在返回之前添加一个来取消移动索引。

s = search(l[1:], key)      # recursion
if s is not False:          
    return s + 1

【讨论】:

  • +1 以获得良好的解释,无需做功课。我认为在失败的情况下返回None而不是False会更好(函数从不返回True),但这可能是个人喜好问题。
  • -1 用于拥有极其丑陋的 if is not False 却没有解释它是极其丑陋的 API 设计所必需的。
  • 谢谢约翰,我明白你为什么要添加一个。这个解决方案(没有参数'idx')会更合适吗?反之亦然?
【解决方案2】:

您需要跟踪索引。因为您的最终返回值 [如果发生 True 搜索] 是布尔值,因此您必须更改它。
我想下面的代码应该可以帮助你,但要彻底测试它,因为我只是试图理解意图并且没有彻底测试逻辑 -

def search(l,key,idx=0):
"""
locates key in list l.  if present, returns location as an index; 
else returns False.
PRE: l is a list.
POST: l is unchanged; returns i such that l[i] == key; False otherwise.
"""

if l:   # checks if list exists
    if l[0] == key:     # base case - first index is key
    return idx

    s = search(l[1:], key, (idx + 1))      # recursion
    if s is not False:          
        return s

return False            # returns false if key not found

【讨论】:

  • @JMJY:inspectorG4dget 正在通过他的帽子说话:-(
  • @JohnMachin:我现在已经看到了您的解决方案,并为他们 +1。我不相信我在发布此评论时看到了您的答案。回头看看时间戳。看来您可能在我发表评论后不久就发布了答案。
  • @inspectorg4dget:没有“似乎”。我的评论确实是在我发布答案之前做出的,并且在这种情况下非常有效......您没有对您作为“正确解决方案”推动的代码进行严格检查。
  • @JohnMachin:我似乎有一个错误的印象。我的道歉
【解决方案3】:

问题是您正在切片列表的尾部,而没有保留有关切片发生位置的任何信息。

事实上,您根本不需要对列表进行切片,因为您只需在列表中进行索引查找即可。

线性搜索的算法是原始递归的,所以如果你能想出一个迭代的解决方案,递归的解决方案是很容易达到的(反之亦然)。

所以迭代解决方案可能看起来像这样:

for every integer i between zero and length of list
    if the element at position i in the list is equal to the key
        return i
else
   return "I couldn't find it"

我们将迭代解决方案转换为递归解决方案基本上意味着将循环转换为函数调用,其参数是下一次循环迭代的值。循环变量是i 和正在搜索的列表。为了让你从这个练习中学习,我就先不说了。

【讨论】:

    【解决方案4】:

    您的 API 设计存在严重缺陷。

    >>> False == 0
    True
    

    您的教练正在为您准备惊喜。例如:

    where = search(["non-foo", "not-foo"], "foo") # returns False
    if where == 0:
        print "foo is in pole position"
        # but "foo" isn't even a candidate
    

    让它在失败时返回None。试试这个:

    >>> def search(alist, key, pos=None):
    ...     if pos is None: pos = len(alist) - 1
    ...     if pos < 0: return None
    ...     if key == alist[pos]: return pos
    ...     return search(alist, key, pos - 1)
    ...
    >>> search([1,2,3], 4) # -> None
    >>> search([1,2,3], 3)
    2
    >>> search([1,2,3], 2)
    1
    >>> search([1,2,3], 1)
    0
    >>> search([], 1) # -> None
    >>>
    

    此方法的其他特点: (1) 向您介绍“隐藏”参数的概念,该参数可用于在非递归函数中使用局部变量的情况。 (2) 避免所有字符串切片的成本。

    ==========================================

    为了@inspectorG4dget 的利益,这是我对@anirvan 答案的重构:

    def xsearch(l,key,idx=0):
        if l:   # checks if list exists
            if l[0] == key:     # base case - first index is key
                return idx
            s = xsearch(l[1:], key, (idx + 1))      # recursion
            if s is not False:          
                return s
            #### and if s is False, it falls through and returns False ####
            #### so it can return s unconditionally!                   ####
        return False            # returns false if key not found
    
    def ysearch(l,key,idx=0):
        if l:   # checks if list exists
            if l[0] == key:     # base case - first index is key
                return idx
            return ysearch(l[1:], key, (idx + 1))      # recursion
        return False            # returns false if key not found
        #### above statement is better put closer to the `if` ####
    
    def zsearch(l,key,idx=0):
        if not l:   # checks if list exists
            return False
        if l[0] == key:     # base case - first index is key
            return idx
        return zsearch(l[1:], key, (idx + 1))      # recursion
    

    【讨论】:

    • 嗨,约翰,感谢您的帮助。我只是想知道您的 xsearch、ysearch 和 zsearch 之间有什么区别?你只是在压缩代码吗?
    • @JMJY:我正在重构 anirvan 的代码 (xsearch) 以删除不必要的代码,这会导致丑陋的“if s is not False”消失。请注意我在此过程中添加到代码中的 cmets;我会回过头来用“####”突出显示它们。如果“只是”是指“仅仅”,请重新考虑——臃肿的代码是糟糕的代码。
    • 感谢您的帮助和澄清。
    猜你喜欢
    • 2019-03-02
    • 2016-11-13
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-06
    相关资源
    最近更新 更多