使用递归来查找列表中的最大值

时间:2014-12-29 13:50:10

标签: python recursion

我正在尝试使用递归查找列表中的最大元素。 输入需要是实际列表,左索引和右索引。

我写了一个函数,无法理解为什么它不起作用。我绘制了递归树,在脑海中运行了列表示例,这很有意义,这就是为什么现在更难找到解决方案的原因! (它基本上是在打自己)。

我知道这很难看,但试着忽略它。我的想法是在每次递归调用时将列表分成两半(这是必需的),而左侧索引将保持为0,右侧将是新的减半列表的长度减去1.

第一次调用函数将来自tail函数。

感谢您的帮助,我希望我不会错过一些非常愚蠢的事情,甚至更糟糕 - 甚至不会关闭! 顺便说一句 - 没有使用切片切割清单,因为我不被允许。

def max22(L,left,right):
    if len(L)==1:
        return L[0]
    a = max22([L[i] for i in range(left, (left+right)//2)], 0 , len([L[i] for i in range(left, (left+right)//2)])-1)
    b = max22([L[i] for i in range(((left+right)//2)+1, right)], 0 ,len([L[i] for i in range(left, (left+right)//2)])-1)
    return max(a,b)



def max_list22(L):
    return max22(L,0,len(L)-1)

输入示例 - 对于max_list22([1,20,3]),输出将为20.

3 个答案:

答案 0 :(得分:4)

问题在于你根本不处理空列表。 max_list22([])无限地递归,[L[i] for i in range(((left+right)//2)+1, right)]最终产生一个空列表。

答案 1 :(得分:4)

首先,为了清楚起见,我建议将列表推导分配给变量,这样您就不必每次写两次。这应该使代码更容易调试。您也可以对(left+right)//2值执行相同操作。

def max22(L,left,right):
    if len(L)==1:
        return L[0]
    mid = (left+right)//2
    left_L = [L[i] for i in range(left, mid)]
    right_L = [L[i] for i in range(mid+1, right)]
    a = max22(left_L,  0 , len(left_L)-1)
    b = max22(right_L, 0 , len(left_L)-1)
    return max(a,b)

def max_list22(L):
    return max22(L,0,len(L)-1)

print max_list22([4,8,15,16,23,42])

我发现此代码存在四个问题。

  1. b =行,第二个参数使用的是len(left_L),而不是len(right_L)
  2. 您错过了left_Lright_L之间的元素。您不应该在mid列表理解中添加一个right_L
  3. 您错过了列表的最后一个元素。您应该right+1中的right_L,而不仅仅是right
  4. 对于偶数大小的列表,您的mid值关闭了一个。防爆。 [1,2,3,4]应该分为[1,2]和[3,4],但是如果你的mid值为[1][2,3,4] 。 (假设您已经修复了之前要点中的缺失元素问题)。
  5. 修复这些问题如下:

    def max22(L,left,right):
        if len(L)==1:
            return L[0]
        mid = (left+right+1)//2
        left_L = [L[i] for i in range(left, mid)]
        right_L = [L[i] for i in range(mid, right+1)]
        a = max22(left_L,  0 , len(left_L)-1)
        b = max22(right_L, 0 , len(right_L)-1)
        return max(a,b)
    
    def max_list22(L):
        return max22(L,0,len(L)-1)
    
    print max_list22([4,8,15,16,23,42])
    

    如果你坚持不使用临时变量,它看起来像是:

    def max22(L,left,right):
        if len(L)==1:
            return L[0]
        a = max22([L[i] for i in range(left, (left+right+1)//2)],  0 , len([L[i] for i in range(left, (left+right+1)//2)])-1)
        b = max22([L[i] for i in range((left+right+1)//2, right+1)], 0 , len([L[i] for i in range((left+right+1)//2, right+1)])-1)
        return max(a,b)
    
    def max_list22(L):
        return max22(L,0,len(L)-1)
    
    print max_list22([4,8,15,16,23,42])
    

    奖励风格提示:您不一定需要max22的三个参数,因为left始终为零,right始终是列表的长度减去一。< / p>

    def max22(L):
        if len(L)==1:
            return L[0]
        mid = (len(L))//2
        left_L = [L[i] for i in range(0, mid)]
        right_L = [L[i] for i in range(mid, len(L))]
        a = max22(left_L)
        b = max22(right_L)
        return max(a,b)
    
    print max22([4,8,15,16,23,42])
    

答案 2 :(得分:4)

你的问题是你不能处理不均匀的分裂。列表可能会使用您的代码变空,但您也可以停止使用大小1和2而不是0和1,这更自然(因为您返回最大值,零大小列表没有最大值)。

def max22(L,left,right):

    if left == right:
        # handle size 1
        return L[left]

    if left + 1 == right:
        # handle size 2
        return max(L[left], L[right])

    # split the lists (could be uneven lists!)
    split_index = (left + right) / 2
    # solve two easier problems
    return max (max22(L, left, split_index), max22(L, split_index, right))

print max22([1,20, 3], 0, 2)

说明:

失去列表理解,你不必创建新的列表,因为列表中有索引。

在处理递归时,你必须考虑:

1 - 停止条件,在这种情况下有两个因为列表拆分可能不均匀,使得递归停止在不平衡的条件下。

2 - 更简单的问题步骤。假设我可以解决一个更容易的问题,我该如何解决这个问题呢?这通常是递归函数末尾的内容。在这种情况下,在两个较小(索引方向)列表上调用相同的函数。如果您熟悉它,递归看起来很像归纳证明。

Python更喜欢明确完成的事情。虽然Python具有一些功能特性,但是最好让代码的读者知道你要评价的是什么,而不是让人们抓住他们头脑的大单行。

祝你好运!