2011-01-14 2 views
5

У меня есть массив, содержащий имена элементов. Я хочу дать пользователю возможность создавать элементы без указания их имени, поэтому моя программа должна будет предоставить уникальное имя по умолчанию, например «Item 1».Эффективный алгоритм для поиска первого доступного имени

Проблема заключается в том, что имя должно быть уникальным, поэтому я должен проверить весь массив для этого имени по умолчанию, и если есть элемент с тем же именем, я должен изменить имя как «Item 2» и так что пока я не найду доступное имя.

Очевидное решение будет что-то вроде этого:

String name = "Item "; 
for (int i = 0; !isAvailable(name + i) ; i++); 

Моя проблема с этим алгоритмом, что он работает в O (N^2).

Интересно, существует ли известный (или новый) более эффективный алгоритм для решения этого случая.

Другими словами, мой вопрос заключается в следующем: существует ли какой-либо алгоритм, который найдет первое большее, чем нулевое число, которое доза НЕ существует в данном массиве и работает меньше, чем N^2?

+0

Должно ли это имя быть уникальным или оно должно быть первой доступной строкой в ​​последовательности «Пункт 1», «Пункт 2» и т. Д.? Вы можете значительно снизить ожидаемое время исполнения, выбрав научное имя («Item xF3g7a»). Вы также можете улучшить массив для структуры данных: если порядок не важен, не используйте массив, и, если важно, возможно, сохраните вторичный индекс имен, а также массив. –

+0

+1 Ace вопрос! –

ответ

4

Вы, конечно, можете сделать это в O (N) времени, N-число элементов в массиве:

  • Один из "пункта 1", "Пункт 2", ... «Пункт N +1 "должно быть бесплатным, поэтому создайте массив из N + 1 флагов.
  • Пройдите по пунктам, и для каждого имени, если оно имеет форму «Item k» с 0 < k < = N + 1, установите этот флаг.
  • Сканирование массива флагов для первого четкого флага.

Дополнительное требование к памяти - это N + 1 бит, что, безусловно, превосходит любую структуру данных, которая фактически хранит все N имен.

+0

+1 Мне это очень нравится. –

+0

Я также рассматривал это, но это предполагает, что числа в элементах достаточно малы, чтобы использоваться в качестве индекса в векторе. Если есть элемент под названием «Item 123456789132456789», тогда ваш массив будет огромным. С другой стороны, если значение настолько велико, вы, вероятно, его вообще не интересуете. – Patrick

+0

@Patrick: массив флагов будет только таким огромным, если в массиве элементов есть 123456789132456788 элементов. Если в массиве всего 4 элемента, и один из них называется «Item 123456789132456789», это не имеет значения, потому что мой флаг-массив занимает всего 5 бит. «Item 123456789132456789», не является кандидатом на наименее неиспользуемое имя, а не «Fred», поэтому мне тоже не нужно записывать. Требование для N + 1 бит памяти раздражает, но на практике редко трудно удовлетворить, так как это очень маленький процент пространства, уже занятого самими элементами. –

3

Да, есть.

Первый сортировать массив. Затем пропустите его и верните первый элемент, значение которого не равно его индексу (плюс 1). Сорт O (n log n), конечным шагом является O (n), поэтому все это O (n log n).

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

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

+0

+1 Да, это способ сделать это! –

+0

А потом я увидел решение Стива, которое лучше! –

+0

Спасибо, Томас, отличный ответ! на самом деле, я думал о чем-то очень похожем, но у меня был недостающий кусок, который вы только что положили на место. Хотя я пишу свою программу для iPhone, поэтому я, вероятно, буду использовать ваш алгоритм, потому что он использует меньше памяти, чем алгоритм Стива, я принимаю ответ Стива здесь, потому что его решение более эффективно. –

1

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

+1

Тогда, если у вас есть Item1, Item2 и Item8, вы добавите Item9. Но пользователь, вероятно, хочет, чтобы Item3 повторно использовался. –

+0

Может быть, но «дефрагментация» элементов может не иметь смысла и/или может не потребоваться. Если нет, это алгоритм O (1) как в пространстве выполнения, так и в пространстве *. – EmeryBerger

+0

Кроме того, если требуется «дефрагментация», это можно сделать лениво и только в конце последовательности элементов. Например, как только элемент [max-1] больше не используется, вы можете сбросить max до max-1. – EmeryBerger

1

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

1

Вы можете попробовать сделать следующее:

первый:

  • цикл по списку, и получить все пронумерованные элементы, это сложности N
  • для каждого элемента нумерованного, положить товар в виде дерева (в C++: станд :: карта), это журнал сложности (N)

Итак, теперь вы создали карту с использованными номерами, со сложностью «N х журнал (N)»

Затем перейдите к дереву, и как только вы увидите «отверстие», используйте номер. Худший случай - это сложность N.

Таким образом, сложность N x log (N) + N или упрощенная: N log (N).

0

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

Таким образом, у вас есть стоимость O (1) для определения уникального имени.

+0

Я хочу ** Первое ** уникальное имя с форматом «Пункт» + i –

+0

Не будет ли «элемент» + наносекунды выполнять это требование? – Davidann

+0

он уникален, но это не первое доступное имя указанного формата. –

0

логарифмического время подход, при условии, что вы никогда не оставляйте «дырку», исключив пункты:

// Inverse binary search for the last one. 
int upperBound = 1; 
while(isInUse(upperBound)) upperBound *= 2; 

// Standard binary search for the end once we have a ballpark. 
int lowerBound = upperBound/2; 
while(lowerBound < upperBound - 1) 
{ 
    int midpoint = (lowerBound + upperBound)/2; 
    if (isInUse(midpoint)) 
     lowerBound = midpoint; 
    else 
     upperBound = midpoint; 
} 
return upperBound; 

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