O (n) для нахождения нечетного числа в массиве последовательных целых чисел от 1 до n (нечетные числа)
У меня много проблем, пытаясь понять этот вопрос, и корень этой проблемы создает алгоритм сложности O(n)
. Вот вопрос, с которым я борюсь:
Array A
длины n
содержит целые числа из диапазона [0, .., n - 1]
. Однако он содержит только n - 1
различные числа. Поэтому одно из чисел отсутствует, а другой номер дублируется. Напишите метод Java, который принимает A
как входной аргумент и возвращает недостающее число; метод должен работать в O(n)
.
Например, когда A = [0, 2, 1, 2, 4]
, oddOneOut()
должен возвращать 3
; когда A = [3, 0, 0, 4, 2, 1]
, oddOneOut()
должен возвращать 5
.
Очевидно, что это простая задача для решения с помощью алгоритма O(n2)
(и, скорее всего, O(n)
, я просто этого не вижу!). Я попытался решить эту проблему, используя всевозможные методы, но безрезультатно. Я пытаюсь решить эту проблему на Java, но если вы более комфортно решаете ее на Python, это тоже будет хорошо.
Спасибо заранее...
Ответы
Ответ 1
Предположим, что число отсутствует x
, а дубликат - y
. Если вы добавите все числа, сумма будет:
(n - 1) * n / 2 - x + y
Из вышесказанного вы можете найти (x - y)
..... (1)
Аналогично суммируем квадраты чисел. Тогда сумма будет:
(n - 1) * n * (2 * n - 1) / 6 - x2 + y2
Из вышесказанного вы получите (x2 - y2)
.... (2)
(2) / (1) gives (x + y).....(3)
(1) + (3) дает 2 * x
, и вы можете найти x
и y
.
Обратите внимание, что в этом решении есть O(1)
дополнительное хранилище и O(n)
временная сложность. Другие вышеприведенные решения не требуют дополнительного хранения O(n)
.
Код в смешанном C/С++ для большей ясности:
#include <stdio.h>
int findDup(int *arr, int n, int& dup, int& missing)
{
int sum = 0;
int squares = 0;
for (int i = 0; i < n; i++) {
sum += arr[i];
squares += arr[i] * arr[i];
}
sum = (n - 1) * n / 2 - sum; // x - y
squares = (n - 1) * n * (2 * (n - 1) + 1) / 6 - squares; // x^2 - y^2
if (sum == 0) {
// no duplicates
missing = dup = 0;
return -1;
}
missing = (squares / sum + sum) / 2; // ((x^2 - y^2) / (x - y) + (x - y)) / 2 = ((x + y) + (x - y)) / 2 = x
dup = missing - sum; // x - (x - y) = y
return 0;
}
int main(int argc, char *argv[])
{
int dup = 0;
int missing = 0;
int a[] = {0, 2, 1, 2, 4};
findDup(a, sizeof(a) / sizeof(int), dup, missing);
printf("dup = [%d], missing = [%d]\n", dup, missing);
int b[] = {3, 0, 0, 4, 2, 1};
findDup(b, sizeof(b) / sizeof(int), dup, missing);
printf("dup = [%d], missing = [%d]\n", dup, missing);
return 0;
}
Вывод:
dup = [2], missing = [3]
dup = [0], missing = [5]
Некоторые коды python:
def finddup(lst):
sum = 0
sumsq = 0
missing = 0
dup = 0
for item in lst:
sum = sum + item
sumsq = sumsq + item * item
n = len(a)
sum = (n - 1) * n / 2 - sum
sumsq = (n - 1) * n * (2 * (n - 1) + 1) / 6 - sumsq
if sum == 0:
return [-1, missing, dup]
missing = ((sumsq / sum) + sum) / 2
dup = missing - sum
return [0, missing, dup]
found, missing, dup = finddup([0, 2, 1, 2, 4])
if found != -1:
print "dup = " + str(dup) + " missing = " + str(missing)
print finddup([3, 0, 0, 4, 2, 1])
Выходы:
dup = 2 missing = 3
[-1, 0, 0]
Ответ 2
Итерации по массиву дважды: это все равно O (n). Создайте временный массив логических элементов (или Java BitSet), чтобы сохранить номера, которые вы получили. Второй раз, когда вы делаете цикл, проверьте, есть ли в массиве логических элементов отверстие.
Ответ 3
Используйте хэш-набор и сделайте один проход, чтобы определить, какой номер является дубликатом. Во время той же итерации отслеживайте совокупную сумму всех чисел.
Теперь вычислите ожидаемое значение, если все числа были разными: n * (n - 1) / 2
. Вычтите итог, который вы нашли. Вы останетесь с "отсутствующим" числом минус дубликат. Добавьте дубликат, чтобы получить ответ.
Поскольку доступ к хеш-таблице является постоянным временем, и мы используем один проход, это O(n)
. (Обратите внимание, что один проход не является строго необходимым: Martijn правильно отмечает, что фиксированное количество проходов по-прежнему является линейной сложностью.)
Ответ 4
Это может представлять интерес, хотя я не уверен, при каких условиях (если они есть) он лучше всего работает. Идея состоит в том, что мы переместим каждый элемент в правильное место в массиве (0
в индекс 0 и т.д.), Пока не станет ясно, чего не хватает и что дополнительно.
def findmissing(data):
upto = 0
gap = -1
while upto < len(data):
#print data, gap
if data[upto] == upto:
upto += 1
continue
idx = data[upto]
if idx is None:
upto += 1
continue
data[upto], data[idx] = data[idx], data[upto]
if data[upto] == data[idx]:
print 'found dupe, it is', data[upto]
data[upto] = None
gap = upto
upto += 1
elif data[upto] is None:
gap = upto
return gap
if __name__ == '__main__':
data = range(1000)
import random
missing = random.choice(data)
print missing
data[missing] = data[0]
data[0] = random.choice(data[1:])
random.shuffle(data)
print 'gap is', findmissing(data)
Это O (n), потому что каждый шаг либо увеличивает upto
, либо перемещает значение в свое "правильное" место в массиве, и каждая из этих вещей может произойти только n
раз.