Как найти неизвестное число в массиве

Let the given array be A with length N. Lets assume in the given array, the single empty slot is filled with 0.

We can find the solution for this problem using many methods including algorithm used in Counting sort. But, in terms of efficient time and space usage, we have two algorithms. One uses mainly summation, subtraction and multiplication. Another uses XOR. Mathematically both methods work fine. But programatically, we need to assess all the algorithms with main measures like

  • Limitations(like input values are large(A[1...N]) and/or number of
    input values is large(N))
  • Number of condition checks involved
  • Number and type of mathematical operations involved

etc. This is because of the limitations in time and/or hardware(Hardware resource limitation) and/or software(Operating System limitation, Programming language limitation, etc), etc. Lets list and assess the pros and cons of each one of them.

Algorithm 1 :

In algorithm 1, we have 3 implementations.

  1. Calculate the total sum of all the numbers(this includes the unknown missing number) by using the mathematical formula(1+2+3+...+N=(N(N+1))/2). Here, N=100. Calculate the total sum of all the given numbers. Subtract the second result from the first result will give the missing number.

    Missing Number = (N(N+1))/2) - (A[1]+A[2]+...+A[100])

  2. Calculate the total sum of all the numbers(this includes the unknown missing number) by using the mathematical formula(1+2+3+...+N=(N(N+1))/2). Here, N=100. From that result, subtract each given number gives the missing number.

    Missing Number = (N(N+1))/2)-A[1]-A[2]-...-A[100]

    (Note:Even though the second implementation’s formula is derived from first, from the mathematical point of view both are same. But from programming point of view both are different because the first formula is more prone to bit overflow than the second one(if the given numbers are large enough). Even though addition is faster than subtraction, the second implementation reduces the chance of bit overflow caused by addition of large values(Its not completely eliminated, because there is still very small chance since (N+1) is there in the formula). But both are equally prone to bit overflow by multiplication. The limitation is both implementations give correct result only if N(N+1)<=MAXIMUM_NUMBER_VALUE. For the first implementation, the additional limitation is it give correct result only if Sum of all given numbers<=MAXIMUM_NUMBER_VALUE.)

  3. Calculate the total sum of all the numbers(this includes the unknown missing number) and subtract each given number in the same loop in parallel. This eliminates the risk of bit overflow by multiplication but prone to bit overflow by addition and subtraction.

    //ALGORITHM
    missingNumber = 0;
    foreach(index from 1 to N)
    {
    missingNumber = missingNumber + index;
    //Since, the empty slot is filled with 0,
    //this extra condition which is executed for N times is not required.
    //But for the sake of understanding of algorithm purpose lets put it.
    if (inputArray[index] != 0)
    missingNumber = missingNumber - inputArray[index];
    }

In a programming language(like C, C++, Java, etc), if the number of bits representing a integer data type is limited, then all the above implementations are prone to bit overflow because of summation, subtraction and multiplication, resulting in wrong result in case of large input values(A[1...N]) and/or large number of input values(N).

Algorithm 2 :

We can use the property of XOR to get solution for this problem without worrying about the problem of bit overflow. And also XOR is both safer and faster than summation. We know the property of XOR that XOR of two same numbers is equal to 0(A XOR A = 0). If we calculate the XOR of all the numbers from 1 to N(this includes the unknown missing number) and then with that result, XOR all the given numbers, the common numbers get canceled out(since A XOR A=0) and in the end we get the missing number. If we don’t have bit overflow problem, we can use both summation and XOR based algorithms to get the solution. But, the algorithm which uses XOR is both safer and faster than the algorithm which uses summation, subtraction and multiplication. And we can avoid the additional worries caused by summation, subtraction and multiplication.

In all the implementations of algorithm 1, we can use XOR instead of addition and subtraction.

Lets assume, XOR(1...N) = XOR of all numbers from 1 to N

Implementation 1 => Missing Number = XOR(1...N) XOR (A[1] XOR A[2] XOR...XOR A[100])

Implementation 2 => Missing Number = XOR(1...N) XOR A[1] XOR A[2] XOR...XOR A[100]

Implementation 3 =>

//ALGORITHM
missingNumber = 0;
foreach(index from 1 to N)
{
    missingNumber = missingNumber XOR index;
    //Since, the empty slot is filled with 0,
    //this extra condition which is executed for N times is not required.
    //But for the sake of understanding of algorithm purpose lets put it.
    if (inputArray[index] != 0)
        missingNumber = missingNumber XOR inputArray[index];
}

All three implementations of algorithm 2 will work fine(from programatical point of view also). One optimization is, similar to

1+2+....+N = (N(N+1))/2

We have,

1 XOR 2 XOR .... XOR N = {N if REMAINDER(N/4)=0, 1 if REMAINDER(N/4)=1, N+1 if REMAINDER(N/4)=2, 0 if REMAINDER(N/4)=3}

We can prove this by mathematical induction. So, instead of calculating the value of XOR(1…N) by XOR all the numbers from 1 to N, we can use this formula to reduce the number of XOR operations.

Also, calculating XOR(1…N) using above formula has two implementations. Implementation wise, calculating

// Thanks to https://a3nm.net/blog/xor.html for this implementation
xor = (n>>1)&1 ^ (((n&1)>0)?1:n)

is faster than calculating

xor = (n % 4 == 0) ? n : (n % 4 == 1) ? 1 : (n % 4 == 2) ? n + 1 : 0;

So, the optimized Java code is,

long n = 100;
long a[] = new long[n];

//XOR of all numbers from 1 to n
// n%4 == 0 ---> n
// n%4 == 1 ---> 1
// n%4 == 2 ---> n + 1
// n%4 == 3 ---> 0

//Slower way of implementing the formula
// long xor = (n % 4 == 0) ? n : (n % 4 == 1) ? 1 : (n % 4 == 2) ? n + 1 : 0;
//Faster way of implementing the formula
// long xor = (n>>1)&1 ^ (((n&1)>0)?1:n);
long xor = (n>>1)&1 ^ (((n&1)>0)?1:n);

for (long i = 0; i < n; i++)
{
    xor = xor ^ a[i];
}
//Missing number
System.out.println(xor);

Учитывая массив n-1 различные целые числа в диапазоне от 1 до n, найти в нем пропущенное число за линейное время.

Например, рассмотрим массив {1, 2, 3, 4, 5, 7, 8, 9, 10} элементы которого различны и находятся в диапазоне от 1 до 10. Недостающее число — 6.

Потренируйтесь в этой проблеме

1. Использование формулы для суммы первых n Натуральные числа

Мы знаем, что сумма первых n натуральные числа можно вычислить по формуле 1 + 2 + … + n = n×(n+1)/2. Мы можем использовать эту формулу, чтобы найти пропущенное число.

Идея состоит в том, чтобы найти сумму целых чисел от 1 до n+1 используя приведенную выше формулу, где n размер массива. Также вычислите фактическую сумму целых чисел в массиве. Теперь недостающее число будет разницей между ними.

Этот подход демонстрируется ниже на C, Java и Python:

C

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

#include <stdio.h>

// Находим пропущенное число в заданном массиве

int getMissingNumber(int arr[], int n)

{

    // фактический размер `n+1`, так как в массиве отсутствует число

    int m = n + 1;

    // получить сумму целых чисел от 1 до `n+1`

    int total = m*(m + 1)/2;

    // получаем реальную сумму целых чисел в массиве

    int sum = 0;

    for (int i = 0; i < n; i++) {

        sum += arr[i];

    }

    // недостающее число – это разница между ожидаемой суммой

    // и фактическая сумма

    return total sum;

}

int main()

{

    int arr[] = { 1, 2, 3, 4, 5, 7, 8, 9, 10 };

    int n = sizeof(arr)/sizeof(arr[0]);

    printf(“The missing number is %d”, getMissingNumber(arr, n));

    return 0;

}

Скачать  Выполнить код

результат:

The missing number is 6

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

import java.util.Arrays;

class Main

{

    // Находим пропущенное число в заданном массиве

    public static int getMissingNumber(int[] arr)

    {

        // получаем длину массива

        int n = arr.length;

        // фактический размер `n+1`, так как в массиве отсутствует число

        int m = n + 1;

        // получить сумму целых чисел от 1 до `n+1`

        int total = m * (m + 1) / 2;

        // получаем реальную сумму целых чисел в массиве

        int sum = Arrays.stream(arr).sum();

        // недостающее число – это разница между ожидаемой суммой

        // и фактическая сумма

        return total sum;

    }

    public static void main(String[] args)

    {

        int[] arr = { 1, 2, 3, 4, 5, 7, 8, 9, 10 };

        System.out.println(“The missing number is “ + getMissingNumber(arr));

    }

}

Скачать  Выполнить код

результат:

The missing number is 6

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

# Найти недостающее число в заданном списке

