Подтвердить что ты не робот

Как сделать сложность пространства как O (1)

Я пытаюсь ответить на следующий вопрос: у вас есть массив целых чисел, так что каждое целое число содержит нечетное количество раз, кроме 3 из них. Найдите три числа.

до сих пор я пришел с методом грубой силы:

 public static void main(String[] args) {
    // TODO Auto-generated method stub

    int number[] = { 1, 6, 4, 1, 4, 5, 8, 8, 4, 6, 8, 8, 9, 7, 9, 5, 9 };
    FindEvenOccurance findEven = new FindEvenOccurance();
    findEven.getEvenDuplicates(number);

  }

  // Brute force
  private void getEvenDuplicates(int[] number) {

    Map<Integer, Integer> map = new HashMap<Integer, Integer>();

    for (int i : number) {

      if (map.containsKey(i)) {
        // a XOR a XOR a ---- - -- - - odd times = a
        // a XOR a ---- -- -- --- - even times = 0
        int value = map.get(i) ^ i;
        map.put(i,value);
      } else {
        map.put(i, i);
      }
    }

    for (Entry<Integer, Integer> entry : map.entrySet()) {

      if (entry.getValue() == 0) {
        System.out.println(entry.getKey());
      }

    }
  }

Он отлично работает, но не эффективен.

O/p:

1
5
6
8

Но вопросы указывают, что нам нужно сделать это в O (1) пространстве и O (N) временной сложности. Для моего решения временной сложностью является O (N), но пространство также O (N). Может ли кто-нибудь предложить мне лучший способ сделать это с помощью O (1) пространства?

Спасибо.

4b9b3361

Ответ 1

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

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

Теперь попробуйте решить проблему, когда число непарных целых чисел равно 3.

В начале мы также вычисляем XOR всех элементов. Обозначим ее через X.

Рассмотрим i-й бит в X. Я предполагаю, что он равен 0. Если он равен 1, следующая процедура практически одинакова, мы просто меняем 0 на 1 и наоборот.

Итак, если i-й бит X равен 0, мы имеем две возможные ситуации. Одна из ситуаций заключается в том, что все непарные целые числа имеют 0 в i-м бите. Другая ситуация заключается в том, что одно не парное целое число имеет 0 в i-м бите, а два непарных целых числа имеют 1 в i-м бите. Этот оператор основан на простых операциях XOR. Итак, у нас есть один или три не спаренных целых числа с 0 в i-м бите.

Теперь разделим все элементы на две группы. Первая группа предназначена для целых чисел с 0 в i-й разрядной позиции, вторая - для целых чисел с 1 в i-й позиции бита. Также наша первая группа содержит один или три не спаренных целых числа с '0' в i-м бите.

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

Главное наблюдение заключается в том, что существует такое, что одно не парное целое число имеет i-й бит, который отличается от i-го бита двух других непарных целых чисел. В этом случае непарные целые числа находятся в обеих группах. Это основано на том, что если нет такого i, то биты во всех позициях в не спаренных целых числах схожи, и они равны друг другу. Но это невозможно в соответствии с выражением о проблеме.

Это решение может быть реализовано без дополнительной памяти. Общая сложность линейна с некоторой константой в зависимости от количества бит в элементах массива.

Ответ 2

К сожалению, невозможно достичь такого решения с пространством O (1) и сложностью O (n), если мы используем строгий смысл пространства, т.е. пространство O (1) связано с максимальным пространством, используемым во входном массив.

В слабом смысле пространства, где любое произвольное большое число Целое число по-прежнему вписывается в O (1), вы можете просто закодировать свой счетчик на биты этого целого числа. Начните со всех битов, установленных в 1. Переключите n-й бит, когда вы встретите число n во входном массиве. Все биты, оставшиеся 1 в конце, представляют 3 числа, которые встречались четное число раз.

Ответ 3

Есть два способа взглянуть на вашу проблему.

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

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

NB очевидная оптимизация пространства заключалась бы в том, чтобы хранить значения только один раз, стирая предыдущее значение для четных отсчетов, вы получите половину пространства.

О других ответах @Lashane и @SGM1: они также решают проблему "вычисления", но, возможно, меньше эффективны, чем ваши, в большинстве реальных сценариев. Зачем? Поскольку они предварительно распределяют массив 512 МБ, вместо выделения пропорциональности количеству различных значений в массиве. Поскольку массив, вероятно, будет использовать намного меньше MAX_INT разных значений, вы, вероятно, будете использовать гораздо меньше, чем 512 МБ, даже если вы храните 32 бита для каждого значения вместо 1. И это с 32-битными целыми числами, с большим количеством бит, распределенный массив будет расти экспоненциально, OTOH ваше решение зависит только от фактических значений в массиве, поэтому на него не влияет количество битов системы (т.е. максимальное значение int).

См. также этот и этот для лучшего (меньше пространства) алгоритмов.

Ответ 4

рассмотрим, например, допустимые числа имеют размер 4 бита, что означает, что диапазон чисел разрешен от 0 до 2 4 -1 который является постоянным числом 16, для каждого возможного ввода мы запускаем весь массив и xor появление этого числа, если результат xor равен нулю, мы добавляем текущее значение к общему результату. это решение O (16N), которое O (N) и использует только одну дополнительную переменную для оценки xor текущего числа, которое O (1) с точки зрения сложности пространства.

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

мы можем улучшить этот подход, пропустив все элементы и найдем самый старший бит по всем входным данным, предположим, что это бит 10 th тогда наша сложность времени выполнения станет O (2 10 N), который также O (N).

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

