Извлеките два самых высоких элемента из списка, содержащего 100 000 целых чисел

Как получить два самых высоких элемента из списка, содержащего 100 000 целых чисел, без необходимости сначала сортировать весь список?

Ответ 1

В Python используйте heapq.nlargest. Это самый гибкий подход, если вы хотите обрабатывать больше, чем только два верхних элемента.

Вот пример.

>>> import heapq
>>> import random
>>> x = range(100000)
>>> random.shuffle(x)
>>> heapq.nlargest(2, x)
[99999, 99998]

Документация: http://docs.python.org/library/heapq.html#heapq.nlargest

Ответ 2

Ответ JacobM - это абсолютно путь. Тем не менее, есть несколько вещей, которые следует иметь в виду при реализации того, что он описал. Здесь небольшой учебник по игре на дому, который поможет вам преодолеть сложные проблемы решения этой проблемы.

Если этот код предназначен для использования в производстве, используйте один из наиболее эффективных/кратких ответов. Этот ответ нацелен на кого-то нового для программирования.

Идея

Идея проста.

  • Сохраняйте две переменные: largest и second_largest.
  • Перейдите по списку.
    • Если элемент больше, чем largest, назначьте его largest.
    • Если элемент больше, чем second_largest, но меньше largest, назначьте его second_largest.

Начало работы

Пусть начнется.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    for item in inlist:
        if item > largest:
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [3, 2, 1]
    print two_largest(inlist)

Хорошо, теперь у нас есть ответ JacobM как функция Python. Что происходит, когда мы пытаемся запустить его?

Traceback (most recent call last):
  File "twol.py", line 10, in <module>
    print two_largest(inlist)
  File "twol.py", line 3, in two_largest
    if item > largest:
UnboundLocalError: local variable 'largest' referenced before assignment

По-видимому, нам нужно установить largest, прежде чем мы начнем цикл. Вероятно, это означает, что мы должны установить также second_largest.

Инициализация переменных

Установите largest и second_largest в 0.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    largest = 0 # NEW!
    second_largest = 0 # NEW!
    for item in inlist:
        if item > largest:
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [3, 2, 1]
    print two_largest(inlist)

Хорошо. Позвольте запустить его.

(3, 2)

Отлично! Теперь пусть тест с inlist будет [1, 2, 3]

    inlist = [1, 2, 3] # CHANGED!

Попробуйте.

(3, 0)

... Uh oh.

Фиксация логики

Наибольшее значение (3) представляется правильным. Второе по величине значение совершенно неверно. Что происходит?

Пусть работает то, что выполняет функция.

  • Когда мы начинаем, largest равно 0, а second_largest также равно 0.
  • Первый элемент в списке, который мы смотрим, равен 1, поэтому largest становится 1.
  • Следующий элемент равен 2, поэтому largest становится 2.

Но как насчет second_largest?

Когда мы присваиваем новое значение largest, наибольшее значение фактически становится вторым по величине. Нам нужно показать, что в коде.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    largest = 0
    second_largest = 0
    for item in inlist:
        if item > largest:
            second_largest = largest # NEW!
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [1, 2, 3]
    print two_largest(inlist)

Запустите его.

(3, 2)

Фантастические.

Инициализация переменных, часть 2

Теперь попробуйте его со списком отрицательных чисел.

    inlist = [-1, -2, -3] # CHANGED!

Запустите его.

(0, 0)

Это не так. Откуда взялись эти нули?

Оказывается, начальные значения для largest и second_largest были фактически больше всех элементов в списке. Первое, что вы могли бы подумать, это установить largest и second_largest на минимальные значения, доступные в Python. К сожалению, Python не имеет минимально возможного значения. Это означает, что даже если вы установили оба из них на 1 000 000 000 000 000 000, вы можете иметь список значений, меньших этого.