def getMissingNumber(arr):

    # получить длину массива

    n = len(arr)

    # Фактический размер # равен `n+1`, так как в списке отсутствует номер.

    m = n + 1

    # получить сумму целых чисел от 1 до `n+1`

    total = m * (m + 1) // 2

    # пропущенное число – это разница между ожидаемой суммой и

    # фактическая сумма целых чисел в списке

    return total sum(arr)

if __name__ == ‘__main__’:

    arr = [1, 2, 3, 4, 5, 7, 8, 9, 10]

    print(‘The missing number is’, getMissingNumber(arr))

Скачать  Выполнить код

результат:

The missing number is 6

2. Использование оператора XOR

Мы знаем, что XOR двух одинаковых чисел отменяет друг друга. Мы можем воспользоваться этим фактом, чтобы найти недостающее число в массиве с ограниченным диапазоном.

Идея состоит в том, чтобы вычислить XOR всех элементов массива и вычислить XOR всех элементов от 1 до n+1, куда n размер массива. Теперь недостающее число будет XOR между ними.

Этот подход демонстрируется ниже на C, Java и Python:

C

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

#include <stdio.h>

// Находим пропущенное число в заданном массиве

int getMissingNumber(int arr[], int n)

{

    // Вычислить XOR всех элементов массива

    int xor = 0;

    for (int i = 0; i < n; i++) {

        xor = xor ^ arr[i];

    }

    // Вычислить XOR всех элементов от 1 до `n+1`

    for (int i = 1; i <= n + 1; i++) {

        xor = xor ^ i;

    }

    return xor;

}

int main()

{

    int arr[] = { 1, 2, 3, 4, 5, 7, 8, 9, 10 };

    int n = sizeof(arr) / sizeof(arr[0]);

    printf(“The missing number is %d”, getMissingNumber(arr, n));

    return 0;

}

Скачать  Выполнить код

результат:

The missing number is 6

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

class Main

{

    // Находим пропущенное число в заданном массиве

    public static int getMissingNumber(int[] arr)

    {

        // Вычислить XOR всех элементов массива

        int xor = 0;

        for (int i: arr) {

            xor = xor ^ i;

        }

        // Вычислить XOR всех элементов от 1 до `n+1`

        for (int i = 1; i <= arr.length + 1; i++) {

            xor = xor ^ i;

        }

        return xor;

    }

    public static void main(String[] args)

    {

        int[] arr = { 1, 2, 3, 4, 5, 7, 8, 9, 10 };

        System.out.println(“The missing number is “ + getMissingNumber(arr));

    }

}

Скачать  Выполнить код

результат:

The missing number is 6

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

# Найти недостающее число в заданном списке

def getMissingNumber(arr):

    # Вычислить XOR всех элементов в списке

    xor = 0

    for i in arr:

        xor = xor ^ i

    # Вычислить XOR всех элементов от 1 до `n+1`

    for i in range(1, len(arr) + 2):

        xor = xor ^ i

    return xor

if __name__ == ‘__main__’:

    arr = [1, 2, 3, 4, 5, 7, 8, 9, 10]

    print(‘The missing number is’, getMissingNumber(arr))

Скачать  Выполнить код

результат:

The missing number is 6

Временная сложность обоих рассмотренных выше методов составляет O(n) и не требует дополнительного места, где n это размер ввода.

Условие задачи

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

Пример:
Ввод: найти 5 в {15, 16, 19, 20, 25, 1, 3, 4, 5, 7, 10, 14}.
Вывод: 8 (индекс числа 5 в массиве).

Решение

Если у вас возникла мысль о бинарном поиске, вы правы!

В классической задаче бинарного поиска х сравнивают с центральной точкой, чтобы узнать, где находится х  — слева или справа. Сложность в нашем случае заключается в том, что массив был циклически сдвинут, а значит, может иметь точку перегиба. Рассмотрим, например, два следующих массива:

Arrayl : {10, 15, 20, 0, 5}
Array2 : { 50, 5, 20, 30, 40}

У обоих массивов есть средняя точка — 20, но в первом случае 5 находится справа от нее, а в другом — слева. Поэтому сравнения х со средней точкой недостаточно. Однако если разобраться поглубже, можно заметить, что половина массива упоря­дочена нормально (то есть по возрастанию). Следовательно, мы можем рассмотреть отсортированную половину массива и решить, где именно следует производить поиск — в левой или правой половине.

