Предыдущее 65-байтовое решение:
r->{for(int a,b=0,z,i=0;;b=a)if((a=b|1<<(z=r[i++]))==b)return z;}
Новое решение 19 байтов включены дляimport java.math.*;
-8 байт благодаря @Nevay
r->{int z,i=0;for(BigInteger c=BigInteger.ZERO;c.min(c=c.setBit(z=r[i++]))!=c;);return z;}
Попробуйте онлайн!
редактировать
Алгоритм в моей исходной программе был в порядке, но статический размер используемого типа данных означал, что он сломался довольно быстро, как только размер превысил определенный порог.
Я изменил тип данных, используемый в вычислениях, чтобы увеличить лимит памяти программы, чтобы приспособиться к этому (используя BigInteger
для произвольной точности вместо int
или long
). Однако, это делает спорным, считается ли это O(1)
сложностью пространства.
Я оставлю свое объяснение ниже без изменений, но я хочу добавить, что теперь я считаю, что невозможно достичь O(1)
космической сложности без некоторых предположений.
доказательство
Определите N
как целое число такое, что 2 <= N
.
Позвольте S
быть список, представляющий ряд случайных целых чисел [x{1}, ..., x{N}]
, где x{i}
имеет ограничение 1 <= x{i} <= N
.
Сложность по времени (в обозначениях Big-O), необходимая для итерации по этому списку ровно один раз для каждого элемента, равна O(n)
Задача состоит в том, чтобы найти первое дублированное значение в списке. Более конкретно, мы ищем первое значение, S
которое является дубликатом предыдущего элемента в списке.
Позвольте p
и q
быть позиции двух элементов в списке, что p < q
и x{p} == x{q}
. Наша задача - найти самое маленькое q
, удовлетворяющее этим условиям.
Очевидный подход к этой проблеме - перебрать S и проверить, x{i}
существует ли наш объект в другом списке T
: если x{i}
его нет в T
, мы сохраняем его в T
. Если x{i}
существует в T
, это первое дублирующее значение и, следовательно, наименьшее q
, и как таковое мы возвращаем его. Эта космическая эффективность есть O(n)
.
Чтобы достичь O(1)
пространственной сложности при сохранении O(n)
временной сложности, мы должны хранить уникальную информацию о каждом объекте в списке в ограниченном количестве пространства. Из-за этого единственный способ, которым любой алгоритм мог работать вO(1)
Сложность пространства заключается в том, что: 1. N задана верхняя граница, соответствующая памяти, необходимой для хранения максимального количества возможных значений для конкретного конечного типа данных. 2. Переназначение одной неизменяемой переменной не учитывается в зависимости от сложности, а только от количества переменных (список состоит из нескольких переменных). 3. (Основано на других ответах) Список (или, по крайней мере, элементы списка) изменчив, а тип данных списка предварительно задан как целое число со знаком, что позволяет вносить изменения в элементы, находящиеся далее в списке. без использования дополнительной памяти.
1 и 3 требуют предположений и спецификаций о типе данных, в то время как 2 требует, чтобы при расчете сложности пространства учитывалось только количество переменных, а не их размер. Если ни одно из этих допущений не будет принято, было бы невозможно достичь как O(n)
временной, так и O(1)
пространственной сложности.
объяснение
Черт возьми, этот занял смущающе много времени, чтобы придумать немного умственных способностей.
Итак, идти за бонусом сложно. Нам нужно одновременно работать со всем списком ровно один раз и отслеживать, какие значения мы уже перебрали, без дополнительной сложности с пространством.
Битовая манипуляция решает эти проблемы. Мы инициализируем наше O(1)
«хранилище», пару целых чисел, затем выполняем итерацию по списку, ИЛИ вставляем i-й бит в наше первое целое число и сохраняем этот результат во втором.
Например, если у нас есть 1101
, и мы выполняем операцию ИЛИ 10
, мы получаем 1111
. Если мы сделаем еще одно ИЛИ с нами 10
, у нас все еще будет 1101
.
Поэтому, как только мы выполним операцию ИЛИ и получим тот же номер, мы нашли наш дубликат. Отсутствие дубликатов в массиве приводит к тому, что программа запускается и выдает исключение.