Рубрики
Без рубрики

[Google собеседование] Найдите K-годный элемент в несортированном массиве

Вы тренируетесь для вашего предстоящего кодирования собеседования? Этот вопрос был запрошен Google, как сообщается в нескольких случаях программистами по всему миру. Можете ли вы решить это оптимально? Давайте сначала погрузимся в проблему. Формулировка проблем с заданием целочисленного массива или списка Python Nums и целочисленное значение k. Найдите и верните … [собеседование Google] Найти K-T-й по величине элемент в несортированном массиве Подробнее »

Автор оригинала: Chris.

Вы тренируетесь для вашего предстоящего кодирования собеседования? Этот вопрос был запрошен Google, как сообщается в нескольких случаях программистами по всему миру. Можете ли вы решить это оптимально?

Давайте сначала погрузимся в проблему.

Постановка проблемы

Дано целочисленный массив или Список Python Nums и целочисленное значение к Отказ

Найти и вернуть k-th самый большой элемент в массиве.

Ограничения: Вы можете предположить, что к это число между 1 и длиной Nums список.

  • 1 длиной

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

Примеры

Давайте посмотрим на некоторые примеры для улучшения нашего понимания этой проблемы.

Example 1
Input: [1, 2, 3, 4, 5], k=2
Output: 4

Example 2
Input: [42, 1, 3, 2], k=1
Output: 42

Example 3
Input: [3], k=1
Output: 3

Example 4
Input: [3, 42, 30, 1, 32, 100, 44, 13, 28, 99, 100000], k=4
Output: 44

Видео-решение

Вы можете посмотреть меня объяснить этот вопрос интервью в следующем видео:

Наивное решение: сортировка

Самый простой способ вернуть к –й самый большой элемент из списка выглядит следующим образом:

  • Сортируйте список в порядке убывания. Самый большой элемент находится в положении 0.
  • Доступ к (K-1) -ный элемент отсортированного списка и вернуть его. Это к –й самый большой элемент.

Вот код, который достигает этого:

def find_k_largest_element(nums, k):
    sorted_nums = sorted(nums, reverse=True)
    return sorted_nums[k-1]

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

Учитывая сортированный список, вам нужно получить доступ к к -ный элемент из списка. Как мы используем Индексирование на основе нуля в Python, к -мерский элемент имеет индекс (K-1) Отказ

Давайте запустим это на наших примерах:

# Example 1
lst = [1, 2, 3, 4, 5]
k = 2
print(find_k_largest_element(lst, k))
# 4

# Example 2
lst = [42, 1, 3, 2]
k = 1
print(find_k_largest_element(lst, k))
# 42

# Example 3
lst = [3]
k = 1
print(find_k_largest_element(lst, k))
# 3

# Example 4
lst = [3, 42, 30, 1, 32, 100, 44, 13, 28, 99, 100000]
k = 4
print(find_k_largest_element(lst, k))
# 44

Да, это проходит все тесты!

Анализ : Код состоит из двух строк: сортировка списка и доступа к к -ный элемент из отсортированного списка. Доступ к элементу с данным индексом имеет постоянную сложность времени выполнения O (1) Отказ Следовательно, выполнение алгоритма, следовательно, доминирует в среде выполнения для сортировки списка с N элементы. Без какой-либо дополнительной информации о списке мы должны предположить, что сложность временной среды выполнения в худшем случае является O (n log n) Так что он растет сверхлинейно с увеличением количества элементов.

Обсуждение : Интуитивно, мы делаем много ненужных работ при сортировке списка, учитывая, что мы заинтересованы только в к –й самый большой элемент. Все меньшие элементы не интересуются нам. Мы наблюдаем, что нам нужно знать (K-1) большие элементы, так что мы можем выяснить к -th по величине. Есть ли лучший способ, чем O (n log n) ?

Итеративно удаление максимума

Наблюдение : Поиск крупнейшего элемента имеет только линейную сложность выполнения O (n) : Нам нужно перейти в список один раз и сравнить каждый элемент к текущему максимуму. Если текущий элемент больше, мы просто обновляем наш максимум. После пересечения всего списка мы определили максимум только с N-1 сравнения.

  • Если k = 1 это уже решение и сложность времени выполнения – O (n) вместо O (n log n) Отказ
  • Если k> 1 , мы можем повторить одну и ту же процедуру в меньшем списке – каждый раз удаляя текущий максимум из списка.

Общая сложность времени выполнения будет O (k * n) потому что нам нужно выполнить N Сравнение, чтобы найти один максимум и повторить это к раз.

Следующий код реализует этот точный алгоритм:

def find_k_largest_element(nums, k):
    for i in range(k-1):
        nums.remove(max(nums))
    return max(nums)

В каждой итерации Я мы убираем максимум. Мы неоднократно удаляем максимум (K-1) времена контролируемыми Диапазон () функция. После завершения цикла максимум в списке является K-T-й по величине элемент. Это то, что мы возвращаем к пользователю.

