В прошлом году я провел бессчетные часы на собеседованиях с кандидатами на разные должности в компании Facebook. И, так как теперь мне довелось побывать по обе стороны процесса отбора, я хотел бы помочь вам – студентам, которые пытаются попасть на первую в жизни интернатуру, более опытным разработчикам, которые готовятся перейти в другую компанию, или тем, кто хочет выйти в программирование из совершенно другой профессиональной среды.
В этой статье я хочу изложить самые важные уроки, которые вынес из опыта проведения собеседований с программистами в Facebook. Надеюсь, они прольют свет на некоторые особенности этого процесса, который очень и очень многим сильно выматывает нервы.
Формат
Собеседования для программистов, как правило, проходят в форме беседы, длятся 45 минут и имеют своей целью проверить ваше знание структур данных и алгоритмов. Интернам обычно требуется пройти только одно собеседование, где они показывают свое умение писать код. Разработчикам более высокого уровня, вероятно, придется посетить два-три собеседования с написанием кода, одно-два собеседования, где будут проверяться навыки проектирования систем, а также отдельная встреча для оценки личных качеств. Здесь я буду говорить только о собеседованиях, посвященных коду.
Недавно меня спросили: «Как быть, если я вот так с ходу не могу найти решения для поставленной задачи?». Я ответил: «Ну, если задача подобрана правильно, то вы и не должны его сразу найти». Иначе какой в этом вообще смысл? Цель собеседования – понять, насколько вы сильны в программировании. В команде Facebook информацию, которая дает ответ на этот вопрос, называют сигналом. Тот, кто проводит собеседование, стремится извлечь из него максимум сигналов. Иными словами, если мы поймем, что с предложенной задачей вы уже знакомы, наша обязанность – дать вам другую.
Мы должны увидеть, как вы справляетесь со сложностями. Если вы, по воле случая, можете наизусть процитировать решение прямиком из книги Cracking The Coding Interview, то о ваших способностях к решению проблем мы ничего не узнаем.
Лучшие практики для собеседований
Лучшие из лучших кандидатов становятся движущей силой собеседования – они сами ведут разговор и практически не нуждаются в том, чтобы сотрудник компании подталкивал их в нужном направлении. Обычно такие программисты по наитию, без подсказок со стороны, делают следующее:
- Задают уточняющие вопросы
- Анализируют варианты решения, их плюсы и минусы
- Начерно набрасывают код
- Показывают реализацию решения
- Тестируют свое решение
Не путайте инициативу со спешкой. Активная позиция вовсе не предполагает, что вы должны немедленно бросаться писать код. Даже наоборот, если вы приступаете к коду в первые пять минут разговора, это может сильно испортить впечатление. Первый шаг к блестящему собеседованию – умные уточняющие вопросы.
Умные уточняющие вопросы
Прежде чем взяться за решение, вам нужно хорошо понять задачу. Несколько продуманных уточнений могут серьезно повысить ваши шансы на успех. Вот несколько для примера:
- Это нужно сделать без привлечения дополнительной памяти?
- На какие вводные данные мы должны ориентироваться?
- Что важнее – производительность или низкий расход памяти?
Таким образом вы сможете сосредоточиться на том, что действительно имеет значение, и выбросить из головы все остальное. Знать, о чем можно не думать, не менее ценно, чем знать, что требует особого внимания.
Не домысливайте
Очень часто кандидаты начинают добавлять в задачу какие-то домыслы от себя (переменные – только положительные числа, массивы не могут быть пустыми, все вводные данные безопасны). Это уже серьезный звоночек. Никогда не подгоняйте условия так, чтобы вам удобнее было найти решение. Спрашивайте.
«Мы исходим из того, что все значения положительные?»
Проще некуда. Если ответят, что да – замечательно. Никаких дополнительных проверок не требуется. Если же нет, то хватит одного-единственного оператора условия, чтобы защитить ваш код от любых случайностей. Нередко при помощи подобных вопросов можно получить и указание на то, в каком направлении нужно двигаться.
Варианты решения
Программисты, проводящие собеседования, очень любят, когда кандидаты освещают несколько путей решения. Это показывает, что вы понимаете: к любой задаче можно подойти с разных сторон, и, что еще более важно, вынуждает собеседующего подсказать вам без прямых просьб с вашей стороны. Йееее!
Мы не можем просто взять и выложить вам правильный ответ. Но если вы предлагаете два варианта, А и Б, и спрашиваете: «Какой подход, на ваш взгляд, здесь уместнее?», то мы уж точно выберем то, что больше похоже на искомое.
Набросайте свое решение в виде кода
На технических собеседованиях писать чаще всего приходится на доске. Соответственно, вставлять операторы когда и куда захочется не выйдет. Вы должны хорошо представлять себе, что собираетесь делать, прежде чем начнете писать.
Сделайте глубокий вдох и начните планировать свой код. Это может быть черновой код, это может быть схема, главное, чтобы вы знали, какие структуры данных в них будут использоваться и какие переменные будут представлять для вас интерес. Думаю, никто не захочет, чтобы итог его работы выглядел как-то так:
Пропишите реализацию
На этом этапе все обычно и буксуют, хотя по-хорошему так быть не должно. По идее, реализация решения – это самое простое. Вы задали умные вопросы, рассмотрели разные подходы, продумали алгоритм – осталось только все расписать. А пока расписываете, не забывайте про…
Общение!
Говорите вслух. Довольно сложно подвести вас к нужной мысли, если я вообще не знаю, о чем вы думаете. Если вас несет куда-то не туда, я вмешаюсь. Если вы двигаетесь в верном направлении, скорее всего, не буду вас сбивать.
Однако нужно сделать оговорку: здесь многое решает личный стиль ведения собеседований. Кто-то вмешивается в ход решения более активно, кто-то предпочитает оставаться в стороне.
Тестирование
Как ни странно, этим шагом пренебрегают чаще всего. Я бы сказал, что 98% разработчиков, которые были на моих собеседованиях, стоило бы больше внимания уделить проверке своих решений.
В начале собеседования кандидату вместе с задачей обычно дается вариант тестирования. По завершению работы над решением они прогоняют код через соответствующий тест. Но тут есть одна проблема: мы даем вам примитивнейший вариант тестирования. Он, как правило, не затрагивает пограничные случаи и не дает возможности проверить код как следует. При этих параметрах ваш алгоритм дает нужные выходные данные, при других может и не дать.
Самый простой способ блеснуть на техническом собеседовании – писать тесты. Чем больше, тем лучше. Чем сложнее, тем лучше. Чем всеохватнее, тем лучше. В большинстве случаев это позволит вам выловить баги, прежде чем я на них укажу. А такие вещи говорят в вашу пользу.
Что делать, если не знаете, что делать
Так все-таки, как быть, если вам дали задачу, а вы вот так с ходу не можете найти решения?
Действуйте поэтапно. Повспоминайте: возможно, задача покажется вам похожей на какую-нибудь из тех, что вы решали раньше. Многие из задач, которые я предлагал на собеседованиях, были базовыми заданиями, которые разбирают на любых курсах, где изучаются алгоритмы и структуры данных – но с подвывертом.
Если в голову ничего не приходит, не впадайте в панику. Все нормально. Не надрывайтесь в попытках сразу же выйти на самое эффективное решение – начните с самого простого. Затем, приняв его на отправную точку, подумайте: какие здесь есть узкие места? Что сильнее всего требует оптимизации? Как это можно оптимизировать?
Сведите слабые места системы с сильными сторонами структур данных. Когда вам нужно сделать алгоритм более эффективным, на выручку часто (хотя не всегда) приходят структуры данных. У каждой из них есть свои достоинства и недостатки (у таблицы хэшей это скорость поиска данных, у двоичного дерева поиска – их сортировка и так далее). Самые лучшие решения получаются тогда, когда вам удается закрыть какое-то узкое место за счет сильной стороны той или иной структуры данных.
Ну, например, перед вами стоит задача:
Дано предложение, рассчитайте, сколько раз в нем появляется каждая из букв алфавита.
Если действовать методом полного перебора, вам придется вести подсчет для каждой буквы по очереди, а затем сводить данные в конечный результат. Неэффективность метода заключается в необходимости хранить и искать информацию: мы сохраняем данные, которые получили для каждой буквы, а затем извлекаем их для формирования общего итога. Если посмотреть на доступные структуры данных, можно заметить, что одна выделяется среди прочих в нужном нам отношении:
- Двоичное дерево поиска
- Массив
- Таблица хэшей
- AVL-дерево
- Стэк
- Очередь
Эффективнее всего хранит и извлекает данные таблица хэшей. Если ее использовать, анализировать предложение двадцать шесть раз не придется – хватит и одного.
Ищите зацепку
Часто в задачи по программированию внедряется зацепка, которая открывает путь к более удобному решению. Обычно это какая-нибудь мелочь, необычное условие, благодаря которому вы можете действовать с большей эффективностью, чем при других исходных. Проверьте, нет ли в вашей задаче чего-то подобного.
Допустим:
Дано два отсортированных массива типа Integer, A и B; требуется слить B с A. Предполагается, что A может разместить все элементы из B; число инициализированных элементов в массивах составляет m и n соответственно.
Задача взята непосредственно из книги How To Crack the Coding Interview. Заметили зацепку? Нам могли бы дать просто два массива для слияния, но нет: в нашем сценарии один полностью помещается в другом. Вот о таких условиях я и говорю. Если замечаете подобные оговорки, знайте, что их включили не случайно.
Здесь свободное пространство дает вам возможность оптимизировать процесс слияния. Полное решение можно посмотреть по ссылке.
Просите о помощи
Иногда бывает и такое, что вы перебрали все шаги, но так и остались в тупике. В этом случае следует просто обратиться к людям, проводящим собеседование, за помощью.
От того, что мы просидим десять минут в тишине, никому легче не станет. Если вы серьезно не имеете понятия, что делать, попросить подсказки будет лучшим выходом. В подсказках время от времени нуждается каждый из нас. Все решает то, как вы сумеете ее использовать.
В заключение
Техническое собеседование – это такой же стандартизованный тест, как и те, которые нам знакомы по выпускным и поступлению в вузы. Задачи отличаются деталями, но основные концепты и стратегии решения остаются более-менее стандартными.
Многие кандидаты срезаются на очень простых вещах: домысливают собственные условия, не проговаривают ход мысли, плохо тестируют свое решение. Все эти ошибки поддаются исправлению, и «однозначно нет» может превратиться в «берем». Используйте систему, которую я набросал в этой статье, и вы будете в хорошей форме.
Комментарии (14)
maaGames
25.10.2019 16:39+2> Эффективнее всего хранит и извлекает данные таблица хэшей. Если ее использовать, анализировать предложение двадцать шесть раз не придется – хватит и одного.
С деревом тоже 1 проход. И дерево будет эффективнее хэша, потому что получение хэша для одной буквы будет медленнее, чем испоьзование этой самой буквы.
Если только Латинские буквы, то и с массивом один проход будет.sshikov
25.10.2019 18:38Да если и не латинские тоже. Двухбайтовый код буквы вполне ложится на массив как индекс, при условии что у нас достаточно памяти (совсем не запредельно много). Хеши имеют смысл тогда, когда исходный ключ — произвольная длинная строка, а если мы подсчитываем буквы — это далеко не лучшее решение.
maaGames
25.10.2019 18:51Ну да, если юникод, то можно и 64К элемента массив использовать. А вот если мультибайт и какой-нибудь диалект китайского, который занимает больше двух байт, то с массивом могут возникнуть «неудбства» :)
sshikov
25.10.2019 19:01>Ну, например, перед вами стоит задача:
>Дано предложение, рассчитайте, сколько раз в нем появляется каждая из букв алфавита.
Ну вот же исходная задача. Тут не написано, какого размера алфавит ) А мы как-бы не в Китае сейчас. Так что автор отступает от своего же совета сразу уточнить задачу, и уже сделал вывод, что лучше всего хеш. А выходит что нифига — зависит от.
P.S. Строго говоря, машинки с терабайтом памяти тоже уже не какое-то невиданное чудо. Так что параметры железки тоже можно уточнить.maaGames
25.10.2019 19:10— Обоснуйте необходимость покупки сервера с терабайтом оперативной памяти?
— Мне тут надо посчитать, сколько раз буквы в предложении встречаются…
%)
P.S. А ещё собеседование может происходит в какой-ибудь Китайской. Короче, мы оба согласны, что заявление автора про «Эффективнее всего» было преждевременным.
Ketovdk
28.10.2019 16:28дело не в том, сколько памяти используется, а в том, что будет тяжело выделить массив такого размера уже даже по времени и быстрее будет работать с хэшами
masai
25.10.2019 21:26Если мы начинаем говорить о юникоде, то сразу возникает множество проблем. Например, нормализация: считать ли букву ё как один символ и букву ё как комбинацию е и диакритики одним и тем же символом? Если мы хотим считать вместе прописные и строчные, то возникает проблема задания локали. В общем, задача не такая простая, как кажется. :)
sshikov
25.10.2019 21:55Ну это все правда, только это другие аспекты. И да, они очень важные — но при этом к выбору хеш таблиц как структуры для хранения результата, о которой говорит автор, они вообще отношения не имеют, на первый взгляд.
maaGames
26.10.2019 06:40Причём, эта проблема будет при любом варианте реализации.
Повело рекрутеру, что мы на собеседование не пришли, а то своими уточняющими вопросами задолбали…
tifon
29.10.2019 02:40Все таки N*log(K) > N. Даже если K = 26. Более того с BST будут постоянные сбросы кэшей.
BalinTomsk
26.10.2019 04:38И после всех таких офигительных интервью оказывается что пароли клиентов тупо хранятся в открытом виде в текстовых файлах.
agent10
Вода какая-то.