Поиск отсутствующих элементов в массиве

Учитывая, что у вас есть массив A [1..n] размера n, он содержит элементы из набора {1..n}. Однако два элемента отсутствуют, (и, возможно, два из элементов массива повторяются). Найдите недостающие элементы.

Например, если n = 5, A может быть A [5] = {1,2,1,3,2}; и поэтому отсутствующие элементы {4,5}

Подход, который я использовал, был:

int flag[n] = {0};  
int i;  
for(i = 0; i < n; i++)  {  
  flag[A[i]-1] = 1;  
 }  

for(i = 0; i < n; i++)  {  
 if(!flag[i]) {  
    printf("missing: %d", (i+1));  
}  

сложность пространства приходит к O (n). Я чувствую, что это очень детский и неэффективный код. Не могли бы вы предоставить лучший алгоритм с более сложной пространственной и временной сложностью.

Ответ 1

Теоретически

В O (1) пространстве (в модели ОЗУ, то есть O (1)) возможно и O (n) время даже с массивом только для чтения.

Предупреждение: длинный пост с некоторой математикой. Если вас интересует только код, а не алгоритм/доказательство, перейдите к разделу кода. Однако вам нужно прочитать некоторые части алгоритма, чтобы понять код.


Алгоритм

Предположим, что недостающие числа - это x и y.

Для массива существует две возможности:

1) Одно число повторяется три раза, а остальные числа в массиве появляются ровно один раз.

Для этого случая будет выполняться трюк XOR с квадратом.

Сделайте XOR всех элементов массива с 1,2,..., n.

В итоге вы получите z = x XOR y.

Существует по крайней мере один бит z, который отличен от нуля.

Теперь, дифференцируя элементы массива на основе этого бита (два ведра), снова пропустите XOR через массив.

В итоге вы получите x и y.

Как только у вас есть x и y, вы можете подтвердить, действительно ли это недостающие элементы.

Если так получится, что шаг подтверждения не удался, тогда мы должны иметь второй случай:

2) Два элемента повторяются ровно дважды, а остальные появляются ровно один раз.

Пусть два повторяющихся элемента - a и b (x и y - недостающие).

Предупреждение: математика впереди.

Пусть S_k = 1^k + 2^k + .. + n^k

Например S_1 = n(n+1)/2, S_2 = n(n+1)(2n+1)/6 и т.д.

Теперь мы вычислим семь вещей:

T_1 = Sum of the elements of the array = S_1 + a + b - x - y.
T_2 = Sum of the squares = S_2 + a^2 + b^2 - x^2 - y^2
T_3 = Sum of cubes = S_3 + a^3 + b^3 - x^3 - y^3
T_4 = Sum of fourth powers = S_4 + a^4 + b^4 - x^4 - y^4
...
T_7 = Sum of seventh powers = S_7 + a^7 + b^7 - x^7 - y^7

Примечание. Мы можем использовать слова O (1) (intsead of one) для решения проблем с переполнением. (Я считаю, 8-10 слов будет достаточно).

Пусть Ci = T_i - S_i

Предположим теперь, что a, b, x, y являются корнями полинома 4-й степени P(z) = z^4 + pz^3 + qz^2 + rz + s

Теперь мы попытаемся преобразовать указанные семь уравнений в четыре линейных уравнения в p,q,r,s.

Например, если мы делаем 4th Eqn + p * 3rd Eqn + q* 2nd equation + r* 1st equation

получаем

C4 + p*C3 + q*C2 + r*C1 = 0

Аналогично получаем

C5 + p*C4 + q*C3 + r*C2 + s*C1 = 0
C6 + p*C5 + q*C4 + r*C3 + s*C2 = 0
C7 + p*C6 + q*C5 + r*C4 + s*C3 = 0

Это четыре линейных уравнения в p,q,r,s, которые могут быть решены методами линейной алгебры, такими как гауссово исключение.

Обратите внимание, что p,q,r,s будет рациональным и поэтому может быть вычислен только с целочисленной арифметикой.

Теперь предположим, что мы получили решение p,q,r,s к приведенной выше системе уравнений.

Рассмотрим P(z) = z^4 + pz^3 + qz^2 + rz + s.

То, что говорят вышеприведенные уравнения, в основном

P(a) + P(b) - P(x) - P(y) = 0
aP(a) + bP(b) - xP(x) -yP(y) = 0
a^2 P(a) + b^2 P(b) - x^2 P(x) - y^2 P(y)  = 0
a^3 P(a) + b^3 P(b) - x^3 P(x) - y^3 P(y) = 0

