简体   繁体   中英

using recursion to find the maximum in a list

I'm trying to find the maximum element in a list using recursion. the input needs to be the actual list, the left index, and the right index.

I have written a function and can't understand why it won't work. I drew the recursion tree, ran examples of lists in my head, and it makes sense , that's why it's even harder to find a solution now ! (it's fighting myself basically).

I know it's ugly, but try to ignore that. my idea is to split the list in half at each recursive call (it's required), and while the left index will remain 0, the right will be the length of the new halved list, minus 1.

the first call to the function will be from the tail function.

thanks for any help, and I hope I'm not missing something really stupid, or even worse- not even close ! by the way- didn't use slicing to cut list because I'm not allowed.

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)

input example - for max_list22([1,20,3]) the output will be 20.

The problem is that you aren't handling empty lists at all. max_list22([]) recurses infinitely, and [L[i] for i in range(((left+right)//2)+1, right)] eventually produces an empty list.

First off, for the sake of clarity I suggest assigning your list comprehensions to variables so you don't have to write each one twice. This should make the code easier to debug. You can also do the same for the (left+right)//2 value.

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

I see four problems with this code.

  1. On your b = line, the second argument is using len(left_L) instead of len(right_L) .
  2. You're missing an element between left_L and right_L . You should not be adding one to mid in the right_L list comprehension.
  3. You're missing the last element of the list. You should be going up to right+1 in right_L , not just right .
  4. Your mid value is off by one in the case of even sized lists. Ex. [1,2,3,4] should split into [1,2] and [3,4], but with your mid value you're getting [1] and [2,3,4] . (assuming you've already fixed the missing element problems in the previous bullet points).

Fixing these problems looks like:

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

And if you insist on not using temporary variables, it looks like:

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

Bonus style tip: you don't necessarily need three arguments for max22 , since left is always zero and right is always the length of the list minus one.

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

Your problem is that you don't handle uneven splits. Lists could become empty using your code, but you can also stop on sizes 1 and 2 instead of 0 and 1 whichi s more natural (because you return a max, zero size lists don't have a max).

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)

Notes:

Lose the list comprehension, you don't have to create new lists since you have indices within the list.

When dealing with recursion, you have to think of:

1 - the stop condition(s) , in this case there are two because list splits can be uneven, making the recursion stop at uneven conditions.

2 - the easier problem step . Assuming I can solve an easier problem, how can I solve this problem? This is usually what's in the end of the recursive function. In this case a call to the same function on two smaller (index-wise) lists. Recursion looks a lot like proof by induction if you're familiar with it.

Python prefers things to be done explicitly. While Python has some functional features it's best to let readers of the code know what you're up to ratehr than having a big one-liner that makes people scratch their head.

Good luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM