Интервью по программированию Вопрос / как найти, если любые два целых числа в массиве равны нулю? - PullRequest
4 голосов
/ 29 июня 2010

Не вопрос домашнего задания, а возможный вопрос для интервью ...

  1. Учитывая массив целых чисел, напишите алгоритм, который проверит, равна ли сумма любых двух нулю.
  2. Что такое Большой О этого решения?

Ищем методы не грубой силы

Ответы [ 9 ]

10 голосов
/ 29 июня 2010

Использовать таблицу поиска: сканировать массив, вставляя все положительные значения в таблицу. Если вы столкнулись с отрицательным значением той же величины (которое вы легко можете найти в таблице); сумма их будет равна нулю. Таблица поиска может быть хеш-таблицей для экономии памяти.

Это решение должно быть O (N).

Псевдокод:

var table = new HashSet<int>();
var array = // your int array
foreach(int n in array)
{
     if ( !table.Contains(n) ) 
         table.Add(n);
     if ( table.Contains(n*-1) )
         // You found it.;
}
8 голосов
/ 29 июня 2010

Упомянутое хеш-таблицей решение обычно O(n), но теоретически оно может выродиться до O(n^2).

Вот решение Theta(n log n), которое никогда не вырождается:

Sort the array (optimal quicksort, heap sort, merge sort are all Theta(n log n))
for i = 1, array.len - 1
    binary search for -array[i] in i+1, array.len

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

3 голосов
/ 29 июня 2010

Решение O (n log n) (т. Е. Сортировка) состояло бы в том, чтобы отсортировать все значения данных, а затем запустить указатель от наименьшего к наибольшему при одновременном запуске указателя от наивысшего к наименьшему:

def findmatch(array n):
    lo = first_index_of(n)
    hi = last_index_of(n)
    while true:
        if lo >= hi:                   # Catch where pointers have met.
            return false
        if n[lo] = -n[hi]:             # Catch the match.
            return true
        if sign(n[lo]) = sign(n[hi]):  # Catch where pointers are now same sign.
            return false
        if -n[lo] > n[hi]:             # Move relevant pointer.
            lo = lo + 1
        else:
            hi = hi - 1

Решение сложности времени O (n) состоит в том, чтобы поддерживать массив всех встреченных значений:

def findmatch(array n):
    maxval = maximum_value_in(n)         # This is O(n).
    array b = new array(0..maxval)       # This is O(1).
    zero_all(b)                          # This is O(n).
    for i in index(n):                   # This is O(n).
        if n[i] = 0:
            if b[0] = 1:
                return true
            b[0] = 1
            nextfor

        if n[i] < 0:
            if -n[i] <= maxval:
                if b[-n[i]] = 1:
                    return true;
                b[-n[i]] = -1
            nextfor

        if b[n[i]] = -1:
            return true;
        b[n[i]] = 1

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

Итак, если в любой точке мы найдем -12, мы установим b [12] в -1.Потом, если мы найдем 12, мы знаем, что у нас есть пара.То же самое для нахождения положительного числа первым, за исключением того, что мы устанавливаем знак в 1. Если мы находим два -12 в ряду, это все равно устанавливает b [12] в -1, ожидая, пока 12 сместит его.

в этом коде есть только особые случаи:

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

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

В этом случае вы бывероятно, вернитесь к решению сортировки O (n log n), но, если вы знаете пределы заранее (скажем, если вы ограничиваете целые числа диапазоном [-100,100]), это может бытьмощная оптимизация.


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

def findmatch(array num):
    # Array empty means no match possible.
    if num.size = 0:
        return false

    # Find biggest value, no match possible if empty.
    max_positive = num[0]
    for i = 1 to num.size - 1:
        if num[i] > max_positive:
            max_positive = num[i]
    if max_positive < 0:
        return false

    # Create and init array of positives.
    array found = new array[max_positive+1]
    for i = 1 to found.size - 1:
        found[i] = false
    zero_found = false

    # Check every value.
    for i = 0 to num.size - 1:
        # More than one zero means match is found.
        if num[i] = 0:
            if zero_found:
                return true
            zero_found = true

        # Otherwise store fact that you found positive.
        if num[i] > 0:
            found[num[i]] = true

    # Check every value again.
    for i = 0 to num.size - 1:
        # If negative and within positive range and positive was found, it's a match.
        if num[i] < 0 and -num[i] <= max_positive:
            if found[-num[i]]:
                return true

    # No matches found, return false.
    return false

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

1 голос
/ 29 июня 2010

Я думаю, что ответом IVlad, вероятно, является то, что вам нужно, но вот немного более нестандартный подход.

Если целые числа, вероятно, будут маленькими, а память не является ограничением, тогда вы можете использоватьBitArray коллекция.Это класс .NET в System.Collections, хотя Microsoft C ++ имеет эквивалент bitset.

Класс BitArray выделяет кусок памяти и заполняет его нулями.Затем вы можете 'получить' и 'установить' биты в указанном индексе, чтобы вы могли вызвать myBitArray.Set(18, true), что бы установить бит в индексе 18 в блоке памяти (который затем читает что-то вроде 00000000, 00000000, 00100000).Операция по установке бита является операцией O (1).

Итак, предполагая 32-разрядную целочисленную область и 1 ГБ резервной памяти, вы можете сделать следующий подход:

BitArray myPositives = new BitArray(int.MaxValue);
BitArray myNegatives = new BitArray(int.MaxValue);
bool pairIsFound = false;
for each (int testValue in arrayOfIntegers)
{
    if (testValue < 0)
    {
        // -ve number - have we seen the +ve yet?
        if (myPositives.get(-testValue))
        {
            pairIsFound = true;
            break;
        }
        // Not seen the +ve, so log that we've seen the -ve.
        myNegatives.set(-testValue, true);
    }
    else
    {
        // +ve number (inc. zero). Have we seen the -ve yet?
        if (myNegatives.get(testValue))
        {
            pairIsFound = true;
            break;
        }
        // Not seen the -ve, so log that we've seen the +ve.
        myPositives.set(testValue, true);
        if (testValue == 0)
        {
            myNegatives.set(0, true);
        }
    }
}

// query setting of pairIsFound to see if a pair totals to zero.

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

Хорошо - это другое, но я думаю, это самое быстрое решениепока что.

Комментарии?

0 голосов
/ 07 июля 2010

Вот небольшая вариация решения IVlad, которое, на мой взгляд, концептуально проще, а также n log n, но с меньшим количеством сравнений. Общая идея состоит в том, чтобы начать с обоих концов отсортированного массива и направить индексы друг к другу. На каждом шаге перемещайте только индекс, значение массива которого больше 0 - только в сравнениях Theta (n) вы узнаете ответ.

sort the array (n log n)

loop, starting with i=0, j=n-1
  if a[i] == -a[j], then stop:
    if a[i] != 0 or i != j, report success, else failure
  if i >= j, then stop: report failure
  if abs(a[i]) > abs(a[j]) then i++ else j--
1003

например.,

[ -4, -3, -1, 0, 1, 2 ]     notes:
  ^i                ^j      a[i]!=a[j], i<j, abs(a[i])>abs(a[j])
      ^i            ^j      a[i]!=a[j], i<j, abs(a[i])>abs(a[j])
          ^i        ^j      a[i]!=a[j], i<j, abs(a[i])<abs(a[j])
          ^i     ^j         a[i]==a[j] -> done
0 голосов
/ 07 июля 2010

Вот хороший математический способ сделать это: имейте в виду все простые числа (т.е. создайте массив prime[0 .. max(array)], где n - длина входного массива, так что prime[i] обозначает i -ое простое число.

counter = 1
for i in inputarray:
    if (i >= 0):
        counter = counter * prime[i]
for i in inputarray:
    if (i <= 0):
        if (counter % prime[-i] == 0):
            return "found"
return "not found"

Однако проблема, когда дело доходит до реализации, состоит в том, что хранение / умножение простых чисел в традиционной модели - просто O (1), но если массив (т.е. n) достаточно большой, эта модель не подходит.

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

0 голосов
/ 29 июня 2010

Учитывая отсортированный массив, вы можете найти пары чисел (-n и + n), используя два указателя:

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

Теперь это O (n), но сортировка (если необходимо) - O (n * log (n)).

РЕДАКТИРОВАТЬ: пример кода (C #)

// sorted array
var numbers = new[]
{
    -5, -3, -1, 0, 0, 0, 1, 2, 4, 5, 7, 10 , 12
};

var npointer = 0;   // pointer to negative numbers
var ppointer = numbers.Length - 1;  // pointer to positive numbers

while( npointer < ppointer )
{
    var nnumber = numbers[npointer];
    var pnumber = numbers[ppointer];

    // each pointer scans only its number range (neg or pos)
    if( nnumber > 0 || pnumber < 0 )
    {
        break;
    }

    // Do we have a match?
    if( nnumber + pnumber == 0 )
    {
        Debug.WriteLine( nnumber + " + " + pnumber );
    }

    // Adjust one pointer
    if( -nnumber > pnumber )
    {
        npointer++;
    }
    else
    {
        ppointer--;
    }
}

Интересно: у нас в массиве 0, 0, 0. Алгоритм выведет две пары. Но на самом деле есть три пары ... нам нужно больше уточнений, что именно должно быть выведено.

0 голосов
/ 29 июня 2010

Может быть, вставьте каждое число в хеш-таблицу, и если вы видите отрицательную проверку на коллизию? На). Вы уверены, что вопрос не в том, чтобы найти, ЛЮБАЯ сумма элементов в массиве равна 0?

0 голосов
/ 29 июня 2010

Сумма двух целых может быть нулевой, только если одно является отрицательным от другого, например, 7 и -7 или 2 и -2.

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