Теперь матрица

   1   1  -1 -1
   a   b   -x   -y
   a^2 b^2 -x^2 -y^2
   a^3 b^3 -x^3 -y^3

имеет тот же детерминант, что и матрица Вандермонда и поэтому обратим, если a,b,x,y различны.

Таким образом, мы должны иметь P(a) = P(b) = P(x) = P(y) = 0.

Теперь проверьте, какие из 1,2,3,...,n являются корнями x^4 + px^3 + qx^2 + rx + s = 0.

Таким образом, это линейный алгоритм пространственного пространства времени.


код

Я написал следующий код С# (.Net 4.0) и, похоже, работает для нескольких образцов, которые я пробовал... (Примечание: я не беспокоился о том, чтобы обслуживать случай 1 выше).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Numerics;

namespace SOManaged
{
    class Program
    {
        static void Main(string[] args)
        {
            ulong[] inp = {1,3,2,1,2};
            ulong[] inp1 = { 1,2,3,4,5,6,7,8,
                             9,10,11,13,14,15,
                             16,17,18,19,20,21,5,14};

            int N = 100000;
            ulong[] inp2 = new ulong[N];
            for (ulong i = 0; i < (ulong)N; i++)
            {
                inp2[i] = i+1;
            }
            inp2[122] = 44;
            inp2[419] = 13;

            FindMissingAndRepeated(inp);
            FindMissingAndRepeated(inp1);
            FindMissingAndRepeated(inp2);
        }

        static void FindMissingAndRepeated(ulong [] nums)
        {
            BigInteger[] C = new BigInteger[8];

            // Compute the C_i
            for (int k = 0; k < 8; k++)
            {
                C[k] = 0;
            }

            BigInteger i = 1;
            BigInteger n = 0;

            for (int j = 0; j < nums.Length; j++)
            {
                n = nums[j];
                i = j + 1;
                for (int k = 1; k < 8; k++)
                {
                    C[k] += i - n;
                    n = n * nums[j];
                    i = i * (j + 1);
                }
            }


            for (int k = 1; k <= 7; k++)
            {
                Console.Write("C[" + k.ToString() + "] = " + 
                               C[k].ToString() +", ");
            }
            Console.WriteLine();

            // Solve for p,q,r,s
            BigInteger[] pqrs = new BigInteger[4];
            BigInteger[] constants = new BigInteger[4];
            BigInteger[,] matrix = new BigInteger[4, 4];

            int start = 4;
            for (int row = 0; row < 4; row++ )
            {
                constants[row] = -C[start];

                int k = start-1;
                for (int col = 0; col < 4; col++)
                {
                    matrix[row, col] = C[k];
                    k--;
                }

                start++;
            }

            Solve(pqrs, matrix, constants, 4);

            for (int k = 0; k < 4; k++)
            {
                Console.Write("pqrs[" + k.ToString() + "] = " 
                               + pqrs[k].ToString() + ", ");
            }
            Console.WriteLine();

            // Find the roots.
            for (int k = 1; k <= nums.Length; k++)
            {
                BigInteger x = new BigInteger(k);
                BigInteger p_k = x * x * x* x + pqrs[0] * x* x * x 
                                 + pqrs[1] * x * x + pqrs[2] * x 
                                 + pqrs[3];

                if (p_k == 0)
                {
                    Console.WriteLine("Found: " + k.ToString());
                }
            }
        }

        // Solve using Cramer method.
        // matrix * pqrs = constants.
        static void Solve(BigInteger[] pqrs, BigInteger[,] matrix, 
                          BigInteger[] constants, int n)
        {
            BigInteger determinant = Determinant(matrix, n);

            for (int i = 0; i < n; i++)
            {
                BigInteger[,] numerator = Replace(matrix, constants, n, i);
                BigInteger numDet = Determinant(numerator,4);
                pqrs[i] = numDet/ determinant;
            }
        }

        // Replace a column of matrix with constants.
        static BigInteger[,] Replace(BigInteger[,] matrix, 
                           BigInteger[] constants, int n, int col)
        {
            BigInteger[,] newMatrix = new BigInteger[n, n];
            for (int i = 0; i < n; i++)
            {
                for (int j = 0; j < n; j++)
                {
                    if (j != col)
                    {
                        newMatrix[i, j] = matrix[i, j];
                    }
                    else
                    {
                        newMatrix[i, j] = constants[i];
                    }
                }
            }

            return newMatrix;
        }

