Итак, вы здесь. Освобожден. Измученные. Вы наконец-то нашли подход к решению сложного вопроса о кодировании, который задает вам интервьюер. Возможно, вы даже написали это на доске, строка за строкой. И вы хорошо провели время! У тебя всего 20 минут до встречи. Ваш интервьюер должен быть впечатлен.
Правильно?
«Это сработает, но есть идеи, как сделать это более эффективно?»
Ваше сердце падает. Вы думали, что закончили с хитрой конструкцией алгоритма! Вы пытаетесь придумать больше способов решения проблемы, но все, что вы можете придумать, - это единственный подход, который вы уже придумали.
Это происходит почти со всеми. И это не потому, что они глупы. Это потому, что у большинства людей нет метода повышения эффективности их алгоритмов.
Но правда в том, что их много. В следующий раз, когда вы озадачены, попробуйте применить эти три общих подхода.
1. Используйте Hash Map
Вот так. Хэш-карты / ассоциативные массивы / словари (они называются многими именами, в зависимости от того, какой язык программирования вы используете) обладают магической способностью сокращать время выполнения алгоритмов.
Например, предположим, что вопрос заключается в том, чтобы найти наиболее повторяющееся число в массиве чисел.
Ваша первая мысль может быть прыгнуть в несколько петель. Для каждого из наших чисел вычислите его количество и посмотрите, является ли оно самым большим. Как мы можем получить количество для каждого числа? Цикл по массиву, считая, сколько раз это происходит! Итак, мы говорим о двух вложенных циклах. В псевдокоде:
def get_mode (nums): max_count = 0 mode = null для потенциала_мод в числах: count = 0 для числа в our_array: count + = 1, если count> = max_count: mode = потенциала_моде max_count = счетчик возврата
Прямо сейчас мы перебираем весь наш массив один раз для каждого элемента в массиве, но мы можем добиться большего. В больших обозначениях O это всего O (n 2 ).
Если мы храним наши счетчики в хэш-карте (сопоставляя числа с их счетами), мы можем решить проблему всего за один проход по массиву (O (n) раз!):
def get_mode (nums): max_count = 0 mode = null count = new HashMap, начиная каждое значение с 0 для factor_mode в числах: count + = 1, если count> max_count: mode = потенциал_моде max_count = считает режим возврата
Намного быстрее!
2. Используйте битовую манипуляцию
Это действительно отличит вас от стаи. Это не относится к каждой проблеме, но если вы держите это в заднем кармане и выбиваете его в нужное время, вы будете выглядеть как рок-звезда.
Вот пример: Предположим, у нас был массив чисел, где каждое число появляется дважды, за исключением одного числа, которое встречается только один раз. Мы пишем функцию, чтобы найти одинокое, неповторяющееся число.
Ваш первый инстинкт может заключаться в использовании хэш-карты, поскольку мы только что говорили об этом. Это хороший инстинкт! И это сработает для этого. Мы можем сделать очень похожую карту «подсчетов» и использовать ее, чтобы увидеть, какое число заканчивается счетом 1.
Но есть еще лучший способ. Если вы знакомы с битовыми манипуляциями, возможно, вы знакомы с XOR. Особенностью XOR является то, что если вы XOR числа с самим собой, биты «отменяются» до 0. Для этой проблемы, если мы XOR каждого числа в массиве вместе, мы останемся с одним числом, которое не не отменять:
def find_unrepeated (nums): unrepeated = 0 для num в nums: unrepeated = неповторимый XOR num вернуть неповторенный
3. Снизу вверх
Напишите функцию, которая выводит «n-е» число Фибоначчи, учитывая число n. Это классика, и она очень хорошо подходит для рекурсии:
def fib (n): если n равно 0 или 1: вернуть 1, вернуть fib (n-1) + fib (n-2)
Но простой рекурсивный ответ не единственный! Тщательно продумайте, что делает эта функция. Предположим, что n равно 5. Чтобы получить ответ, он рекурсивно вызывает fib (4) и fib (3). Теперь, что это делает с fib (4)? Он вызывает fib (3) и fib (2). Но мы только что сказали, что у нас уже был вызов fib (3)! Эта милая рекурсивная функция выполняет много повторений. Общая стоимость времени оказывается O (2 n ). Это плохо - намного хуже, чем O (n 2 ).
Вместо того, чтобы рекурсивно переходить от n к 1, давайте перейдем «снизу вверх» от 1 к n. Это позволяет нам пропустить рекурсию:
def fib (n): предыдущий = 0 предыдущий_предыдущий = 1 для i в диапазоне от 1 до n: текущий = предыдущий + предыдущий_предыдущий предыдущий_предыдущий = предыдущий предыдущий = текущий возвращаемый ток
Код длиннее, но гораздо эффективнее! Вплоть до O (n) времени. В качестве дополнительного бонуса при развертывании рекурсивных алгоритмов мы экономим место. Все эти рекурсивные вызовы накапливаются в стеке вызовов, который находится в памяти и учитывает стоимость нашего пространства. Наша рекурсивная функция имела O (n) пространственную стоимость, но эта итерационная занимает O (1) пространство.
В следующий раз, когда ваш интервьюер попросит вас повысить эффективность вашего решения, попробуйте пройтись по этим стратегиям и посмотреть, помогут ли они. При достаточной практике вы, скорее всего, поймете, что прыгаете прямо к оптимизированному решению, пропуская более наивное решение. И это отличная вещь. Это не просто означает, что вы становитесь лучшим интервьюером, это означает, что вы становитесь лучшим инженером.