Например, если мы ищем 5 в Array1, то посмотрим на крайний левый элемент (10) и средний элемент (20). Так как 10 < 20, становится понятно, что левая половина отсортирована. Поскольку 5 не попадает в этот диапазон, мы знаем, что искомое место находится в правой половине массива.

В Array2 мы видим, что 50 > 20, а значит, отсортирована правая половина. Мы обращаемся к середине (20) и крайнему правому элементу (40), чтобы проверить, по­падает ли 5 в рассматриваемый диапазон. Значение 5 не может находиться в правой части, а значит, его нужно искать в левой части массива.

Задача становится более сложной, если левый и средний элементы идентич­ны, как в случае с массивом {2, 2, 2, 3, 4, 2}. В этом случае можно вы­полнить сравнение с правым элементом и, если он отличается, ограничить поиск правой половиной. В противном случае выбора не остается, и придется анализировать обе половины.

public class Question {
	
	public static int search(int a[], int x) {
		return search(a, 0, a.length - 1, x);
	}

	
	public static int search(int a[], int left, int right, int x) {
		int mid = (left + right) / 2;
		if (x == a[mid]) { // Элемент найден
			return mid;
		}
		if (right < left) {
			return -1;
		}
		
		/* Либо левая, либо правая половина должна быть нормально упорядочена .
                 * Найти нормально упорядоченную сторону, а затем использовать ее
                 * для определения стороны, в которой следует искать х. */
		if (a[left] < a[mid]) { // Левая половина нормально упорядочена .
			if (x >= a[left] && x < a[mid]) { 
				return search(a, left, mid - 1, x);// Искать слева
			} else {
				return search(a, mid + 1, right, x);// Искать справа
			}
		} else if (a[mid] < a[left]) { // Правая половина нормально упорядочена.
			if (x > a[mid] && x <= a[right]) {
				return search(a, mid + 1, right, x); // Искать справа
			} else {
				return search(a, left, mid - 1, x); // Искать слева
			}				
		} else if (a[left] == a[mid]) { // Левая половина состоит из повторов
			if (a[mid] != a[right]) { // Если правая половина отличается, искать в ней
				return search(a, mid + 1, right, x);// Искать справа
			} else { // Иначе искать придется в обеих половинах
				int result = search(a, left, mid - 1, x); // Искать слева
				if (result == -1) {
					return search(a, mid + 1, right, x); // Искать справа
				} else {
					return result;
				}
			}
		}
		return -1;
	}

	public static void main(String[] args) {
		int[] a = { 2, 3, 1, 2, 2, 2, 2, 2 , 2 , 2 };

		System.out.println(search(a, 2));
		System.out.println(search(a, 3));
		System.out.println(search(a, 4));
		System.out.println(search(a, 1));
		System.out.println(search(a, 8));
	}

}

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

Концептуально данная задача несложна, но написать идеальную реализацию до­статочно трудно. Не беспокойтесь, если вы допустите пару-тройку ошибок при решении этой задачи. Из-за высокого риска смещения на 1 и других второстепенных ошибок код необходимо тщательно протестировать.

Также вы можете попробовать реализовать алгоритм поиска элемента в отсортированной матрице размером MxN. Решение найдете здесь.

Источник: Карьера программиста

Один из наиболее часто задаваемых вопросов на собеседованиях — найти пропущенное число в массиве на Java, C # или любом другом языке. Такого рода вопросы задаются не только в небольших стартапах, но и в некоторых крупнейших технических компаниях, таких как Google, Amazon, Facebook, Microsoft.

В простейшей версии этого вопроса надо найти недостающий элемент в массиве целых чисел, содержащем числа от 1 до 100. Это можно легко решить, вычислив предполагаемую сумму с помощью n (n + 1) /2 и отняв от нее сумму всех существующих элементов массива. Это также один из самых быстрых и эффективных способов, но его нельзя использовать, если массив содержит более одного пропущенного числа или если массив содержит дубликаты.

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

В отсортированном массиве вы можете сравнить, равно ли число ожидаемому следующему числу или нет.

Кроме того, вы также можете использовать BitSet в Java для решения этой проблемы — надо перебрать все записи и использовать набор битов, чтобы запомнить, какие числа установлены, а затем проверить на 0 бит. Запись с битом 0 является отсутствующим номером.

пропущенное число

Найти пропущенное число в массиве: решение на Java

import java.util.Arrays;
import java.util.BitSet;
 
public class MissingNumberInArray {
 
    public static void main(String args[]) {

        // one missing number
        printMissingNumber(new int[]{1, 2, 3, 4, 6}, 6);
 
        // two missing number
        printMissingNumber(new int[]{1, 2, 3, 4, 6, 7, 9, 8, 10}, 10);
 
        // three missing number
        printMissingNumber(new int[]{1, 2, 3, 4, 6, 9, 8}, 10);
 
        // four missing number
        printMissingNumber(new int[]{1, 2, 3, 4, 9, 8}, 10);
 
        // Only one missing number in array
        int[] iArray = new int[]{1, 2, 3, 5};
        int missing = getMissingNumber(iArray, 5);
        System.out.printf("Missing number in array %s is %d %n",
                           Arrays.toString(iArray), missing);
    }

   /**
    * A general method to find missing values from an integer array in Java.
    * This method will work even if array has more than one missing element.
    */
    private static void printMissingNumber(int[] numbers, int count) {
        int missingCount = count - numbers.length;
        BitSet bitSet = new BitSet(count);
 
        for (int number : numbers) {
            bitSet.set(number - 1);
        }
 
        System.out.printf("Missing numbers in integer array %s, with total number %d is %n",
        Arrays.toString(numbers), count);
        int lastMissingIndex = 0;

        for (int i = 0; i < missingCount; i++) {
            lastMissingIndex = bitSet.nextClearBit(lastMissingIndex);
            System.out.println(++lastMissingIndex);
        }
 
    }

   /**
    * Java method to find missing number in array of size n containing
    * numbers from 1 to n only.
    * can be used to find missing elements on integer array of
    * numbers from 1 to 100 or 1 - 1000
    */
    private static int getMissingNumber(int[] numbers, int totalCount) {
        int expectedSum = totalCount * ((totalCount + 1) / 2);
        int actualSum = 0;
        for (int i : numbers) {
            actualSum += i;
        }
 
        return expectedSum - actualSum;
    }
 
}

Алгоритм с XOR

Мы можем использовать свойство XOR, чтобы получить решение этой проблемы, не беспокоясь о проблеме переполнения битов. Кроме того, XOR безопаснее и быстрее суммирования. Мы знаем свойство XOR, что XOR двух одинаковых чисел равен 0 ( A XOR A = 0). Если мы вычислим XOR всех чисел от 1 до N (это включает в себя неизвестное пропущенное число), а затем с этим результатом, XOR всех заданных чисел, общие числа будут отменены (так как A XOR A=0), и в конце мы получим пропущенное число. Если у нас нет проблемы переполнения битов, мы можем использовать как суммирование, так и алгоритмы на основе XOR, чтобы получить решение. Но алгоритм, использующий XOR, безопаснее и быстрее, чем алгоритм, который использует суммирование, вычитание и умножение. И мы можем избежать дополнительных забот, вызванных суммированием, вычитанием и умножением.

long n = 100;
long a[] = new long[n];

//XOR of all numbers from 1 to n
// n%4 == 0 ---> n
// n%4 == 1 ---> 1
// n%4 == 2 ---> n + 1
// n%4 == 3 ---> 0

//Slower way of implementing the formula
// long xor = (n % 4 == 0) ? n : (n % 4 == 1) ? 1 : (n % 4 == 2) ? n + 1 : 0;
//Faster way of implementing the formula
// long xor = (n>>1)&1 ^ (((n&1)>0)?1:n);
long xor = (n>>1)&1 ^ (((n&1)>0)?1:n);

for (long i = 0; i < n; i++)
{
   xor = xor ^ a[i];
}
//Missing number
System.out.println(xor);

Если вы нашли опечатку – выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.

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

$data = [1,2,5,6,9,11];

$prev = current($data);
$result = array_reduce($data, function($c, $item) use (&$prev){
                    if($item - $prev > 1){
                        $c = array_merge($c, range($prev + 1, $item - 1));
                    }
                    $prev = $item;
                    return $c;
                }, []);

А если специфика задачи такова, что у нас очень большой массив и получается очень много пропущенных значений (и не очень большом числе самих пропущенных интервалов), что использование функции range тратит не оправдано много памяти, то как по классическому примеру из справки по использованию генераторов можем сделать следующее:

$result =  function($data){
                $prev = current($data);
                $ranges = array_reduce($data, function($c, $item) use (&$prev){
                            if($item - $prev > 1)   $c[] = [$prev+1, $item -1];
                            $prev = $item;
                            return $c;
                          }, []);
                foreach($ranges as list($l, $h)){
                     while($l <= $h) yield $l++;
                }
            };

foreach($result($data) as $x) echo $x, "n";

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

Добавить комментарий