        // Recursively compute determinant for matrix.
        static BigInteger Determinant(BigInteger[,] matrix, int n)
        {
            BigInteger determinant = new BigInteger(0);
            int multiplier = 1;

            if (n == 1)
            {
                return matrix[0,0];
            }

            for (int i = 0; i < n; i++)
            {
                BigInteger [,] subMatrix = new BigInteger[n-1,n-1];
                int row = 0;
                for (int j=1; j < n; j++)
                {
                    int col = 0;
                    for (int k = 0; k < n ; k++)
                    {
                        if (k == i)
                        {
                            continue;
                        }
                        subMatrix[row,col] = matrix[j,k];
                        col++;
                    }
                    row++;
                }

                BigInteger subDeterminant = Determinant(subMatrix, n - 1);
                determinant += multiplier * subDeterminant * matrix[0,i];
                multiplier = -multiplier;
            }

            return determinant;
        }
    }
}

Выходной сигнал

C[1] = 6, C[2] = 36, C[3] = 180, C[4] = 864, C[5] = 4116, C[6] = 19656, C[7] = 9
4380,
pqrs[0] = -12, pqrs[1] = 49, pqrs[2] = -78, pqrs[3] = 40,
Found: 1
Found: 2
Found: 4
Found: 5


C[1] = 15, C[2] = 407, C[3] = 9507, C[4] = 215951, C[5] = 4861515, C[6] = 108820
727, C[7] = 2424698067,
pqrs[0] = -53, pqrs[1] = 980, pqrs[2] = -7396, pqrs[3] = 18480,
Found: 5
Found: 12
Found: 14
Found: 22


C[1] = 486, C[2] = 189424, C[3] = 75861486, C[4] = 31342069984, C[5] = 130971109
69326, C[6] = 5492487308851024, C[7] = 2305818940736419566,
pqrs[0] = -600, pqrs[1] = 83183, pqrs[2] = -3255216, pqrs[3] = 29549520,
Found: 13
Found: 44
Found: 123
Found: 420

Ответ 2

Как отметил @j_random_hacker, это очень похоже на Поиск дубликатов в O (n) времени и O (1) пространстве и адаптацию моего ответа там Здесь тоже работает. В псевдокоде:

for i := 1 to n
    while A[A[i]] != A[i] 
        swap(A[i], A[A[i]])
    end if
end for

for i := 1 to n
    if A[i] != i then 
        print i
    end if
end for

Первый цикл переставляет массив так, что если элемент x присутствует хотя бы один раз, то одна из этих записей будет находиться в позиции A[x].

Обратите внимание, что хотя он имеет вложенный цикл, он все еще работает в O(N) времени - обмен происходит только тогда, когда существует i, такое что A[i] != i, и каждая подкачка устанавливает хотя бы один элемент таким образом, что A[i] == i, где раньше это не было правдой. Это означает, что общее количество свопов (и, следовательно, общее количество выполнений тела цикла while) не превышает N-1.

Ответ 3

Ваше решение неплохое. Здесь альтернатива - Сортировка списка и повторение его проверки соседних номеров. Когда есть пробел, напечатайте все числа между ними. Если k - длина массива, а n - число для подсчета, мы получаем O (k lg k + n) время, O (1) пространство

Ответ 4

Это бит qwirky Поскольку все ваши цифры положительны (по проблеме). Я сделаю число в позиции i-1 неопределенным, если я присутствует в массиве.

int i;  
for(i = 0; i < n; i++)  {  
    A[abs(A[i])-1] = -1*abs(A[abs(A[i])-1]);
 }  