Обсуждение : Этот алгоритм имеет сложность выполнения O (k * n) По сравнению с сложностью времени выполнения метода сортировки O (n log n) Отказ Итак, если k На самом деле это более эффективный алгоритм. Однако для K> Журнал (N) Этот алгоритм будет хуже!

Можем ли мы сделать лучше?

Гибридное решение, чтобы получить лучшее из обоих миров

В предыдущем обсуждении мы заметили, что если K> Журнал (N) Мы должны использовать алгоритм на основе сортировки и если k Мы должны использовать алгоритм, основанный на неоднократных удалении максимума. Для k = log (n) , это не имеет значения. К счастью, мы можем использовать эту простую проверку в начале нашего кода, чтобы определить лучший алгоритм для выполнения.

import math


def find_k_largest_sort(nums, k):
    sorted_nums = sorted(nums, reverse=True)
    return sorted_nums[k-1]


def find_k_largest_remove_max(nums, k):
    for i in range(k-1):
        nums.remove(max(nums))
    return max(nums)


def find_k_largest_element(nums, k):
    n = len(nums)
    if k > math.log(n, 2):
        return find_k_largest_sort(nums, k)
    else:
        return find_k_largest_remove_max(nums, k)

Код показывает функцию find_k_larget_Element Это либо выполняет сортировочный алгоритм, если K> Журнал (N) или алгоритм на основе удаления в противном случае.

Обсуждение : Объединяя оба алгоритмы таким образом, общая сложность времени выполнения падает на O (min (k, log (n)) * n) что лучше, чем либо O (n * log (n)) или O (n * k) Отказ

Можем ли мы сделать еще лучше?

Лучшее решение с отсортированным списком Top K элементов

Алгоритм на основе удаления имеет основную проблему, которую нам нужно выполнить мин () вычисление на весь список. Это частично избыточная работа. Давайте рассмотрим альтернативную идею на основе скользящего окна, которое в значительной степени удаляет накладные расходы вычислений мин.

Идея следующего алгоритма – поддерживать окно к самые большие элементы в отсортированном порядке. Первоначально вы заполняете окно первым к элементы из списка. Затем вы добавляете один элемент в окно за раз, но только если он больше, чем минимум из окна. Хитрость в том, что как окно к Элементы отсортированы, доступ к окну имеет O (1) постоянная сложность времени выполнения. Тогда вы повторите это поведение (N-K) раз.

Вот пример пробега алгоритма:

Вы начинаете со списка [5, 1, 3, 8, 7, 9, 2] и отсортированное окно [1, 3, 5] Отказ В каждой итерации вы проверяете, если текущий элемент больше минимального в положении 0 отсортированного окна. Для элементов 8, 7 и 9, это действительно так. В этих случаях вы выполняете операцию отсортированной вставки, чтобы добавить новый элемент в окно после удаления предыдущего минимума из окна. После одного полного прогона у вас будут крупные элементы K в окне.

Вот анализ времени выполнения алгоритма, который показывает, что время выполнения только O (n log k) Что самое лучшее, которое мы достигли до сих пор.

Давайте посмотрим на код:

import bisect


def find_k_largest_element(nums, k):
    window = nums[:k]
    for element in nums[k:]:
        if element > window[0]:
            # Remove minimum from window
            window = window[1:]

            # Sorted insert of new element
            bisect.insort(window, element)
    return window[0]

Код использует bisect.insort () Способ выполнения отсортированной вставки в окно. Вы должны знать, насколько фактически работает сортированная вставка. Однако в кодирующем интервью вы обычно можете предположить, что у вас есть доступ к базовым внешним функциям. Вот базовый рециллер по идее отсортированной вставки:

💡 Концепция отсортирована вставка: Чтобы вставить элемент в отсортированный список, вы пик среднего элемента в списке и проверьте, больше или меньше, чем элемент, который вы хотите вставить. Если он больше, все элементы справа будут также больше, и вы можете их пропустить. Если средний элемент меньше, все элементы слева будут также меньшими, и вы можете их пропустить. Затем вы повторите одинаковую добавьте потенциальные элементы каждый раз, пока вы не найдете правильное положение, чтобы вставить новый элемент.

Как сортированная вставка несколько раз половины интервала, только это занимает O (log k) Операции для вставки нового элемента в отсортированный список с к элементы. Это основная идея всего алгоритма, поэтому убедитесь, что вы понимаете это!

Этот вопрос Google собеседования является частью нашего предстоящего Академия информатики Finxter курс. Проверьте это!

Работая в качестве исследователя в распределенных системах, доктор Кристиан Майер нашел свою любовь к учению студентов компьютерных наук.

Чтобы помочь студентам достичь более высоких уровней успеха Python, он основал сайт программирования образования Finxter.com Отказ Он автор популярной книги программирования Python One-listers (Nostarch 2020), Coauthor of Кофе-брейк Python Серия самооставленных книг, энтузиаста компьютерных наук, Фрилансера и владелец одного из лучших 10 крупнейших Питон блоги по всему миру.

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