Так что лучше всего делать? Попробуйте установить largest и second_largest на первый и второй элементы в списке. Затем, чтобы избежать двойного подсчета всех элементов в списке, мы смотрим только на часть списка после второго элемента.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    largest = inlist[0] # CHANGED!
    second_largest = inlist[1] # CHANGED!
    # Only look at the part of inlist starting with item 2
    for item in inlist[2:]: # CHANGED!
        if item > largest:
            second_largest = largest
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [-1, -2, -3]
    print two_largest(inlist)

Запустите его.

(-1, -2)

Отлично! Попробуйте использовать другой список отрицательных чисел.

    inlist = [-3, -2, -1] # CHANGED!

Запустите его.

(-1, -3)

Подождите, что?

Инициализация переменных, часть 3

Повторите шаг назад через нашу логику.

  • largest установлено значение -3
  • second_largest установлено значение -2

Подождите прямо там. Это уже кажется неправильным. -2 больше -3. Это вызвало проблему? Продолжим.

  • largest установлено значение -1; second_largest устанавливается на старое значение largest, которое составляет -3

Да, это проблема. Нам нужно убедиться, что largest и second_largest установлены правильно.

def two_largest(inlist):
    """Return the two largest items in the sequence. The sequence must
    contain at least two items."""
    if inlist[0] > inlist[1]: # NEW
        largest = inlist[0]
        second_largest = inlist[1]
    else: # NEW
        largest = inlist[1] # NEW
        second_largest = inlist[0] # NEW
    # Only look at the part of inlist starting with item 2
    for item in inlist[2:]:
        if item > largest:
            second_largest = largest
            largest = item
        elif largest > item > second_largest:
            second_largest = item
    # Return the results as a tuple
    return largest, second_largest

# If we run this script, it will should find the two largest items and
# print those
if __name__ == "__main__":
    inlist = [-3, -2, -1]
    print two_largest(inlist)

Запустите его.

(-1, -2)

Excellent.

Заключение

Итак, вот код, красиво прокомментированный и отформатированный. У него также были все ошибки, которые я мог найти из него. Наслаждайтесь.

Однако, полагая, что это действительно вопрос домашней работы, я надеюсь, что вы получите некоторый полезный опыт, увидев, что несовершенная часть кода медленно улучшилась. Я надеюсь, что некоторые из этих методов будут полезны в будущих назначениях программирования.


Эффективность

Не очень эффективно. Но для большинства целей это должно быть хорошо: на моем компьютере (Core 2 Duo) список из 100 000 элементов может быть обработан за 0,27 секунды (с использованием timeit, усредненного более чем на 100 прогонов).

Ответ 3

Вы перебираете список, сохраняя переменные, которые содержат значение самого высокого и второго наивысшего элемента, обнаруженного до сих пор. Каждый новый элемент, который встречается, заменяет любой из двух новых элементов выше (если они есть).

Ответ 4

Действительно гладкий способ - использовать heapq. Измените массив (O (n)), затем просто введите много элементов, которые вам нужны (log (n)). (Увидел этот вопрос в одном интервью, хороший вопрос, чтобы иметь в виду.)

Ответ 5

"2 высших" невозможно; Только один элемент может быть "самым высоким". Возможно, вы имеете в виду "самое высокое 2". В любом случае вам нужно сказать, что делать, когда список содержит дубликаты. Чего вы хотите от [8, 9, 10, 10]: (10, 9) или (10, 10)? Если ваш ответ (10, 10), пожалуйста, рассмотрите ввод [8, 9, 10, 10, 10]. Что вы собираетесь делать с "самыми высокими двумя", когда вы их получите? Измените свой вопрос, чтобы дать это руководство.

В то же время, вот ответ, который берет первый подход (два уникальных значения):

largest = max(inlist)
second_largest = max(item for item in inlist if item < largest)

Вы должны добавить в список защиты от менее 2 уникальных значений.

Ответ 6

Это будет работать, но я не знаю, хотите ли вы сохранить элементы в списке:

max1 = max(myList)
myList.remove(max1)
max2 = max(myList)

Если вы это сделаете, вы можете сделать это:

max1 = max(myList)
idx1 = myList.index(max1)
myList.pop(idx1)

max2 = max(myList)
myList.insert(idx1,max1)

Ответ 7

Скопируйте List в List_copy. Получить наивысшее значение и получить его позицию:

Highest_value = max(List_copy)
Highest_position = List_copy.index(max(List_copy))

Назначьте 0 в Highest_value.

List_copy[Highest_position] = 0

И снова запустите свою линию.

Second_Highest = max(List_copy)

Ответ 8

Итерация по всему списку - единственный способ сделать это без сортировки.

Ответ 9

Без сортировки списка единственный способ сделать это - перебрать весь список и сохранить самые высокие два числа. Я думаю, вам будет лучше сортировать список.

Ответ 10

Второй наивысший элемент - довольно простой случай, но для k-го наивысшего элемента вам нужен алгоритм выбора. Эта страница довольно тщательная, поэтому, вероятно, лучше всего это прочитать.

Ответ 11

Лучшее время, которое вы можете ожидать, является линейным, так как вам нужно хотя бы просмотреть все элементы.

Вот мой псевдокод для решения проблемы:

//assume list has at least 2 elements
(max, nextMax) = if (list[0] > list[1])
                 then (list[0], list[1])
                 else (list[1], list[0])

for (2 <= i < length) {
    (max, nextMax) = if       (max < list[i])     => (list[i], max)
                     elseif   (nextMax < list[i]) => (max, list[i])
                     else     (no change)         => (max, nextMax)
}

return (max, nextMax)

Ответ 12

Я знаю, что эта тема устарела, но вот простое решение этой проблемы. Протестировано против heapq.nlargest, и это немного быстрее (сортировка не требуется):

Работает как для положительных, так и для отрицательных чисел.

Функция ниже: Максимальное используемое время: 0,12, максимальная используемая память: 29290496 heapq.nlargest: Максимальное используемое время: 0.14, максимальная используемая память: 31088640

def two_highest_numbers(list_to_work):

    first = None
    second = None

    for number in list_to_work:
        if first is None:
            first = number
        elif number > first:
            second = first
            first = number
        else:
            if second is None:
                second = number
            elif number > second:
                second = number

return [first, second]

Ответ 13

Другое решение, которое использует только базовые функции Python, можно увидеть ниже:

>>> largest = max(lst)
>>> maxIndex = lst.index(largest)
>>> secondLargest = max(max(lst[:maxIndex]), max(lst[maxIndex+1:]))

Если мы разделим список вокруг его наибольшего числа, мы знаем, что второе по величине число находится либо в левой половине, либо в правой половине. Таким образом, мы можем тривиально найти второе по величине число, просто найдя наибольшее из наибольшего числа в левой и правой половине списка.

Тривиально показать, что это O (n) время и O (1) пространство. Мы просматриваем список один раз, чтобы найти самый большой элемент, затем снова, чтобы найти второй по величине. Мы храним только самые большие значения и индекс наибольшего значения.

Ответ 14

Сортируйте список, и если список не нулевой, извлеките последние два элемента

>>> a=[0,6,8,5,10,5]
>>> a.sort()
>>> a
[0, 5, 5, 6, 8, 10]
>>> if a:
...  print a[-1],a[-2]
... 
10 8

Простой и самый эффективный :)

Теперь, если сортировка не требуется, найдите max, удалите max, найдите max снова

>>> a=[0,6,8,5,10,5]
>>> max(a)
10
>>> a.remove(max(a))
>>> max(a)
8
>>> 

Конечно, вы потеряете исходный список, но вы также можете создать временный список.

Ответ 15


my_list = [20, 1, 9, 5, 10, 3, 4, 2, 11, 21, 2]

max2 = 0
max1 = 0
for i in my_list:
    if i > max1:
        max1 = i
    elif max2 < i < max1:
        max2 = i

print(f'max1: {max1}; max2: {max2}')
max1: 21; max2: 11