Наконец, я считаю, что существует еще одно лучшее решение для этой проблемы, но я решил поделиться своей мыслью.

Изменить:

алгоритм в изображении может быть неясным, вот некоторое объяснение алгоритму.
это начинается с идеи попытаться разделить элементы в соответствии с битами, другими словами сделать биты как фильтры на каждом этапе xor разделенными элементами до тех пор, пока результат xor не будет равен нулю, тогда стоит проверить эту группу один один, так как он наверняка будет содержать хотя бы один из желаемых выходов. или если два консультативных фильтра приведут к тому же размеру, мы остановим этот фильтр, это будет более ясно с примера ниже.
вход: 1,6,4,1,4,5,8,8,4,6,8,8,9,7,9,5,9
мы начинаем с деления элементов по наименьшему значащему значению.
1 st бит 0: 6,4,4,8,8,4,6,8,8
6 xor 4 xor 4 xor 8 xor 8 xor 4 xor 6 xor 8 xor 8 = 4
поэтому мы продолжим деление этой группы на бит 2 nd.
1 st бит 0 и 2 nd бит 0: 4,4,4,8,8,8, 8
4 xor 4 xor 4 xor 8 xor 8 xor 8 xor 8 xor 8 = 4.
поэтому мы продолжим деление этой группы на бит 3 rd.
1 st бит нуль и 2 nd бит нуль и 3 rd бит 0: 8,8,8,8
8 xor 8 xor 8 xor 8 = 0
поэтому мы будем проходить через каждый элемент под этим фильтром, поскольку результат xor равен нулю, и мы добавим 8 к нашему результату до сих пор.
1 st бит нуль и 2 nd бит нуль и 3 rd бит один: 4,4,4
4 xor 4 xor 4 = 4
1 st бит нуль и 2 nd бит нуль и 3 rd бит один и 4 th бит: 4,4,4
4 xor 4 xor 4 = 4.
поэтому мы остановимся здесь, поскольку этот фильтр содержит тот же размер, что и предыдущий фильтр
теперь мы вернемся к фильтру 1 st и 2 nd бит
1 st бит нуль и 2 nd бит один: 6,6
6 xor 6 = 0.
поэтому мы будем проходить через каждый элемент под этим фильтром, поскольку результат xor равен нулю, и мы добавим 6 к нашему результату до сих пор.
теперь мы вернемся к фильтру бит st
1 st бит один: 9,5,9,7,9,1,1
теперь мы будем продолжать под этим фильтром, как и раньше. для полного примера см. приведенное выше изображение.

Ответ 5

Ваш контур проблемы и пример не совпадают. Вы говорите, что вы ищете 3 целых числа в своем вопросе, но пример показывает 4.

Я не уверен, что это возможно без дополнительных ограничений. Мне кажется, что худшая размерность случая всегда будет по крайней мере O (N-6) = > O (N) без отсортированного списка и с полным набором целых чисел.

Если мы начали с отсортированного массива, тогда да, просто, но это ограничение не указано. Сортировка массива будет слишком сложным или пространственным.

Ответ 6

как только нам понадобится O (N) -решение и O (1) пространство, вот моя версия:

final int numbers[] = {1, 6, 4, 1, 4, 5, 8, 8, 4, 6, 8, 8, 9, 7, 9, 5, 9, -1, -1};
final long bits[] = new long[0x3FFFFFF + 1]; // O(1) space
for (final int n : numbers) { // O(N) time
    final int aidx = n >>> 6;
    final long eidx = 1 << (n & 0x3f);
    bits[aidx] ^= eidx;
}
for (final int n : numbers) { // O(N) time
    final int aidx = n >>> 6;
    final long eidx = 1 << (n & 0x3f);
    if ((bits[aidx] & eidx) == 0) {
        System.out.println(n);
        bits[aidx] |= eidx; // set bit to prevent duplicates in output
    }
}

Представьте, что у вас есть одна переменная с 2 ^ 32 бит (~ 4KKK). Основная идея состоит в том, чтобы перевернуть соответствующий бит для каждого числа, то есть первое появление числа 25 перевернет 25-й бит от 0 до 1, второе вхождение перевернет его обратно на 0 и т.д., Поэтому для чисел с нечетной частотой у нас будет 1 и 0 для четных частот.

Ответ 7

Мой удар в ответ, используя предложение Lashane несколько иначе:

char negBits[268435456]; // 2 ^ 28 = 2 ^ 30 (number of negative integer numbers) / 8 (size of char)
char posBits[268435456]; // ditto except positive

int number[] = { 1, 6, 4, 1, 4, 5, 8, 8, 4, 6, 8, 8, 9, 7, 9, 5, 9 };

for (int num : number){
   if (num &lt 0){
       num = -(num + 1);// Integer.MIN_VALUE would be excluded without this + 1
       negBits[ &lt&lt 4] ^= ((num & 0xf) >> 1);
   }
   else {
       posBits[num &lt&lt 4] ^= ((num & 0xf) >> 1);
       // grab the rite char to mess with
       // toggle the bit to represent the integer value.
   }
}

// Now the hard part, find what values after all the toggling:

for (int i = 0; i &lt Integer.MAX_VALUE; i++){
    if (negBits[i &lt&lt 4] & ((i & 0xf) >> 1)){
        System.out.print(" " + (-i - 1));
    }
    if (posBits[i &lt&lt 4] & ((i & 0xf) >> 1)){
        System.out.print(" " + i);
    }
}

В соответствии с обсуждением в комментариях ниже следует отметить следующие моменты:

  • Предполагает Java в 32-разрядной версии.
  • В массиве Java есть собственный предел Integer.MAX_INT