Двоичные деревья C# и словари

Я борюсь с понятием того, когда использовать деревья двоичного поиска и когда использовать словари.

В моем приложении я действительно немного экспериментировал, который пользовался библиотекой C5 TreeDictionary (которому я верю, красно-черное дерево двоичного поиска), и словарь C#. Словарь был всегда быстрее в, добавляют/находят операции и также всегда использовали меньше пространства памяти. Например, в 16 809 <int, float> записи, словарь использовал 342 кибибайта, пока дерево использовало 723 кибибайта.

Я думал, что BST, как предполагалось, было большей эффективной памятью, но кажется, что один узел дерева требует большего количества байтов, чем одна запись в словаре. Что дает? Существует ли точка в том, где BST лучше, чем словари?

Кроме того, как вопрос о стороне, делает любой знает, существует ли более быстрое + больше памяти эффективная структура данных для хранения <int, float> пары для словаря вводят доступ, чем любая из упомянутых структур?

16
задан David Pfeffer 28 January 2010 в 01:52
поделиться

5 ответов

Я думал, что BST предполагалось быть более эффективным памятью, но кажется что один узел дерева требует больше байтов, чем одна запись в Словарь. Что дает? Есть ли указать, где BST лучше, чем Словари?

Я лично никогда не слышал о таком принципе. Даже еще его только общий принцип, а не категорический факт, оформленный в ткани вселенной.

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

LinkedList<Tuple<TKey, TValue>> list =
    internalArray[internalArray % key.GetHashCode()];
if (list.Exists(x => x.Key == key))
    throw new Exception("Key already exists");
list.AddLast(Tuple.Create(key, value));

так его почти O (1) операция. Словарь использует память o (InternationArray.length + N), где n представляет собой количество элементов в коллекции.

В целом BSTS может быть реализован как:

  • связанные списки, которые используют пространство O (N), где n представляет собой числовые элементы в коллекции.
  • Массивы , которые используют O (2 H - N) пространство, где H - высота дерева, а N - количество предметов в коллекции.
    • Поскольку красно-черные деревья имеют ограниченную высоту O (1.44 * N), реализация массива должна иметь ограниченное использование памяти около O (2 1.44N - N)

, Treeneionary C5 реализован с использованием массивов, которые, вероятно, ответственны за потраченное пространство.

Что дает? Есть ли точка в том, где BST лучше, чем словари?

словари имеют некоторые нежелательные свойства:

  • Не может быть недостаточно непрерывных блоков памяти для удержания вашего словаря, даже если его требования к памяти намного меньше, чем общая доступная ОЗУ.

  • Оценка хеш-функции может принимать произвольно долгую продолжительность времени. Строки, например, используют отражатель для изучения метода System.String.GethashCode - вы заметите хеширование строки, всегда принимают o (n) время, что означает, что он может занять значительное время для очень длинных струн Отказ С рукой, сравнивая строки для неравенства почти всегда быстрее, чем хеширование, поскольку это может потребовать смотреть на первые несколько символов. Это полностью возможно для вставок дерева, чтобы быть быстрее, чем словарные вставки, если оценка хеш-кода занимает слишком длинную.

    • INT32 Метод gethashcode буквально просто возвращает это , поэтому вы были бы сблудочены, чтобы найти случай, когда hashtable с ключами int медленнее, чем словарь дерева.

RB-деревья имеют некоторые желательные свойства:

  • Вы можете найти / удалить минимальные и максимальные элементы в O (log n), по сравнению с o (n) временем с использованием словаря.

  • Если дерево реализуется как связанный список, а не массив, дерево - это обычно больше пространства эффективно, чем в словаре.

  • Аналогично, его смешное легко писать неизменные версии деревьев, которые поддерживают вставку / поиск / удаление в O (log n). Словари не адаптируются к неизменности, поскольку вам нужно скопировать весь внутренний массив для каждой операции (на самом деле, I имеет , наблюдал . , но реализация очень сложная).

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

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

8
ответ дан 30 November 2019 в 23:09
поделиться

Вы не сравниваете «яблоки с яблоками», A BST предоставит вам представление , в то время как словарь позволяет сделать поиск в паре значения ключа ( в твоем случае ).

Я бы не ожидал большого размера в след памяти между 2, но словарь даст вам гораздо быстрый поиск. Чтобы найти предмет в BST (потенциально) надо перейти к всему дереву. Но сделать Dictnary Looking Вы просто обратитесь в поиска на основе ключа.

0
ответ дан 30 November 2019 в 23:09
поделиться

Это имеет смысл, что узел дерева потребует больше хранения, чем в записи словаря. Узел двоичного дерева должен хранить значение и левые, так и правые поддеревы. Общий словарь реализован в виде хеш-таблица, который - я предполагаю - либо использует связанный список для каждого ведра (значение плюс один указатель / ссылка), либо каким-то образом переменным (просто Значение). Я должен был бы иметь заглянуть в отражатель, чтобы быть уверенным, но с целью этого вопроса я не думаю, что это важно.

Реструктуру хэш-таблица, тем менее эффективным с точки зрения хранения / памяти. Если вы создаете хэш-таблица (словарь) и инициализируйте его вместимость до 1 миллиона, и только заполните его 10 000 элементов, тогда я уверен, что он съел бы намного больше памяти, чем BST с 10 000 узлов.

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


Если вопрос: «Почему вы хотите использовать двоичное дерево вместо хэш-таблица?» Тогда лучший ответ IMO заключается в том, что двоичные деревья заказываются, тогда как хэш-таблицы нет. Вы можете найти только хэш-таблицу для ключей, которые точно равны кое-что; С деревом вы можете искать ряд ценностей, ближайшее значение и т. Д. Это довольно важное различие, если вы создаете индекс или что-то подобное.

2
ответ дан 30 November 2019 в 23:09
поделиться

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

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

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

0
ответ дан 30 November 2019 в 23:09
поделиться

Мне кажется, вы проводите преждевременную оптимизацию.

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

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

1
ответ дан 30 November 2019 в 23:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: