Красно-Черные Деревья - PullRequest
       61

Красно-Черные Деревья

54 голосов
/ 21 августа 2008

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

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

Так что же делает бинарные деревья полезными в некоторых общих задачах, которые вы выполняете во время программирования? Помимо этого, какие деревья вы предпочитаете использовать (пожалуйста, включите пример реализации) и почему?

Ответы [ 12 ]

54 голосов
/ 21 августа 2008

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

Красные Черные деревья решают эту проблему, заставляя ваше дерево быть сбалансированным при вставке или удалении. Это достигается с помощью серии вращений между узлами-предками и дочерними узлами. Алгоритм на самом деле довольно простой, хотя он немного длинный. Я бы посоветовал взять учебник CLRS (Cormen, Lieserson, Rivest и Stein) «Введение в алгоритмы» и почитать о деревьях RB.

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

Хотя BST не могут использоваться явно - один из примеров использования деревьев в целом есть почти в каждой современной СУБД. Точно так же ваша файловая система почти наверняка представлена ​​в виде некоторой древовидной структуры, и файлы также индексируются таким образом. Деревья питают Google. Деревья приводят в действие практически каждый веб-сайт в Интернете.

18 голосов
/ 21 августа 2008

Я хотел бы ответить только на вопрос «Так что же делает бинарные деревья полезными в некоторых общих задачах, которые вы выполняете во время программирования?»

Это большая тема, с которой многие не согласны. Некоторые говорят, что алгоритмы, изучаемые в степени CS, такие как бинарные деревья поиска и ориентированные графы, не используются в повседневном программировании и поэтому не имеют значения. Другие не согласны, утверждая, что эти алгоритмы и структуры данных являются основой для всего нашего программирования, и важно понимать их, даже если вам никогда не придется писать один для себя. Это фильтрует разговоры о хороших методах интервьюирования и найма. Например, Стив Йегге имеет статью о интервью в Google , в которой рассматривается этот вопрос. Помните эту дискуссию; опытные люди не согласны.

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

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

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

10 голосов
/ 02 декабря 2009

Ни в одном из ответов не упоминается, для чего нужны именно BST.

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

BST будет O (log N), где N - количество узлов в дереве, вставки также O (log N).

Деревья RB и AVL важны, как и другой ответ, упомянутый из-за этого свойства: если простой BST создается со значениями по порядку, то дерево будет таким же большим, как количество вставленных значений, что плохо сказывается на производительности поиска.

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

Почему вы готовы платить за дерево по хеш-таблице? ПОРЯДОК! Хеш-таблицы не имеют порядка, BST, с другой стороны, всегда естественным образом упорядочены в силу своей структуры. Поэтому, если вы выбросите кучу данных в массив или другой контейнер, а затем отсортируете их, лучшим решением будет BST.

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

Красные черные деревья используются внутри почти каждого упорядоченного контейнера языковых библиотек, C ++ Set and Map, .NET SortedDictionary, Java TreeSet и т. Д. ...

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

4 голосов
/ 21 августа 2008

Красные Черные Деревья и B-деревья используются во всех видах постоянного хранения; из-за того, что деревья сбалансированы, показатели ширины и глубины обхода уменьшены.

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

2 голосов
/ 26 июля 2011

Красно-черные деревья остаются сбалансированными, поэтому вам не нужно проходить глубоко, чтобы достать предметы. Сэкономленное время делает деревья RB O (log () n)) в случае WORST, тогда как неудачные двоичные деревья могут попасть в одностороннюю конфигурацию и вызвать поиск в O (n) в плохом случае. Это происходит на практике или на случайных данных. Поэтому, если вам нужен критичный ко времени код (поиск в базе данных, сетевой сервер и т. Д.), Вы используете деревья RB для поддержки упорядоченных или неупорядоченных списков / наборов.

Но RBTrees для новичков! Если вы занимаетесь искусственным интеллектом и вам нужно выполнить поиск, вы обнаружите, что много разветвляетесь. Вы можете использовать постоянный красно-черный, чтобы раскошелиться на новые состояния в O (log (n)). Постоянное красно-черное дерево сохраняет копию дерева до и после морфологической операции (вставка / удаление), но без копирования всего дерева (обычно операция O (log (n))). Я открыл источник устойчивого красно-черного дерева для Java

http://edinburghhacklab.com/2011/07/a-java-implementation-of-persistent-red-black-trees-open-sourced/

2 голосов
/ 18 сентября 2008

Лучшее описание красно-черных деревьев, которое я видел, приведено в «Введение в алгоритмы» Кормена, Лейзерсена и Ривеста. Я мог даже понять это достаточно, чтобы частично реализовать один (только вставка). Существует также довольно много апплетов, таких как This One на различных веб-страницах, которые анимируют процесс и позволяют просматривать графическое представление алгоритма построения древовидной структуры и переходить по нему.

2 голосов
/ 21 августа 2008

BST заставляют мир вращаться, как сказал Майкл. Если вы ищете хорошее дерево для реализации, взгляните на AVL trees (Wikipedia). У них есть условие балансировки, поэтому они гарантированно будут O (logn). Этот вид эффективности поиска делает логичным включение в любой процесс индексации. Единственной вещью, которая была бы более эффективной, была бы функция хеширования, но они становятся ужасно быстрыми, быстрыми и быстрыми. Кроме того, вы сталкиваетесь с парадоксом дня рождения (также известным как проблема с голубями).

Какой учебник вы используете? Мы использовали Структуры данных и анализ в Java Марка Аллена Вайса. Я на самом деле держу его открытым на коленях, когда я набираю это. В нем есть отличный раздел о красно-черных деревьях, и даже содержит код, необходимый для реализации всех деревьев, о которых он говорит.

1 голос
/ 04 сентября 2013

Здесь много и много тепла, но не так много света, поэтому давайте посмотрим, сможем ли мы его дать.

Сначала , дерево RB является ассоциативной структурой данных, в отличие, скажем, от массива, который не может взять ключ и вернуть ассоциированное значение, ну, если только это не целочисленный «ключ» в 0% разреженный индекс смежных целых чисел. Массив также не может увеличиваться в размере (да, я тоже знаю о realloc (), но под прикрытием, которое требует новый массив и затем memcpy ()), поэтому, если у вас есть какое-либо из этих требований, массив не подойдет , Эффективность памяти массива идеальна. Ноль отходов, но не очень умных или гибких - realloc () не выдерживает.

Секунда , в отличие от bsearch () для массива элементов, который является ассоциативной структурой данных, дерево RB может динамически увеличиваться (И уменьшаться) в своем размере. Bsearch () отлично работает для индексации структуры данных известного размера, который останется таким же. Поэтому, если вы заранее не знаете размер ваших данных, или необходимо добавить или удалить новые элементы, функция bsearch () будет недоступна. Bsearch () и qsort () хорошо поддерживаются в классическом C и имеют хорошую эффективность использования памяти, но недостаточно динамичны для многих приложений. Хотя они мои любимые, потому что они быстрые, простые и если вы не имеете дело с приложениями реального времени, довольно часто они достаточно гибки. Кроме того, в C / C ++ вы можете отсортировать массив указателей на записи данных, указав, например, на элемент struc {}, который вы хотите сравнить, а затем переставив указатель в массиве указателей так, чтобы указатели читались по порядку. в конце указателя сортировка возвращает ваши данные в отсортированном порядке. Использование этого с отображенными в память файлами данных чрезвычайно эффективно, быстро и довольно просто. Все, что вам нужно сделать, это добавить несколько "*" к вашим функциям сравнения.

Третий , в отличие от хеш-таблицы, которая также должна иметь фиксированный размер и не может быть увеличена после заполнения, дерево RB будет автоматически расти и балансироваться для поддержания своего O (log (n )) гарантия выполнения. Особенно, если ключ дерева RB является целым числом, он может быть быстрее, чем хеш, потому что, хотя сложность хеш-таблицы равна O (1), этот 1 может быть очень дорогим вычислением хеша. Множественное целочисленное одночасовое целое дерево часто сравнивает с вычислениями 100-часовых + хешей, не говоря уже о перефразировании, и malloc () пространстве для хэш-коллизий и перефразировок. Наконец, если вам нужен доступ ISAM, а также ключевой доступ к вашим данным, хеш исключается, так как отсутствует упорядочение данных, присущее хеш-таблице, в отличие от естественного упорядочения данных в любой реализации дерева. Классическое использование хеш-таблицы - обеспечение доступа к таблице зарезервированных слов для компилятора. Это эффективность памяти отлично.

Четвёртый , и очень низкий в любом списке, это связанный или двусвязный список, который, в отличие от массива, естественным образом поддерживает вставки и удаления элементов и, как следствие, изменение размера. Это самая медленная из всех структур данных, так как каждый элемент знает, как добраться до следующего элемента, поэтому вам нужно в среднем искать (element_knt / 2) ссылки, чтобы найти данные. Он в основном используется там, где вставки и удаления где-то в середине списка являются общими, и особенно, когда список является круглым и запитывает дорогостоящий процесс, что делает время для чтения ссылок относительно небольшим. Мой общий прием - использовать произвольно большой массив вместо связанного списка, если ваше единственное требование - увеличить его размер. Если вы исчерпали размер с массивом, вы можете realloc () больший массив. STL делает это для вас «под прикрытием», когда вы используете вектор. Грубо, но потенциально в тысячи раз быстрее, если вам не нужны вставки, удаления или поиск по ключу. Это плохая эффективность памяти, особенно для двусвязных списков. На самом деле, двусвязный список, требующий двух указателей, точно так же неэффективен в памяти, как красно-чёрное дерево, и при этом НИКОГО из его привлекательных быстрых упорядоченных характеристик поиска.

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

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

1 голос
/ 17 января 2013

Поскольку вы спрашиваете, какое дерево используют люди, вам нужно знать, что красное черное дерево по сути является 2-3-4 B-деревом (то есть B-деревом порядка 4). B-дерево не эквивалентно бинарному дереву (как задано в вашем вопросе).

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

Итак, вот что вы получаете, когда переводите правила красного черного дерева в терминах B-дерева (я использую формат правило красного черного дерева => B Tree эквивалент ):

1) Узел красного или черного цвета. => Узел в b-дереве может быть либо частью узла, либо как узел на новом уровне.

2) Корень черный. (Это правило иногда опускается, так как оно не влияет на анализ) => Корневой узел можно рассматривать как часть внутреннего корневого узла как дочерний элемент воображаемого родительского узла.

3) Все листья (NIL) черные. (Все листья одного цвета с корнем.) => Так как один из способов представления дерева RB - это опустить листья, мы можем исключить это.

4) Оба потомка каждого красного узла черные. => Дочерние элементы внутреннего узла в B-дереве всегда лежат на другом уровне.

5) Каждый простой путь от данного узла к любому из его дочерних листьев содержит одинаковое количество черных узлов. => B-дерево поддерживается сбалансированным, так как для этого требуется, чтобы все узлы листьев были на одной глубине (следовательно, высота узла B-дерева представлена ​​числом черных связей от корня до листа красного черного дерева )

Также есть более простая «нестандартная» реализация Роберта Седжвика здесь : (Он является автором книги Алгоритмы вместе с Уэйном)

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

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

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

В любом случае, AVL-дерево было первым алгоритмом сбалансированного бинарного дерева, и статья в Википедии о нем довольно ясна. Честно говоря, статья в Википедии о красно-черных деревьях ясна, как грязь.

Помимо двоичных деревьев, B-деревья - это деревья, в которых каждый узел может иметь много значений. B-Tree - это , а не бинарное дерево, просто его название. Они действительно полезны для эффективного использования памяти; каждый узел дерева может быть подобран по размеру, чтобы поместиться в один блок памяти, чтобы вы не (медленно) не находили тонны разных вещей в памяти, которая была выгружена на диск. Вот феноменальный пример B-Tree .

...