for(i = 0; i < n; i++)  {  
 if(A[i]>0) {  
    printf("missing: %d", i+1);  
}  

Сложность O (n), без пользователя вспомогательного массива, но уничтожает входной массив.

Ответ 5

Ниже приведен один из подходов к идентификации всех недостающих чисел, когда известно, что массив содержит только диапазоны от 1 до n включительно, без использования какого-либо дополнительного пространства. Сложность времени - O (n).

Давайте возьмем наименьшее число k таким образом, чтобы оно не было в массиве до k = n + 1 (назовем его добавочным множителем).

первый цикл через каждый массив, и для каждого a [i] мы обновим [a [i] - 1] + = k; после этого цикла каждый элемент массива содержит два набора информации, число, которое первоначально было в элементе массива + k * (количество событий i-го числа в массиве).

во втором цикле вы могли бы узнать, сколько повторений i-го числа, совершив целочисленное деление числа в каждом месте на k. И исходное число в i-м месте будет [i]% k;

Давайте рассмотрим пример

A[5] = {1,2,1,3,2};

здесь (addfactor) k = 5 (длина массива) + 1 = 6

После того, как массив циклов fisrt будет выглядеть, если исходный элемент m, а встречания i-го числа O(i), результирующий элемент массива будет m + k * O(i) этот элемент divide (integer) на k, вы получите события i-го уровня, и% k вы получите исходный массив.

A = {1 + 6*2, 2 + 6*2, 1 + 6*1, 3+6*0 , 2+6*0 }
A = {13, 14, 7, 3, 2 }

Ниже приведен код С#, который (я сожалею, мой C немного ржавый, это было некоторое время.) можно портировать на любой язык, просто заменив Printf и scanfs.

    static void Main(string[] args)
    {
        int[] A = { 1, 2, 1, 3, 2 };
        PrintDuplicateAndMissing(A);
        Console.ReadLine();
    }

    static void PrintDuplicateAndMissing(int[] array)
    {
        int addfactor = array.Length + 1;
        for (int i = 0; i < array.Length; i++)
        {
            array[array[i] - 1] += addfactor; // -1 only if array contains from 1 to n. if it is 0 to n (this -1 is not required)
        }
        for (int i = 0; i < array.Length; i++)
        {
            if ( (array[i] / addfactor) == 0 )
                Console.WriteLine(string.Format("{0} is missing", i + 1)); // i + 1 only if array is 1 to n, if 0 to n then +1 is not required
            array[i] %= addfactor; //restore original content of the array
        }
    }

Ответ 6

Зациклируйте каждый элемент 0... n-1.

x = abs(A[i]) (with i = 0...n-1);

A[x - 1] can be: 
> 0: we haven't checked the element, change its sign to negative:
    A[x - 1] = -A[x - 1]
< 0: we already found the same number

В конце цикла передайте каждый A [0... n-1]. Индекс положительных элементов + 1 - это недостающие числа.

Итак, если

y = abs(A[i]) > 0: i + 1 is missing.

В С#

var arr = new[] { 1, 2, 1, 2, 4 };

for (int i = 0; i < arr.Length; i++) {
    int x = Math.Abs(arr[i]);
    int y = arr[x - 1];
    if (y > 0) {
        arr[x - 1] = -arr[x - 1];
    }
}

for (int i = 0; i < arr.Length; i++) {
    int x = arr[i];
    if (x > 0) {
        Console.WriteLine("Missing {0}", i + 1);
    } else {
        arr[i] = -arr[i];
    }
}

И массив не хуже нового.

Ответ 7

Как мы знаем, мы ищем элементы между 1 и N. Создаем набор хешей, содержащий от 1 до N.

foreach(int i in input)
{
   if(hashset.contains(i))
   {
      hashset.delete(i);
   }
}

return "remaining elements in Hashset.";

Остальные элементы в Hashset - это недостающие элементы.

Ответ 8

As you have given an array of n size and find the missing number when it in a sequence.

#include<stdio.h>
main()
{
print("value of n");
scan("%d",&n);
print("enter the elements");
for(i=0;i<n;i++)
scan("%d",&a[i]);
for(i=0;i<n;i++)
{
d1[i]=a[i+1]-a[i];
temp=d1[i];
d[i]=temp;
}
for(i=0;i<n;i++)
{
if(d[i]==d[i+1]
{
c=d[i];
break;
}
}
for(i=0;i<n;i++)
b[i]=a[0]+i*c;
for(i=0;i<n;i++)
{
miss=0;
for(j=0;j<n;j++)
{
if(b[i]!=a[j])
{
miss++;
}
if(miss==n)
print("missing no. %d",b[i]);
}
}

It would find the missing when its in sequence only.

Ответ 9

Пример фрагмента кода для поиска отсутствующих элементов без сортировки массива ниже:

     public static void series(int[] arr) {
     for (int i = 0; i < arr.length; i++) {
        while (arr[i] != i + 1) {
            int jump = arr[arr[i] - 1];
            if (jump == arr[i]) {
                break;
            }
            arr[arr[i] - 1] = arr[i];
            arr[i] = jump;
        }
     }
     System.out.println("Missing number is ");
     for (int i = 0; i < arr.length; i++) {
        if (arr[i] != i + 1) {
            System.out.println(i + 1);
        } else {
            arr[i] = -1;
        }
     }

Этот код работает для ряда чисел от 0 до N.