Реализация набора строк - PullRequest
2 голосов
/ 04 ноября 2011

Я должен реализовать набор ADT для пары строк. Интерфейс, который я хочу (в Java):

public interface StringSet {
  void add(String a, String b);
  boolean contains(String a, String b);
  void remove(String a, String b);
}

Шаблон доступа к данным имеет следующие свойства:

  1. Операция contains встречается гораздо чаще, чем операции add и remove.
  2. Чаще всего это не так, contains возвращает true, т.е. поиск успешен

Простая реализация, о которой я могу подумать, - это использовать двухуровневую хеш-таблицу, т.е. HashMap<String, HashMap<String, Boolean>>. Но эта структура данных не использует две особенности шаблона доступа. Мне интересно, есть ли что-то более эффективное, чем хеш-таблица, возможно, за счет использования особенностей шаблона доступа.

Ответы [ 4 ]

3 голосов
/ 04 ноября 2011

Лично я бы разработал это в терминах стандартного Set<> интерфейса:

public class StringPair {
   public StringPair(String a, String b) {
     a_ = a;
     b_ = b;
     hash_ = (a_ + b_).hashCode();
   }

   public boolean equals(StringPair pair) {
      return (a_.equals(pair.a_) && b_.equals(pair.b_));
   }

   @Override
   public boolean equals(Object obj) {
      if (obj instanceof StringPair) {
        return equals((StringPair) obj);
      }
      return false;
   }

   @Override
   public int hashCode() {
     return hash_;
   }

   private String a_;
   private String b_;
   private int hash_;
}

public class StringSetImpl implements StringSet {
   public StringSetImpl(SetFactory factory) {
     pair_set_ = factory.createSet<StringPair>();
   }

   // ...

   private Set<StringPair> pair_set_ = null;
}

Тогда вы можете оставить его на усмотрение пользователя StringSetImpl, чтобы использовать предпочтительный тип Set.Тем не менее, если вы пытаетесь оптимизировать доступ, это трудно сделать лучше, чем HashSet <> (по крайней мере, с точки зрения сложности времени выполнения), учитывая, что доступ равен O (1), тогда как для наборов на основе дерева есть O (log N).время доступа.

То, что содержит (), обычно возвращает true, может стоить рассмотреть Фильтр Блума , хотя для этого необходимо, чтобы было разрешено некоторое количество ложных срабатываний для метода has () (нене знаю, так ли это).

Редактировать

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

public class StringSetImpl implements StringSet {
   public StringSetImpl() {
     elements_ = new HashMap<String, Set<String>>();
   }

   public boolean contains(String a, String b) {
     if (!elements_.containsKey(a)) {
       return false;
     }
     Set<String> set = elements_.get(a);
     if (set == null) {
       return false;
     }
     return set.contains(b);
   }

   public void add(String a, String b) {
     if (!elements_.containsKey(a) || elements_.get(a) == null) {
       elements_.put(a, new HashSet<String>());
     }
     elements_.get(a).add(b);
   }

   public void remove(String a, String b) {
     if (!elements_.containsKey(a)) {
       return;
     }
     HashSet<String> set = elements_.get(a);
     if (set == null) {
       elements_.remove(a);
       return a;
     }
     set.remove(b);
     if (set.empty()) {
       elements_.remove(a);
     }
   }

   private Map<String, Set<String>> elements_ = null;
}

Поскольку сейчас я нахожусь в 4:20 утра, вышеописанное, безусловно, не самая лучшая моя работа (слишком утомленная, чтобы обновлятьсяобработка нуля этими различными типами коллекций), но это обрисовывает в общих чертах подход.

0 голосов
/ 04 ноября 2011

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

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

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

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

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

Ps Три можно рассматривать как комбинацию дерева и нескольких хеш-таблиц,так что это дает вам лучшее из обеих структур данных.

0 голосов
/ 04 ноября 2011

Я бы рекомендовал Майклу Аарону Сафяну использовать тип StringPair.Возможно, с более конкретным именем или в качестве универсального типа кортежа: Tuple<A,B> создается Tuple<String,String>.Но я настоятельно рекомендую использовать одну из предоставленных реализаций набора, либо HashSet, либо TreeSet.

0 голосов
/ 04 ноября 2011
  1. Red-Black Tree реализация set будет хорошим вариантом. C++ STL реализовано в Red-Black Tree
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...