Вы также можете попробовать использовать SpannedString , но вам нужно будет проанализировать его и изменить расстояние между символами для каждого из слов
Если структура данных может быть изменена на месте и поддерживает произвольный доступ, вы можете сделать это за O (N) времени и O (1) дополнительного пространства. Просто пройдите по массиву последовательно и для каждого индекса запишите значение индекса в индекс, указанный в value, рекурсивно помещая любое значение в этом месте на его место и отбрасывая значения> N. Затем снова пройдите по массиву в поисках места где значение не соответствует индексу - это наименьшее значение, отсутствующее в массиве. В результате получается не более 3N сравнений и используется только несколько значений временного пространства.
# Pass 1, move every value to the position of its value
for cursor in range(N):
target = array[cursor]
while target < N and target != array[target]:
new_target = array[target]
array[target] = target
target = new_target
# Pass 2, find first location where the index doesn't match the value
for cursor in range(N):
if array[cursor] != cursor:
return cursor
return N
Я не уверен, понял ли я вопрос. Но если для списка 1,2,3,5,6 и отсутствующее число равно 4, то недостающее число можно найти в O (n) следующим образом: (n + 2) (n + 1) / 2- (n + 1) n / 2
РЕДАКТИРОВАТЬ: извините, я думаю, что вчера вечером я слишком быстро думал. В любом случае, вторую часть следует заменить на sum (list), откуда приходит O (n). Формула раскрывает идею, стоящую за ней: для n последовательных целых чисел сумма должна быть (n + 1) * n / 2. Если число пропущено, сумма будет равна сумме (n + 1) последовательных целых чисел за вычетом пропущенного числа.
Спасибо, что указали на тот факт, что я думал о некоторых средних числах.
Отсортируйте список, посмотрите на первый и второй элементы и начните движение вверх, пока не появится пробел.
Я бы просто отсортировал их, а затем пробежал по последовательности, пока не нашел пробел (включая пробел в начале между нулем и первым числом).
С точки зрения алгоритма, что-то вроде этого могло бы сделать это:
def smallest_not_in_list(list):
sort(list)
if list[0] != 0:
return 0
for i = 1 to list.last:
if list[i] != list[i-1] + 1:
return list[i-1] + 1
if list[list.last] == 2^64 - 1:
assert ("No gaps")
return list[list.last] + 1
Конечно, если у вас намного больше памяти, чем ворчание ЦП, вы можете создать битовую маску всех возможных 64-битных значений и просто установить биты для каждого числа в списке. Затем найдите первый 0-бит в этой битовой маске. Это превращает его в операцию O (n) с точки зрения времени, но чертовски дорогую с точки зрения требований к памяти: -)
Я сомневаюсь, что вы могли бы улучшить O (n), поскольку я не вижу способа сделать это это не требует просмотра каждого числа хотя бы один раз.
Алгоритм для этого был бы следующим:
def smallest_not_in_list(list):
bitmask = mask_make(2^64) // might take a while :-)
mask_clear_all (bitmask)
for i = 1 to list.last:
mask_set (bitmask, list[i])
for i = 0 to 2^64 - 1:
if mask_is_clear (bitmask, i):
return i
assert ("No gaps")
Поскольку все числа имеют длину 64 бита, мы можем использовать radix sort ] на них, что составляет O (n). Сортируйте их, затем просматривайте em, пока не найдете то, что ищете.
если наименьшее число равно нулю, просматривайте вперед, пока не найдете пробел. Если наименьшее число не равно нулю, ответ равен нулю.
Как указано в других ответах, вы можете выполнить сортировку, а затем просто сканировать, пока не найдете пробел.
Вы можете улучшить алгоритмическую сложность до O (N) и сохранить пространство O (N), используя модифицированную QuickSort, при которой вы удаляете разделы, которые не являются потенциальными кандидатами на содержание пробела.
Это экономит большое количество вычислений.
Как разумно заметил Стивен С., ответ должен быть числом меньше, чем длина массива. Тогда я бы нашел ответ с помощью бинарного поиска. Это оптимизирует наихудший случай (чтобы интервьюер не мог поймать вас в патологическом сценарии «что, если»). В интервью
Thanks to egon, swilden, and Stephen C for my inspiration. First, we know the bounds of the goal value because it cannot be greater than the size of the list. Also, a 1GB list could contain at most 134217728 (128 * 2^20) 64-bit integers.
Hashing part
I propose using hashing to dramatically reduce our search space. First, square root the size of the list. For a 1GB list, that's N=11,586. Set up an integer array of size N. Iterate through the list, and take the square root* of each number you find as your hash. In your hash table, increment the counter for that hash. Next, iterate through your hash table. The first bucket you find that is not equal to it's max size defines your new search space.
Bitmap part
Now set up a regular bit map equal to the size of your new search space, and again iterate through the source list, filling out the bitmap as you find each number in your search space. When you're done, the first unset bit in your bitmap will give you your answer.
This will be completed in O(n) time and O(sqrt(n)) space.
(*You could use use something like bit shifting to do this a lot more efficiently, and just vary the number and size of buckets accordingly.)
Для экономичного метода, когда все значения различны, вы можете сделать это в пространстве O (k)
и времени O (k * log (N) * N)
. Это занимает мало места, нет перемещения данных, и все операции элементарны (сложение вычитание).
U = N; L = 0
k
областей. Нравится:
0->(1/k)*(U-L) + L
, 0->(2/k)*(U-L) + L
, 0->(3/k)*(U-L) + L
... 0->(U-L) + L
count{i}
) are in each region. (N*k
steps)h
) that isn't full. That means count{h} < upper_limit{h}
. (k
steps)h - count{h-1} = 1
you've got your answerU = count{h}; L = count{h-1}
this can be improved using hashing (thanks for Nic this idea).
k
regions. Like this:
L + (i / k) -> L + (i + 1 / k) * (UL)
inc count {j}
используя j = (number - L) / k
(если L <число
h
), в которой нет k элементов count {h} = 1
h - ваш ответ U = максимальное значение в области h
L = минимальное значение в области h
Это будет выполняться в O (log (N) * N)
.
Вы можете сделать это за O (n) раз и O (1) дополнительного места, хотя скрытый фактор довольно велик. Это непрактичный способ решения проблемы, но, тем не менее, он может быть интересен.
Для каждого 64-битного целого числа без знака (в порядке возрастания) перебирайте список, пока не найдете целевое целое число или не дойдете до конца список. Если вы дойдете до конца списка, целевое целое число будет наименьшим целым числом, отсутствующим в списке. Если вы дойдете до конца 64-битных целых чисел, каждое 64-битное целое будет в списке.
Вот это как функция Python:
def smallest_missing_uint64(source_list):
the_answer = None
target = 0L
while target < 2L**64:
target_found = False
for item in source_list:
if item == target:
target_found = True
if not target_found and the_answer is None:
the_answer = target
target += 1L
return the_answer
Эта функция намеренно неэффективна, чтобы сохранить ее O (n). Обратите особое внимание на то, что функция продолжает проверять целевые числа даже после того, как был найден ответ. Если функция вернулась, как только был найден ответ, количество запусков внешнего цикла будет ограничено размером ответа, который ограничен значением n. Это изменение сделало бы время выполнения O (n ^ 2), хотя оно было бы намного быстрее.
Мне нравится метод "угадай ноль". Если бы числа были случайными, очень вероятно, что ноль. Если «экзаменатор» установил неслучайный список, затем добавьте его и снова угадайте:
LowNum=0
i=0
do forever {
if i == N then leave /* Processed entire array */
if array[i] == LowNum {
LowNum++
i=0
}
else {
i++
}
}
display LowNum
Наихудший случай - n * N с n = N, но на практике n, скорее всего, будет небольшим числом (например, 1 )
Мы могли бы использовать хеш-таблицу для хранения чисел. Когда все числа будут выполнены, запустите счетчик от 0 до наименьшего. Достаточно хороший хэш будет хэшироваться и храниться в постоянное время и извлекаться в постоянное время.
for every i in X // One scan Θ(1)
hashtable.put(i, i); // O(1)
low = 0;
while (hashtable.get(i) <> null) // at most n+1 times
low++;
print low;
Наихудший случай, если в массиве n
элементов, а это {0, 1, ... n-1}
, и в этом случае ответ будет получено в n
, все еще сохраняя его O (n)
.
Что ж, если в списке чисел пропущено только одно число, самый простой способ найти недостающее число - это сложить ряды и вычесть каждое значение в списке. Конечное значение - это пропущенное число.
Since the OP has now specified that the original list is held in RAM and that the computer has only, say, 1GB of memory, I'm going to go out on a limb and predict that the answer is zero.
1GB of RAM means the list can have at most 134,217,728 numbers in it. But there are 264 = 18,446,744,073,709,551,616 possible numbers. So the probability that zero is in the list is 1 in 137,438,953,472.
In contrast, my odds of being struck by lightning this year are 1 in 700,000. And my odds of getting hit by a meteorite are about 1 in 10 trillion. So I'm about ten times more likely to be written up in a scientific journal due to my untimely death by a celestial object than the answer not being zero.
To illustrate one of the pitfalls of O(N)
thinking, here is an O(N)
algorithm that uses O(1)
space.
for i in [0..2^64):
if i not in list: return i
print "no 64-bit integers are missing"
Here's a simple O(N)
solution that uses O(N)
space. I'm assuming that we are restricting the input list to non-negative numbers and that we want to find the first non-negative number that is not in the list.
N
.N
booleans, initialized to all false
. X
in the list, if X
is less than N
, set the X'th
element of the array to true
.0
, looking for the first element that is false
. If you find the first false
at index I
, then I
is the answer. Otherwise (i.e. when all elements are true
) the answer is N
.In practice, the "array of N
booleans" would probably be encoded as a "bitmap" or "bitset" represented as a byte
or int
array. This typically uses less space (depending on the programming language) and allows the scan for the first false
to be done more quickly.
This is how / why the algorithm works.
Suppose that the N
numbers in the list are not distinct, or that one or more of them is greater than N
. This means that there must be at least one number in the range 0 .. N - 1
that is not in the list. So the problem of find the smallest missing number must therefore reduce to the problem of finding the smallest missing number less than N
. This means that we don't need to keep track of numbers that are greater or equal to N
... because they won't be the answer.
The alternative to the previous paragraph is that the list is a permutation of the numbers from 0 .. N - 1
. In this case, step 3 sets all elements of the array to true
, and step 4 tells us that the first "missing" number is N
.
The computational complexity of the algorithm is O(N)
with a relatively small constant of proportionality. It makes two linear passes through the list, or just one pass if the list length is known to start with. There is no need to represent the hold the entire list in memory, so the algorithm's asymptotic memory usage is just what is needed to represent the array of booleans; i.e. O(N)
bits.
(By contrast, algorithms that rely on in-memory sorting or partitioning assume that you can represent the entire list in memory. In the form the question was asked, this would require O(N)
64-bit words.)
@Jorn comments that steps 1 through 3 are a variation on counting sort. In a sense he is right, but the differences are significant:
Xmax - Xmin
counters where Xmax
is the largest number in the list and Xmin
is the smallest number in the list. Each counter has to be able to represent N states; i.e. assuming a binary representation it has to have an integer type (at least) ceiling(log2(N))
bits.Xmax
and Xmin
.ceiling(log2(N)) * (Xmax - Xmin)
bits.By contrast, the algorithm presented above simply requires N
bits in the worst and best cases.
However, this analysis leads to the intuition that if the algorithm made an initial pass through the list looking for a zero (and counting the list elements if required), it would give a quicker answer using no space at all if it found the zero. It is definitely worth doing this if there is a high probability of finding at least one zero in the list. And this extra pass doesn't change the overall complexity.
EDIT: I've changed the description of the algorithm to use "array of booleans" since people apparently found my original description using bits and bitmaps to be confusing.
Молодцы, Муравьи Аасма! Я размышлял над ответом около 15 минут и независимо придумал ответ в том же духе, что и ваш:
#define SWAP(x,y) { numerictype_t tmp = x; x = y; y = tmp; }
int minNonNegativeNotInArr (numerictype_t * a, size_t n) {
int m = n;
for (int i = 0; i < m;) {
if (a[i] >= m || a[i] < i || a[i] == a[a[i]]) {
m--;
SWAP (a[i], a[m]);
continue;
}
if (a[i] > i) {
SWAP (a[i], a[a[i]]);
continue;
}
i++;
}
return m;
}
m представляет собой «текущий максимально возможный результат с учетом того, что я знаю о первых i входах, и ничего другого не предполагая. значения до входа в m-1 ».
Это значение m будет возвращено, только если (a [i], ..., a [m-1]) является перестановкой значений (i, ..., m-1). Таким образом, если a [i]> = m или если a [i]
Если это не так, но a [i]> i, тогда зная, что a [i]! = A [a [i]] мы знаем, что замена [i] на [a [i]] увеличит количество элементов на их собственном месте.
В противном случае a [i] должен быть равен i, и в этом случае мы можем увеличить i, зная, что все значения до этого индекса включительно равны их индексу.
Доказательство того, что это не может войти в бесконечный цикл. оставлен в качестве упражнения для читателя. :)