TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #312 4K
​Решение задачи

Повторюсь, что какое-то решение задачи написать можно довольно легко. Банальный вариант - вложенный цикл, для каждого элемента массива ищем другой такой же элемент, у которого не совпадают индексы. Это очень просто, но очень неэффективно - квадратичное время и константные затраты памяти.

Можно чуть получше - догадаться, но в отсортированном массиве одинаковые элементы будут стоять рядом и нам нужно лишь найти тот, у которого нет пары. Время - O(nlogn), память - константная.

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

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

Математика!

Не зря вчерашний пост был про операцию xor. Это была подводка к этой задаче.

Очевидным свойством операции xor над числами - xor двух одинаковых чисел дает ноль. И ксор любого числа с нулем в результате будет давать то же самое число.

Представим себе, что числа в нашем массиве расположены определенным образом - в начале идут парные числа, а в самом конце - наше одинокое число.

Тогда мы выставим результирующую переменную в ноль и будем ксорить по порядку все числа с этим результатом. Применяя эти два свойства мы получим следующее:

3 3 2 2 7 7 5
int res = 0;
res ^= 3; // res == 3
res ^= 3; // res == 0
res ^= 2; // res == 2
res ^= 2; // res == 0
res ^= 7; // res == 7
res ^= 7; // res == 0
res ^= 5; // res == 5


по итогу результат будет равен последнему числу, то есть нужному нам ответу.

"Но это же фигня какая-то. Порядок совсем не такой! Он может быть любым!"

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

Тут можно такую аналогию провести. Вместо xor использовать операцию "+", а вместо одинаковых чисел - число и его отрицательный брат. Выглядеть это может так:

-3 + 1 + 5 + (-4) + 3 + (-1) + (-5)


У вас же не возникает сомнений, что результатом вычисления этого выражения будет -4. И я даже знаю, как вы это делаете. Находите противоположные числа, в уме зачеркиваете их и как бы зануляете их. По итогу остается сирота.

Ровно то же самое происходит и в нашей сегодняшней задаче.

Пока писал пост еще глубже понял прикол задачи. Она удивительно красивая! И подходит для решения разработчикам и энтузиастам разных уровней: начинающие решают за 2 цикла, продвинутые колдуют ксорами. Надеюсь, решение вам тоже понравилось)

Solve your problems. Stay cool.
  • 🔥 48
  • 👍 9
  • 💯 4
  • ⚡ 1
  • ❤ 1
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →