Find the Smallest Integer Not in a List

Вы также можете попробовать использовать SpannedString , но вам нужно будет проанализировать его и изменить расстояние между символами для каждого из слов

84
задан eeerahul 5 October 2011 в 16:00
поделиться

17 ответов

Если структура данных может быть изменена на месте и поддерживает произвольный доступ, вы можете сделать это за 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
117
ответ дан 24 November 2019 в 08:26
поделиться

Я не уверен, понял ли я вопрос. Но если для списка 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) последовательных целых чисел за вычетом пропущенного числа.

Спасибо, что указали на тот факт, что я думал о некоторых средних числах.

0
ответ дан 24 November 2019 в 08:26
поделиться

Отсортируйте список, посмотрите на первый и второй элементы и начните движение вверх, пока не появится пробел.

2
ответ дан 24 November 2019 в 08:26
поделиться

Я бы просто отсортировал их, а затем пробежал по последовательности, пока не нашел пробел (включая пробел в начале между нулем и первым числом).

С точки зрения алгоритма, что-то вроде этого могло бы сделать это:

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")
3
ответ дан 24 November 2019 в 08:26
поделиться

Поскольку все числа имеют длину 64 бита, мы можем использовать radix sort ] на них, что составляет O (n). Сортируйте их, затем просматривайте em, пока не найдете то, что ищете.

если наименьшее число равно нулю, просматривайте вперед, пока не найдете пробел. Если наименьшее число не равно нулю, ответ равен нулю.

8
ответ дан 24 November 2019 в 08:26
поделиться

Как указано в других ответах, вы можете выполнить сортировку, а затем просто сканировать, пока не найдете пробел.

Вы можете улучшить алгоритмическую сложность до O (N) и сохранить пространство O (N), используя модифицированную QuickSort, при которой вы удаляете разделы, которые не являются потенциальными кандидатами на содержание пробела.

  • На первом этапе разделения удалите дубликаты.
  • После завершения разделения посмотрите на количество элементов в нижнем разделе.
  • Равно ли это значение значению, используемому для создания раздела?
    • Если так, то это означает, что разрыв находится в верхнем разделе.
      • Продолжите быструю сортировку, игнорируя нижний раздел.
    • В противном случае разрыв находится в нижнем разделе
      • Продолжите быструю сортировку, игнорируя более высокий раздел

Это экономит большое количество вычислений.

10
ответ дан 24 November 2019 в 08:26
поделиться

Как разумно заметил Стивен С., ответ должен быть числом меньше, чем длина массива. Тогда я бы нашел ответ с помощью бинарного поиска. Это оптимизирует наихудший случай (чтобы интервьюер не мог поймать вас в патологическом сценарии «что, если»). В интервью

0
ответ дан 24 November 2019 в 08:26
поделиться

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.)

1
ответ дан 24 November 2019 в 08:26
поделиться

Для экономичного метода, когда все значения различны, вы можете сделать это в пространстве O (k) и времени O (k * log (N) * N) . Это занимает мало места, нет перемещения данных, и все операции элементарны (сложение вычитание).

  1. set U = N; L = 0
  2. Сначала разделите числовое пространство на k областей. Нравится:
    • 0->(1/k)*(U-L) + L, 0->(2/k)*(U-L) + L, 0->(3/k)*(U-L) + L ... 0->(U-L) + L
  3. Find how many numbers (count{i}) are in each region. (N*k steps)
  4. Find the first region (h) that isn't full. That means count{h} < upper_limit{h}. (k steps)
  5. if h - count{h-1} = 1 you've got your answer
  6. set U = count{h}; L = count{h-1}
  7. goto 2

this can be improved using hashing (thanks for Nic this idea).

  1. same
  2. First partition the number space in k regions. Like this:
    • L + (i / k) -> L + (i + 1 / k) * (UL)
  3. inc count {j} используя j = (number - L) / k (если L <число
  4. найти первую область ( h ), в которой нет k элементов
  5. , если count {h} = 1 h - ваш ответ
  6. установите U = максимальное значение в области h L = минимальное значение в области h

Это будет выполняться в O (log (N) * N) .

5
ответ дан 24 November 2019 в 08:26
поделиться

Вы можете сделать это за 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), хотя оно было бы намного быстрее.

1
ответ дан 24 November 2019 в 08:26
поделиться

Мне нравится метод "угадай ноль". Если бы числа были случайными, очень вероятно, что ноль. Если «экзаменатор» установил неслучайный список, затем добавьте его и снова угадайте:

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
ответ дан 24 November 2019 в 08:26
поделиться

Мы могли бы использовать хеш-таблицу для хранения чисел. Когда все числа будут выполнены, запустите счетчик от 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) .

1
ответ дан 24 November 2019 в 08:26
поделиться

Что ж, если в списке чисел пропущено только одно число, самый простой способ найти недостающее число - это сложить ряды и вычесть каждое значение в списке. Конечное значение - это пропущенное число.

1
ответ дан 24 November 2019 в 08:26
поделиться

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.

13
ответ дан 24 November 2019 в 08:26
поделиться

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"
8
ответ дан 24 November 2019 в 08:26
поделиться

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.

  1. Find the length of the list; lets say it is N.
  2. Allocate an array of N booleans, initialized to all false.
  3. For each number X in the list, if X is less than N, set the X'th element of the array to true.
  4. Scan the array starting from index 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:

  • A counting sort requires an array of (at least) 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.
  • To determine the array size, a counting sort needs to make an initial pass through the list to determine Xmax and Xmin.
  • The minimum worst-case space requirement is therefore 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.

89
ответ дан 24 November 2019 в 08:26
поделиться

Молодцы, Муравьи Аасма! Я размышлял над ответом около 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, зная, что все значения до этого индекса включительно равны их индексу.

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

0
ответ дан 24 November 2019 в 08:26
поделиться