Search code examples
pythonlistmax

Error when trying to find 2nd maximum value in a list


I am trying to write code for finding the second maximum value of a list.

I tried it like this:

arr = map(int, input().split())
lista = list(arr)
max_value = lista[0]
run = lista[0]
for i in lista:
    if max_value < i:
        max_value = i
for j in lista:
    if run < j and run < max_value:
        run = j
print(run)

And the second maximum value and the maximum value comes out to be the same. What is the mistake in my program?


Solution

  • The issue

    TLDR

        run = lista[0]
        ...
        if run < j and run < max_value:
    

    Should be

              v
        run = min(lista)
        ...
                       v
        if run < j and j < max_value:
    

    How to find the Issue

    1. Make the code simpler

    Comprehension instead of map - list constructor succession

    We can reduce the boilerplate of firsts lines by using a comprehension instead of a succession of map and list constructors:

    arr = map(int, input().split())
    lista = list(arr)
    

    become:

    lista = [int(n) for n in input().split()]
    

    Ask the max to Python, don't do it yourself

    The next block of your logic is here to find the max of the list. You don't need to write this code yourself, you can ask Python. This will reduce the complexity of your code, making the issue/bug easier to spot.

    max_value = lista[0]
    for i in lista:
        if max_value < i:
            max_value = i
    

    become:

    max_value = max(lista)
    

    Rename variables

    To help our brain understand what happens, let's clarify variables names:

    lista = [int(n) for n in input().split()]
    max_value = max(lista)
    run = lista[0]
    for j in lista:
        if run < j and run < max_value:
            run = j
    print(run)
    

    become:

    user_input = [int(n) for n in input().split()]
    max_value = max(user_input)
    
    second_max = user_input[0]
    for current_value in user_input:
        if second_max < current_value and second_max < max_value:
            second_max = current_value
            
    print(second_max)
    

    2. Find the issue

    Now, the code is smaller, easier to understand, with less place for error. If there is an error, we don't have to look at many places.

    What's wrong? second_max should be the 2nd maximum value, but it's not. What could cause that? An update of previous_value that shouldn't happen.

    So this is where the issue might happen.

        if second_max < current_value and second_max < max_value:
            second_max = current_value
    

    The attribution is correct, the condition should be wrong.

    This seems correct second_max < current_value since we want to update only if second_max is lower than current_value (meaning that current might be the true second_max value OR equal to max_value. So we need another condition: current_value shouldn't be the max_value, otherwise, second_max might be set to the max_value.

    And, we look at the second condition: second_max < max_value. Here is our mistake.

    Let's fix the condition, since it's current_value that should be lower than max_value. Also, the initial value of second_max need to be set at the minimum value in case the first value is the max.

    user_input = [int(n) for n in input().split()]
    max_value = max(user_input)
    
    second_max = min(user_input)
    for current_value in user_input:
        if second_max < current_value and current_value < max_value:
            second_max = current_value
    
    print(second_max)  # 55
    

    Done.

    Alternative: Use set, sorted and index

    If you want the second maximum value in a list, it is easier to sort a de-duplicated list and print the element at index 1 of the list e.g. the second element.

    Step by Step Example

    without_duplicates = {int(n) for n in input().split()}
    ordered_without_duplicates = sorted(without_duplicates, reverse=True)
    print(ordered_without_duplicates[1])
    

    Oneliner Example

    print(sorted({int(n) for n in input().split()}, reverse=True)[1])
    

    With heapq

    print(heapq.nlargest(2, {int(n) for n in input().split()})[1])