2012-04-06 2 views
5

Я рассматриваю наилучший возможный способ удаления дубликатов из массива (Unsorted) строк - массив содержит миллионы или десятки миллионов строкz. Массив уже, поэтому цель оптимизации заключается только в удалении дубликатов и не мешает обманывать изначально заселение!удаляет повторяющиеся строки из массивного массива в java эффективно?

Я думал по линиям делать сортировку, а затем бинарный поиск, чтобы получить поиск в журнале (n) вместо n (линейного) поиска. Это даст мне nlogn + n поисков, которые лучше, чем несортированные (n^2) search =, но это все еще кажется медленным. (Также рассматривался вдоль линий хеширования, но не был уверен в пропускной способности)

Пожалуйста, помогите! Ищете эффективное решение, которое учитывает скорость и память, так как миллионы строк задействованы без использования API коллекций!

+2

* Почему бы вам не использовать API коллекций? –

+1

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

ответ

7

До последнего предложения, ответ казался очевидным для меня: использовать HashSet<String> или LinkedHashSet<String>, если вам нужно сохранить порядок:

HashSet<String> distinctStrings = new HashSet<String>(Arrays.asList(array)); 

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

+2

Хороший вопрос - это был вопрос, который меня спрашивал. Я предложил сравнить quiksort + соседний, но это было недостаточно для них. Я почти уверен, что они правы - я надеялся найти здесь людей, которые были бы лучше, чем nlogn + n? –

+0

@PreatorDarmatheon: Создание хеш-набора, вероятно, будет O (n), предполагая разумную реализацию и низкие столкновения. Но * пожалуйста * укажите контекст в будущем. –

+0

Я вижу - разумным - какие подводные камни вы предлагаете, если стратегия реализации ошибочна? Любой хороший ресурс для создания такого хешета для критериев, с которыми я сталкиваюсь? –

0

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

Если вы поместите все записи в набор типа коллекции. Вы можете использовать

HashSet(int initialCapacity) 

конструктор для предотвращения расширения памяти во время работы.

Set<T> mySet = new HashSet<T>(Arrays.asList(someArray)) 

Arrays.asList() имеет время выполнения O (п), если память не должны быть расширены.

2

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

Для получения дополнительной информации о см http://en.wikipedia.org/wiki/Merge_sort слиянии

0

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

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

Первым элементом в массиве будет корень.

  1. Если следующий элемент равен узлу, верните его. -> удалить удаляемые элементы

  2. Если следующий элемент меньше узла, сравните его влево, а затем сравните его вправо.

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

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

Перемещение принимает O (n) и поиск двоичного дерева принимает O (logn) (вставка должна принимать только O (1), поскольку вы просто присоединяете его, а не перераспределяете/балансируете дерево), поэтому общее количество должно быть O (NlogN).

+0

'Вставка должна принимать только O (1)' в каком мире ?! Я НЕ голосую за это. Но подумай об этом. – kasavbere

+0

Да, в двоичном дереве поиска средняя вставка должна принимать o (logn). Эта вставка O (logn) на самом деле потому, что она начинается с поиска. Мое предложение заключалось в том, что поиск уже состоялся из O (logn), чтобы найти нужный узел, поэтому фактическая вставка просто привязывает новый узел либо к левому, либо к правому узлу. Разве это не просто O (1)? – evanwong

4

АНАЛИЗ

Давайте выполнять некоторый анализ:

  1. Использование HashSet. Сложность времени - O (n). Сложность пространства O (n). Обратите внимание, что для этого требуется около 8 * байтов размера массива (8-16 байтов - ссылка на новый объект).

  2. Быстрый поиск. Время - O (n * log n). Пространство O (log n) (наихудший случай O (n * n) и O (n) соответственно).

  3. Merge Sort (бинарное дерево/TreeSet). Время - O (n * log n). Пространство O (n)

  4. Куча сортировка. Время O (n * log n). Пространство O (1). (но медленнее, чем 2 и 3).

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

ЗАКЛЮЧЕНИЕ

  1. Если время ваша забота, и вы не против выделения 8 * array.length байт для HashSet - это решение представляется оптимальным.

  2. Если космос является проблемой, то QuickSort + один проход.

  3. Если пространство является большой проблемой - реализуйте кучу с выбросом дубликатов на лету. Он все еще O (n * log n), но без дополнительного пространства.

+0

спасибо eugene - четкий и ясный ответ –

+0

Хорошо, за исключением идеи кучи. «выбросить дубликаты на лету». Ну, правда? – kasavbere

+0

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

1

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

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

ВАРИАНТ 1:

public static void removeDuplicates(String[] input){ 
    Arrays.sort(input);//Use mergesort/quicksort here: n log n 
    for(int i=1; i<input.length; i++){ 
     if(input[i-1] == input[i]) 
      input[i-1]=null; 
    }  
} 

ВАРИАНТ 2:

public static String[] removeDuplicates(String[] input){ 
    Arrays.sort(input);//Use mergesort here: n log n 
    int size = 1; 
    for(int i=1; i<input.length; i++){ 
     if(input[i-1] != input[i]) 
      size++; 
    } 
    System.out.println(size); 
    String output[] = new String[size]; 
    output[0]=input[0]; 
    int n=1; 
    for(int i=1;i<input.length;i++) 
     if(input[i-1]!=input[i]) 
      output[n++]=input[i]; 
    //final step: either return output or copy output into input; 
    //here I just return output 
    return output; 
} 

ВАРИАНТ 3: (добавленные 949300, на основании варианта 1).Обратите внимание, что этот управляет входным массивом, если это неприемлемо, вы должны сделать копию.

public static String[] removeDuplicates(String[] input){ 
    Arrays.sort(input);//Use mergesort/quicksort here: n log n 
    int outputLength = 0; 
    for(int i=1; i<input.length; i++){ 
     // I think equals is safer, but are nulls allowed in the input??? 
     if(input[i-1].equals(input[i])) 
      input[i-1]=null; 
     else 
      outputLength++; 
    } 

    // check if there were zero duplicates 
    if (outputLength == input.length) 
     return input; 

    String[] output = new String[outputLength]; 
    int idx = 0; 
    for (int i=1; i<input.length; i++) 
     if (input[i] != null) 
      output[idx++] = input[i]; 

    return output; 
} 
+0

Мне нравится этот общий подход, хотя для безопасности я использовал бы equals() вместо ==. См. Отредактированный вариант 3. – user949300

+0

Cousre! Я сначала написал его с помощью int [], потому что было легче протестировать. Будет ли редактировать – kasavbere

+0

проверить мой отредактированный вариант 3, который основан на вашей опции 1/2, но только цикл сравнения один раз. – user949300

0

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

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

  2. Петля снова через массив, с другим BitSet. True означает, что String проходит. Если хэш-код для String не существует в Bitset, вы можете просто пометить его как true. Иначе, отметьте его как возможно дублирующее, как ложное. Пока вы на нем, подсчитайте количество возможных дубликатов.

  3. Соберите все возможные дубликаты в большой String [], названный possibleDuplicates. Сортируйте его.

  4. Теперь пройдите через возможные дубликаты в исходном массиве и двоичный поиск в возможныхDuplicates. Если присутствует, ну, вы все еще застряли, потому что хотите включить его ОДИН, но не во все другие времена. Поэтому вам нужен еще один массив. Беспокойство, и мне нужно пойти поужинать, но это начало ...

Смежные вопросы