> Как оптимизировать алгоритм поиска уникального элемента без дополнительной памяти (PHP)

Уровень: senior · Роль: backend · Язык: PHP · Категория: Технические вопросы

Компании: Travelata

Стек: PHP

> Пример ответа

Короткий ответ

Оптимальный способ - XOR-свертка всех элементов массива. Поскольку a ^ a = 0 и 0 ^ a = a, парные элементы взаимно уничтожаются, а уникальный остаётся. Сложность O(n) по времени и O(1) по памяти. В PHP это делается циклом foreach с оператором ^. Альтернатива для строк - суммирование по байтам, но XOR универсален для целых чисел и не зависит от порядка элементов.

Подробное объяснение

Задача: в массиве все элементы встречаются ровно дважды, кроме одного - его нужно найти без использования дополнительных структур данных (массивов, hash-таблиц, множеств).

Ключевое свойство XOR: операция коммутативна, ассоциативна, и x ^ x = 0. При последовательном применении XOR ко всем элементам парные значения сокращаются. Результат - искомый уникальный элемент.

Почему это работает для любых целых чисел, включая отрицательные и PHP-строки (при приведении к int)? Потому что XOR оперирует на битовом уровне, и сокращение пар не зависит от порядка.

Ограничения: метод применим только когда уникальный элемент ровно один и все остальные встречаются ровно дважды. Если условие другое (например, три вхождения), XOR не подойдёт - тогда потребуется сортировка или подсчёт с дополнительной памятью.

На практике

В PHP для больших массивов важно избегать array_reduce с замыканием - он создаёт накладные расходы на вызов функции для каждого элемента. Простой foreach с локальной переменной быстрее.

Для строк или смешанных данных можно привести к целому через ord() или unpack(), но это редко нужно - обычно задача даётся для целых чисел.

Если массив ассоциативный, ключи игнорируются, работаем только со значениями.

Пример кода

PHP
function findUnique(array $arr): int
{
$result = 0;
foreach ($arr as $value) {
$result ^= $value;
}
return $result;
}
// Пример использования
$data = [4, 1, 2, 1, 2];
echo findUnique($data); // 4

Для строк (если гарантированно один уникальный символ):

PHP
function findUniqueChar(string $s): string
{
$result = 0;
$len = strlen($s);
for ($i = 0; $i < $len; $i++) {
$result ^= ord($s[$i]);
}
return chr($result);
}

Как отвечать на собеседовании

Начните с уточнения условий: "Предполагаю, что все элементы кроме одного встречаются ровно дважды". Затем предложите наивное решение (hash-таблица) и объясните его недостаток - O(n) памяти. После этого переходите к XOR, подчеркнув битовую природу операции. Обязательно упомяните сложность O(n) по времени и O(1) по памяти.

Если интервьюер спрашивает про альтернативы - упомяните сортировку (O(n log n), но без дополнительной памяти) и суммирование (не работает для отрицательных чисел и переполнений). XOR - самый элегантный вариант.

Что проверяет интервьюер

  • Понимание битовых операций и их свойств.
  • Умение оценивать сложность алгоритмов по времени и памяти.
  • Способность видеть неочевидные решения (XOR вместо hash-таблицы).
  • Практические знания PHP: производительность циклов, особенности работы с типами.
  • Внимательность к деталям: корректность для пустого массива, отрицательных чисел, больших значений.

Типичные ошибки

  • Использование array_count_values или array_flip - это дополнительная память O(n).
  • Применение XOR к строкам без преобразования - PHP приведёт строку к int, что даст неверный результат.
  • Забывают про случай пустого массива - вернётся 0, хотя по условию уникальный элемент должен быть.
  • Предлагают суммирование: array_sum - не работает для отрицательных чисел и может переполниться.
  • Используют array_reduce с замыканием - медленнее, чем foreach на больших данных.
  • Не проверяют, что условие "ровно два вхождения" соблюдено - XOR даст мусор для других кратностей.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью