Красная дерево – текстура, фото, цена на породы

Содержание

коротко и ясно / Habr

История из жизни. Девушка предложила своему парню-программисту пройти психологический тест:
Девушка: Нарисуй дерево.
Программист: (рисует бинарное дерево)
Девушка: Нет, другое.
Программист: Я и красно-черное дерево могу нарисовать.

Итак, сегодня хочу немного рассказать о красно-черных деревьях. Рассказ будет кратким, без рассмотрения алгоритмов балансировки при вставке/удалении элементов в красно-черных деревьях.

Красно-черные деревья относятся к сбалансированным бинарным деревьям поиска.

Как бинарное дерево, красно-черное обладает свойствами:


1) Оба поддерева являются бинарными деревьями поиска.

2) Для каждого узла с ключом выполняется критерий упорядочения:

ключи всех левых потомков <= < ключи всех правых потомков

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

Свойства красно-черных деревьев:


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

2) Корень окрашен в черный цвет.

3) Листья(так называемые NULL-узлы) окрашены в черный цвет.

4) Каждый красный узел должен иметь два черных дочерних узла. Нужно отметить, что у черного узла могут быть черные дочерние узлы. Красные узлы в качестве дочерних могут иметь только черные.

5) Пути от узла к его листьям должны содержать одинаковое количество черных узлов(это черная высота).

Ну и почему такое дерево является сбалансированным?


Действительно, красно-черные деревья не гарантируют строгой сбалансированности (разница высот двух поддеревьев любого узла не должна превышать 1), как в АВЛ-деревьях. Но соблюдение свойств красно-черного дерева позволяет обеспечить выполнение операций вставки, удаления и выборки за время . И сейчас посмотрим, действительно ли это так.

Пусть у нас есть красно-черное дерево. Черная высота равна (black height).

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

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

То есть, пути из корня к листьям могут различаться не более, чем вдвое (, где h — высота поддерева), этого достаточно, чтобы время выполнения операций в таком дереве было

Как производится вставка?


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

Свойство 1 не нарушается, поскольку новому узлу сразу присваивается красный цвет.

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

Свойство 3 также не нарушается, поскольку при добавлении узла он получает черные листовые NULL-узлы.

В основном встречаются 2 других нарушения:

1) Красный узел имеет красный дочерний узел (нарушено свойство ).

2) Пути в дереве содержат разное количество черных узлов (нарушено свойство ).

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

Это вообще где-то используется?


Да! Когда в институте на третьем курсе нам читали «Алгоритмы и структуры данных», я и не могла представить, что красно-черные деревья где-то используются. Помню, как мы не любили тему сбалансированных деревьев. Ох уж эти родственные связи в красно-черных деревьях («дядя», «дедушка», «чёрный брат и крестный красный отец»), прям Санта-Барбара какая-то. Правые и левые, малые и большие повороты АВЛ-деревьев – сплошные американские горки. Вы тоже не любите красно-черные деревья? Значит, просто не умеете их готовить. А кто-то просто взял и приготовил. Так, например, ассоциативные массивы в большинстве библиотек реализованы именно через красно-черные деревья.

Это все, что я хотела рассказать.

habr.com

какое дерево называют «красным деревом»????

Красное дерево — древесина некоторых видов деревьев, имеющая красные и коричневатые тона, обычно прочная и хорошо поддающаяся обработке. К красному дереву обычно относят древесину следующих деревьев: два вида рода Swietenia, получаемая из которых древесина называется махагони: Swietenia mahagoni — Махагониевое дерево, или Вест-Индское махагони, Swietenia macrophylla — Американское махагони. виды рода Цезальпиния (Caesalpinia L.) некоторые виды деревьев из рода Птерокарпус (Pterocarpus) служат источником древесины падук: Красный сандал (Pterocarpus santalinus L.f.) Малайский падук (Pterocarpus indicus Willd.). Древесина деревьев этих видов также иногда относят к «красному дереву» : Цедрела пахучая (Cedrela odorata L.) некоторые виды рода Дёрен: Дёрен белый (Cornus alba L.),

Красное дерево — древесина некоторых видов деревьев, имеющая красные и коричневатые тона, обычно прочная и хорошо поддающаяся обработке. К красному дереву обычно относят древесину следующих деревьев: два вида рода Swietenia, получаемая из которых древесина называется махагони: Swietenia mahagoni — Махагониевое дерево, или Вест-Индское махагони, Swietenia macrophylla — Американское махагони. виды рода Цезальпиния (Caesalpinia L.) некоторые виды деревьев из рода Птерокарпус (Pterocarpus) служат источником древесины падук: Красный сандал (Pterocarpus santalinus L.f.) Малайский падук (Pterocarpus indicus Willd.). Древесина деревьев этих видов также иногда относят к «красному дереву» : Цедрела пахучая (Cedrela odorata L.) некоторые виды рода Дёрен: Дёрен белый (Cornus alba L.), Дёрен сибирский (Cornus sibirica Lodd. ex G.Don), nom. nud. некоторые виды рода Тис: Тис ягодный (Taxus baccata L.), Тис остроконечный (Taxus cuspidata Siebold et Zucc.) вид рода Жостер: Жостер даурский (Rhamnus davurica Pall.)[источник не указан 171 день] вид рода Ольха: Ольха клейкая, или чёрная, или европейская (Alnus glutinosa (L.) Gaertn.) даёт ценную древесину единственный вид монотипного рода Секвойя (Sequoia Endl.)

махонь . естли не ошибаюсь то это вьетнамское дерево но могу ошибиться

Красное дерево — древесина некоторых видов деревьев, имеющая красные и коричневатые тона, обычно прочная и хорошо поддающаяся обработке. Гляньте в Википедии 🙂

Красное дерево — древесина некоторых видов деревьев, имеющая красные и коричневатые тона, обычно прочная и хорошо поддающаяся обработке.

У нас красным деревом называют тисс ягодный, так как его древесина и правда красноватого цвета. Но всё-таки мебель из красного дерева — это мебель из секвойи.

Красное дерево, окрашенная в красные и коричневатые тона древесина тропических деревьев. Очень прочное, тяжёлое, хорошо полируется. Окраска красного дерева обусловлена присутствием красителей, иногда экстрагируемых для изготовления красок. Для получения красного дерева чаще используются деревья из семейства мелиевых: американские и африканские махагони (см. Махагониевое дерево), а также саппановое дерево семейства цезальпиниевых из Юго-Вост. Азии (имеет запах фиалки) и др. Красное дерево употребляют в виде облицовочной фанеры в мебельной промышленности, для внутренней отделки пароходов, вагонов, квартир и т. д. Иногда красным деревом называют древесину тисса, чёрной ольхи и секвойи, имеющую красную окраску, но не обладающую др. качествами настоящего красного дерева. <a rel=»nofollow» href=»http://www.inter-meb.ru/info/vidy-derevev/krasnoe-derevo/» target=»_blank»>http://www.inter-meb.ru/info/vidy-derevev/krasnoe-derevo/</a>

touch.otvet.mail.ru

Цвет волос Красное дерево — как покрасить и 20 идей окрашивания

Красное дерево. Само слово звучит напыщенно, роскошно, экзотично. Этот богатый и яркий цвет, который является оттенком между красным и коричневым, один из самых популярных цветов волос во всем мире. И не просто так этот оттенок красивый и яркий, но не кричащий для привлечения внимания. Яркий, но сохраняющий свою элегантность. Цвет волос Красное дерево — это оттенок, созданный специально для тех, кто хочет экспериментировать с цветом волос, не потакая возмутительным тенденциям моды. Это для тех женщин, которые хотят обладать классическим образом. Но только потому, что Вы хотите цвет, который приближен к более естественному тону волос, не означает, что Вы не можете экспериментировать с тем, как его оформлять. Итак, здесь мы составили список наших 20 лучших идей для окрашивания волос цветом красного дерева.

Но прежде чем мы перейдем к списку, давайте посмотрим, как Вы можете окрасить волосы в этот прекрасный оттенок прямо в домашних условиях!

Как покрасить волосы в цвет красного дерева

Что вам нужно:

  • Краска цвета красного дерева;
  • Старая футболка или старое полотенце;
  • Щетка для волос;
  • Секционные зажимы;
  • Вазелин;
  • Миска;
  • Резиновые перчатки;
  • Бутылка или кисть аппликатора;
  • Шампунь;
  • Кондиционер.

Как покрасить волосы:

  1. Наденьте старую футболку или оберните старое полотенце вокруг плеч, чтобы не жалко было испачкать.
  2. Расчешите все волосы, чтобы не было спутанных.
  3. Разделите все свои волосы на 4 части: сначала горизонтально, затем вертикально.
  4. Закрепите 3 части ваших волос, оставив ту, которую вы хотите начать красить первой.
  5. Нанесите вазелин по всей линии волос и на уши, чтобы не запачкать кожу.
  6. Наденьте резиновые перчатки.
  7. Следуйте инструкциям на упаковке и смешайте краску для волос в чаше.
  8. Взяв сантиметровую прядь волос, начните наносить краску с помощью либо красильной кисти, либо аппликаторной бутылки, отступая примерно на 2 см от корней.
  9. Распределите краску пальцами по всей длине ваших волос.
  10. Повторяйте этот процесс, пока не нанесете краску на все 4 части ваших волос.
  11. Оставьте краску на то время, которое указано на упаковке.
  12. Смойте краску теплой водой.
  13. Подождите около часа прежде, чем мыть голову и кондиционировать волосы.

Ну теперь, когда Вы знаете, как красить волосы в цвет красного дерева, давайте пройдёмся по всем способам, которыми Вы можете оформить этот великолепный цвет волос!

20 великолепных идей для волос цвета красного дерева

  1. Красное дерево мечты. Я поняла, что окрашивание волос может показаться вам чрезвычайно сложной задачей. Так почему бы Вам не начать с более естественного цвета, не так ли? Вы могли бы сохранить естественный темно-коричневый цвет своих корней и сделать нежный переход к омбре цвета красного дерева, чтобы создать абсолютно сказочный вид волос.
  2. Красное дерево в огне. Когда дело доходит до волос (и сердца), иногда лучше всего пойти ва-банк. Хороший способ сделать это – сделать цвет красного дерева на ваших волосах. В то время как глубокий оттенок красного дерева действует как основа, яркие полосы меди подчёркивают его, чтобы создать огненный эффект.
  3. Виноградное красное дерево. Есть что-нибудь лучше, чем отдыхать и расслабляться с бокалом красного вина в конце длинного дня? Думаю, нет. Так проявите некоторую любовь к своему любимому занятию в конце дня, попробовав этот образ, который является потрясающим переплетением красного дерева и фиолетового цветов.
  4. Красное дерево в закате. Разве Вы не восхищаетесь красивыми цветами, которые освещают небо на закате? Окрасьте свои волосы в этих самых цветах, решаясь на этот прекрасный образ для ваших волос. Великолепный оттенок красного дерева был перехвачен пыльно-розовым посередине, чтобы подражать заходу солнца в темноту сумерек.
  5. Пыльно-розовое красное дерево. Хотите выглядеть как богемная принцесса пустыни, танцующая от всей души на Коачелле? Тогда Вам нужно попробовать это великолепное окрашивание. Красивые волосы цвета красного дерева здесь выделены пыльно-коричневым оттенком и оформлены в текстурированные волны, чтобы создать мягкий и романтичный образ.
  6. Персики и красное дерево. Если в этом сезоне есть один цвет, определяющий тенденции в цвете волос, то это должен быть персик. А почему бы и нет? Этот приглушённый оттенок оранжевого прекрасно сочетается со всеми оттенками кожи. Этот образ с омбре, сделанный с цветом красного дерева сверху и персиковым на кончиках, является ошеломляющей смесью цветов.
  7. Розово-золотое красное дерево. Давайте будем реалистами. Мы все знаем, что металлический оттенок покоряет мир моды волос прямо сейчас. Розовое золото, конечно! Этот образ, в основе которого цвет красного дерева, подсвечен золотыми прядями на кончиках, чтобы создать красивое вплетение древесных и металлических оттенков.
  8. Шоколадно-красное дерево в вихре. Что Вы получаете, когда вместе взбалтываете два насыщенных оттенка коричневого? Красоту. И это именно то, что Вы получаете, когда у Вас есть тёмный шоколадный оттенок в качестве основы, подчеркнутый едва уловимым оттенком красного дерева, чтобы создать этот изысканный и элегантный вид.
  9. Водопад «красное дерево». Кто не хочет выглядеть мистическим существом? Лесная нимфа, может быть? Так Вы можете воплотить эту мечту, сделав этот великолепный образ. Насыщенный и однотонный цвет красного дерева создает эффект не меньше, чем красно-коричневый водопад, спускающийся каскадом вниз по вашей спине.
  10. Роща красного дерева. Есть причина, по которой «рисование на волосах» является самой популярной техникой окрашивания прямо сейчас (и, вероятно, будет надолго). Это потому, что оно создает самую потрясающую объёмность и движение Ваших волос. Например, этот образ с цветом красного дерева воссоздает насыщенные темно-коричневые оттенки, расположенные глубоко в сердце леса.
  11. Изящное красное дерево. Тонкость — это искусство. И это еще более важно при создании потрясающей причёски. Это добавление красного дерева настолько красиво вписывается в темно-коричневый базовый цвет, что создаваемый им образ очень естественен. Едва уловимый эффект в этом облике делает его великолепным.
  12. Отвар из красного дерева. Вы когда-нибудь замечали потрясающие оттенки оранжевого и коричневого, которые появляется в вашей чашке, как только ваш чайный пакетик попадает в горячую воду? Потому что это стало вдохновением для данного образа. Этот стиль, основанный на цвете красного дерева, был выделен с использованием красных и оранжевых оттенков для создания этого необычного результата.
  13. Блеск красного дерева. Итак, Вы не хотите красить все свои волосы? Это нормально, потому что у меня есть легкое решение для Вас. Просто добавьте блеск фиалки и красного дерева поверх естественных темно-коричневых волос. Когда солнечный свет попадает на Ваши волосы, он врывается в симфонию насыщенных цветов.
  14. Брюнетка в цвете красного дерева. Хотите выглядеть полностью утонченной и стильной с вашей следующей прической? Можете на меня рассчитывать! Этот образ с цветом красного дерева был слегка подсвечен оттенками красно-имбирного и коричневого, чтобы создать элегантный внешний вид, который по-прежнему веет молодостью.
  15. Огненно-красное дерево. Девушки, только потому, что Вы хотите получить цвет красного дерева на волосах, вовсе не означает, что Вам нужно попрощаться с мечтой о ярких волосах. Вот суперкрасивый вид, который Вы можете попробовать. Вы могли бы сделать тонирование цветом красного дерева на темно-коричневой основе, чтобы создать этот великолепно огненный вид.
  16. Панцирь черепахи цвета красного дерева. Мы все знаем, что панцирь черепахи — это тенденция, когда дело касается солнечных очков. Но знаете ли Вы, что этот великолепный цветовой эффект перешел в мир окраски волос? Да, вы меня правильно поняли! Попробуйте этот красивый эффект черепахи, созданный с оттенками красного дерева и карамельным блондом.
  17. Триколор из красного дерева. Вы знаете, Вам не всегда нужно придерживаться одного или двух цветов, когда вы собираетесь сделать приглушённый цвет на волосах. Вы могли бы взять и третий оттенок, что совсем не будет лишним. Данный триколор из красных волос является свидетельством именно этого. С ярким оттенком красного дерева на корнях и смесью светло- и темно-коричневого цветов на кончиках, это ошеломляющий образ на века.
  18. Ультрафиолетовое красное дерево. Смешение разных вещей и эксперименты с различными оттенками —  подтекст для образа Ваших волос с цветом красного дерева. Вы могли бы, например, попробовать красные и фиолетовые оттенки, чтобы создать немного вибраций в вашем темном и приглушенном базовом цвете красного дерева. Это добавляет теплоту и размерность всему облику.
  19. Буря в пустыне и красное дерево. Посмотрите фотографии пустынных бурь, и вы поймете, что я имею в виду, когда говорю, что это впечатляющее зрелище. Этот великолепный светло-коричневый с нотками красного дерева образ был вдохновлен именно этим природным явлением. Пыльные коричневые блики контрастируют с базой красного дерева, чтобы создать этот потрясающий облик.
  20. Прохладное красное дерево. Когда дело доходит до окраски волос в оттенки коричневого, Вы не всегда должны использовать теплые тона. У этого прохладного тонированного образа с цветом красного дерева есть немного синих оттенков. С гладко выпрямленными волосами красное дерево находится на пике своего лоска и блеска.

bezumo.ru

📌 Красно-чёрное дерево — это… 🎓 Что такое Красно-чёрное дерево?

Красно-чёрное дерево
Типдерево поиска
Изобретено в1972 году
ИзобретеноРудольф Байер
Временная сложность
в О-символике
В среднемВ худшем случае
Расход памятиO(n)O(n)
ПоискO(log n)O(log n)
ВставкаO(log n)O(log n)
УдалениеO(log n)O(log n)

Красно-чёрное дерево (англ. Red-Black-Tree, RB-Tree) — это одно из самобалансирующихся двоичных деревьев поиска, гарантирующих логарифмический рост высоты дерева от числа узлов и быстро выполняющее основные операции дерева поиска: добавление, удаление и поиск узла. Сбалансированность достигается за счёт введения дополнительного атрибута узла дерева — «цвета». Этот атрибут может принимать одно из двух возможных значений — «чёрный» или «красный».

Изобретателем красно-чёрного дерева считают немца Рудольфа Байера. Название «красно-чёрное дерево» структура данных получила в статье Л. Гимпаса и Р. Седжвика (1978). В журнале были доступны две краски (красная и чёрная)[источник не указан 551 день] и дополнительный бит, «прикреплявшийся» к каждому из узлов, обозначался цветом.

Терминология

Красно-чёрное дерево является особым видом двоичного дерева, используемым в компьютерной науке для организации сравнимых данных, таких как фрагменты текста или числа. Листовые узлы красно-черных деревьев не содержат данных. Такие листья не нуждаются в явном выделении памяти — нулевой указатель на потомка может фактически означать, что этот потомок — листовой узел, но в некоторых случаях работы с красно-черными деревьями использование явных листовых узлов может послужить упрощением алгоритма.

Свойства

Пример красно-чёрного дерева

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

  1. Узел либо красный, либо чёрный.
  2. Корень — чёрный. (В других определениях это правило иногда опускается. Это правило слабо влияет на анализ, так как корень всегда может быть изменен с красного на чёрный, но не обязательно наоборот).
  3. Все листья(NIL) — черные.
  4. Оба потомка каждого красного узла — черные.
  5. Всякий простой путь от данного узла до любого листового узла, являющегося его потомком, содержит одинаковое число черных узлов.

Эти ограничения реализуют критическое свойство красно-черных деревьев: путь от корня до самого дальнего листа не более чем в два раза длиннее пути от корня до ближайшего листа (если дальний лист расположен на 3-м уровне). Результатом является то, что дерево примерно сбалансировано. Так как такие операции как вставка, удаление и поиск значений требуют в худшем случае времени, пропорционального длине дерева, эта теоретическая верхняя граница высоты позволяет красно-чёрным деревьям быть более эффективными в худшем случае, чем обычные двоичные деревья поиска.

Чтобы понять, почему это гарантируется, достаточно рассмотреть эффект свойств 4 и 5 вместе. Пусть для красно-чёрного дерева T число черных узлов в свойстве 5 равно B. Тогда кратчайший возможный путь от корня дерева T до любого листового узла содержит B черных узлов. Более длинный возможный путь может быть построен путем включения красных узлов. Однако, свойство 4 не позволяет вставить несколько красных узлов подряд. Поэтому самый длинный возможный путь состоит из 2B узлов, попеременно красных и черных. Любой максимальный путь имеет одинаковое число черных узлов (по свойству 5), следовательно, не существует пути, более чем вдвое длинного, чем любой другой путь.

Во многих реализациях структуры дерева возможно, чтобы узел имел только одного потомка и листовой узел содержал данные. В этих предположениях реализовать красно-чёрное дерево возможно, но изменятся несколько свойств и алгоритм усложнится. По этой причине данная статья использует «фиктивные листовые узлы», которые не содержат данных и просто служат для указания, где дерево заканчивается. Эти узлы часто опускаются при графическом изображении, в результате дерево выглядит противоречиво с вышеизложенными принципами, но на самом деле противоречия нет. Следствием этого является то, что все внутренние (не являющиеся листовыми) узлы имеют два потомка, хотя один из них может быть нулевым листом. Свойство 5 гарантирует, что красный узел обязан иметь в качестве потомков либо два черных нулевых листа, либо два черных внутренних узла. Для чёрного узла с одним потомком нулевым листовым узлом и другим потомком, не являющимся таковым, свойства 3, 4 и 5 гарантируют, что последний должен быть красным узлом с двумя черными нулевыми листьями в качестве потомков.

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

Аналогия с B-деревом порядка 4

То же самое красно-чёрное дерево, что и в примере выше, представленное как B-дерево.

Красно-чёрное дерево схоже по структуре с B-деревом порядка 4, в котором каждый узел может содержать от 1 до 3 значений и, соответственно, от 2 до 4 указателей на потомков. В таком В-дереве каждый узел будет содержать только одно значение, соответствующее значению чёрного узла красно-чёрного дерева с необязательным значениями до и/или после него в том же узле, оба из которых соответствуют эквивалентным красным узлам красно-чёрного дерева.

Один из способов увидеть эту эквивалентность — «поднять» красные узлы в графическом представлении красно-чёрного дерева так, чтобы они оказались на одном уровне по горизонтали со своими предками черными узлами, образуя страницу. В В-дереве, или в модифицированном графическом представлении красно-чёрного дерева, у всех листовых узлов глубина одинаковая.

Этот тип В-дерева является более общим, чем красно-чёрное дерево, хотя, как видно, из одного такого В-дерева порядка 4 могут быть получены несколько красно-черных деревьев. Если страница В-дерева содержит только одно значение, данный узел чёрный и имеет двух потомков. Если страница содержит три значения, то центральный узел является чёрным, а каждый его сосед — красным. Однако, если страница содержит два значения, любой узел может стать чёрным в красно-чёрном дереве (и тогда второй будет красным).

Работа с красно-чёрными деревьями

Красно-чёрные деревья являются одними из наиболее активно используемых на практике самобалансирующихся деревьев поиска. В частности, контейнеры set и map в большинстве реализаций библиотеки STL языка C++[1], класс TreeMap языка Java[2], так же, как и многие другие реализации ассоциативного массива в различных библиотеках, основаны на красно-чёрных деревьях.

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

Операции

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

Вставка

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

Что происходит дальше зависит от цвета близлежащих узлов. Термин дядя будем использовать для обозначения брата родительского узла, как и в фамильном дереве. Заметим, что:

  • Свойство 3 (Все листья черные) выполняется всегда.
  • Свойство 4 (Оба потомка любого красного узла — черные) может нарушиться только при добавлении красного узла, при перекрашивании чёрного узла в красный или при повороте.
  • Свойство 5 (Все пути от любого узла до листовых узлов содержат одинаковое число черных узлов) может нарушиться только при добавлении чёрного узла, перекрашивании красного узла в чёрный (или наоборот), или при повороте.
Примечание: Буквой N будем обозначать текущий узел (окрашенный красным). Сначала это новый узел, который вставляется, но эта процедура может рекурсивно применена к другим узлам (смотрите случай 3). P будем обозначать предка N, через G обозначим дедушку N, а U будем обозначать дядю N. Отметим, что в некоторых случаях роли узлов могут меняться, но, в любом случае, каждое обозначение будет представлять тот же узел, что и в начале. Любой цвет, изображенный на рисунке, либо предполагается в данном случае, либо получается из других соображений.

Каждый случай рассматривается с примерами кода на языке C. Дядя и дедушка текущего узла могут быть найдены с помощью функций:

struct node *
grandparent(struct node *n)
{
        if ((n != NULL) && (n->parent != NULL))
                return n->parent->parent;
        else
                return NULL;
}
 
struct node *
uncle(struct node *n)
{
        struct node *g = grandparent(n);
        if (g == NULL)
                return NULL; // No grandparent means no uncle
        if (n->parent == g->left)
                return g->right;
        else
                return g->left;
}

Случай 1: Текущий узел N в корне дерева. В этом случае, он перекрашивается в чёрный цвет, чтобы оставить верным Свойство 2 (Корень — чёрный). Так как это действие добавляет один чёрный узел в каждый путь, Свойство 5 (Все пути от любого данного узла до листовых узлов содержат одинаковое число черных узлов) не нарушается.

void
insert_case1(struct node *n)
{
        if (n->parent == NULL)
                n->color = BLACK;
        else
                insert_case2(n);
}

Случай 2: Предок P текущего узла чёрный, то есть Свойство 4 (Оба потомка каждого красного узла — черные) не нарушается. В этом случае дерево действительно. Свойство 5 (Все пути от любого данного узла до листовых узлов содержат одинаковое число черных узлов) не нарушается, потому что текущий узел N имеет двух черных листовых потомков, но так как N является красным, пути до каждого из этих потомков содержит такое же число черных узлов, что и путь до чёрного листа, который был заменен текущим узлом, который был чёрный, так что свойство остается верным.

void
insert_case2(struct node *n)
{
        if (n->parent->color == BLACK)
                return; /* Tree is still valid */
        else
                insert_case3(n);
}
Примечание: В следующих случаях предполагается, что у N есть дедушка G, так как его родитель P является красным, а если бы он был корнем, то был бы окрашен в черный цвет. Таким образом, N также имеет дядю U, хотя он может быть листовым узлом в случаях 4 и 5.

Случай 3: Если и родитель P и дядя U — красные, то они оба могут быть перекрашены в чёрный и дедушка G станет красным (для сохранения свойства 5 (Все пути от любого данного узла до листовых узлов содержат одинаковое число черных узлов)). Теперь у текущего красного узла N чёрный родитель. Так как любой путь через родителя или дядю должен проходить через дедушку, число черных узлов в этих путях не изменится. Однако, дедушка G теперь может нарушить свойства 2 (Корень — чёрный) или 4 (Оба потомка каждого красного узла — черные) (свойство 4 может быть нарушено, так как родитель G может быть красным). Чтобы это исправить, вся процедура рекурсивно выполняется на G из случая 1.

void
insert_case3(struct node *n)
{
        struct node *u = uncle(n), *g;
 
        if ((u != NULL) && (u->color == RED) && (n->parent->color == RED)) {
                n->parent->color = BLACK;
                u->color = BLACK;
                g = grandparent(n);
                g->color = RED;
                insert_case1(g);
        } else {
                insert_case4(n);
        }
}
Примечание: В оставшихся случаях предполагается, что родитель P является левым потомком своего предка. Если это не так, необходимо поменять лево и право. Примеры кода позаботятся об этом.

Случай 4: Родитель P является красным, но дядя U — чёрный. Также, текущий узел N — правый потомок P, а P в свою очередь — левый потомок своего предка G. В этом случае может быть произведен поворот дерева, который меняет роли текущего узла N и его предка P. Тогда, бывший родительский узел P рассматривается, используя случай 5 (перенумеровывающий N и P), потому что Свойство 4 (Оба потомка любого красного узла — черные) все ещё нарушено. Вращение приводит к тому, что некоторые пути (в поддереве, обозначенном «1» на схеме) проходят через узел N, чего не было до этого. Это также приводит к тому, что некоторые пути (в поддереве, обозначенном «3») не проходят через узел P. Однако, оба из этих узлов являются красными, так что Свойство 5 (Все пути от любого данного узла до листовых узлов содержат одинаковое число черных узлов) не нарушается при вращении.

void
insert_case4(struct node *n)
{
        struct node *g = grandparent(n);
 
        if ((n == n->parent->right) && (n->parent == g->left)) {
                rotate_left(n->parent);
                n = n->left;
        } else if ((n == n->parent->left) && (n->parent == g->right)) {
                rotate_right(n->parent);
                n = n->right;
        }
        insert_case5(n);
}

Случай 5: Родитель P является красным, но дядя U — чёрный, текущий узел N — левый потомок P и P — левый потомок G. В этом случае выполняется поворот дерева на G. В результате получается дерево, в котором бывший родитель P теперь является родителем и текущего узла N и бывшего дедушки G. Известно, что G — чёрный, так как его бывший потомок P не мог бы в противном случае быть красным (без нарушения Свойства 4). Тогда цвета P и G меняются и в результате дерево удовлетворяет Свойству 4 (Оба потомка любого красного узла — черные). Свойство 5 (Все пути от любого данного узла до листовых узлов содержат одинаковое число черных узлов) также остается верным, так как все пути, которые проходят через любой из этих трех узлов, ранее проходили через G, поэтому теперь они все проходят через P. В каждом случае, из этих трех узлов только один окрашен в чёрный.

void
insert_case5(struct node *n)
{
        struct node *g = grandparent(n);
 
        n->parent->color = BLACK;
        g->color = RED;
        if ((n == n->parent->left) && (n->parent == g->left)) {
                rotate_right(g);
        } else { /* (n == n->parent->right) and (n->parent == g->right) */
                rotate_left(g);
        }
}

Удаление

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

Будем использовать обозначение M для удаляемого узла; через C обозначим потомка M, который также будем называть просто «его потомок». Если M имеет не листового потомка, возьмем его за C. В противном случае за C возьмем любой из листовых потомков.

Если M является красным узлом, заменим его своим потомком C, который по определению должен быть чёрным. (Это может произойти только тогда, когда M имеет двух листовых потомков, потому что если красный узел M имеет чёрного не листового потомка с одной стороны, а с другой стороны — листового, то число черных узлов на обеих сторонах будет различным, таким образом дерево станет недействительным красно-чёрным деревом из-за нарушения Свойства 5.) Все пути через удаляемый узел просто будут содержать на один красный узел меньше, предок и потомок удаляемого узла должны быть черными, так что Свойство 3 («Все листья — черные») и Свойство 4 («Оба потомка красного узла — черные») все ещё сохраняется.

Другим простым является случай, когда M — чёрный и C — красный. Простое удаление чёрного узла нарушит Свойство 4 («Оба потомка красного узла — черные») и Свойство 5 («Всякий простой путь от данного узла до любого листового узла, содержит одинаковое число черных узлов»), но если мы перекрасим С в чёрный, оба эти свойства сохранятся.

Сложным является случай, когда и M и C — черные. (Это может произойти только тогда, когда удаляется чёрный узел, который имеет два листовых потомка, потому что если чёрный узел M имеет чёрного не листового потомка с одной стороны, а с другой — листового, то число черных узлов на обеих сторонах будет различным и дерево станет недействительным красно-чёрным деревом из-за нарушения Свойства 5.) Мы начнем с замены узла M своим потомком C. Будем называть этот потомок (в своем новом положении) N, а его «брата» (другого потомка его нового предка) — S. (До этого S был «братом» M.) На рисунках ниже мы также будем использовать обозначение P для нового предка N (старого предка M), SL для левого потомка S и SR для правого потомка S (S не может быть листовым узлом, так как если N по нашему предположению является чёрным, то поддерево P, которое содержит N, чёрной высоты два и поэтому другое поддерево P, которое содержит S должно быть также чёрной высоты два, что не может быть в случае, когда S — лист).

Примечание: В некоторых случаях мы меняем роли и обозначения узлов, но в каждом случае любое обозначение продолжает означать тот же узел, что и в начале случая. Любые цвета, изображенные на рисунке либо предполагаются случаем, либо получается из других предположений. Белый означает неизвестный цвет (либо красный, либо черный).

Будем искать «брата», используя эту функцию:

struct node *
sibling(struct node *n)
{
        if (n == n->parent->left)
                return n->parent->right;
        else
                return n->parent->left;
}
Примечание: Для того, чтобы дерево оставалось верно определенным, нам нужно, чтобы каждый лист оставался листом после всех преобразований (чтобы у него не было потомков). Если удаляемый нами узел — не листовой потомок N, легко видеть, что свойство выполняется. С другой стороны, если N — лист, то, как можно увидеть из рисунков или кода, свойство также выполняется.

Мы можем выполнить действия, описанные выше, используя следующий код, где функция replace_node ставит child на место узла n в дереве. Для удобства, код в этом разделе предполагает, что нулевые листья представлены реальными объектами узла, а не NULL (код вставки должен работать с таким же представлением).

void
delete_one_child(struct node *n)
{
        /*
         * Условие: n имеет не более одного ненулевого потомка.
         */
        struct node *child = is_leaf(n->right) ? n->left : n->right;
 
        replace_node(n, child);
        if (n->color == BLACK) {
                if (child->color == RED)
                        child->color = BLACK;
                else
                        delete_case1(child);
        }
        free(n);
}
Примечание: Если N является нулевым листом и мы не хотим представлять нулевые листы как реальные объекты, мы можем изменить алгоритм сначала вызывая delete_case1() на его отца (узел, который мы удалили, n в коде выше) и удаляя его после этого. Мы можем сделать это потому, что отец черный, и поэтому ведет себя так же как нулевой лист (и иногда называется ‘phantom’ лист). Мы можем безопасно удалить его так как n останется листом после всех операций, как показано выше.

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


Случай 1: N — новый корень. В этом случае, все сделано. Мы удалили один чёрный узел из каждого пути и новый корень является чёрным узлом, так что свойства сохранены.

void
delete_case1(struct node *n)
{
        if (n->parent != NULL)
                delete_case2(n);
}
Примечание: В случаях 2, 5, и 6 мы предполагаем, что N является левым потомком своего предка P. Если он — правый потомок, left и right нужно поменять местами во всех трех случаях. Опять-таки, примеры кода принимают это во внимание.

Случай 2: S — красный. В этом случае мы меняем цвета P и S, и затем делаем вращение влево вокруг P, ставя S дедушкой N. Нужно заметить, что P должен быть чёрным, если он имеет красного потомка. Хотя все пути по прежнему содержат одинаковое количество черных узлов, сейчас N имеет чёрного брата и красного отца. Таким образом, мы можем перейти к шагу 4, 5 или 6. (Его новый брат является чёрным потому, что он был потомком красного S.) Далее через S будет обозначен новый брат N.

void delete_case2(struct node *n)
{
        struct node *s = sibling(n);
 
        if (s->color == RED) {
                n->parent->color = RED;
                s->color = BLACK;
                if (n == n->parent->left)
                        rotate_left(n->parent);
                else
                        rotate_right(n->parent);
        } 
        delete_case3(n);
}

Случай 3: P, S, и дети S’ — черные. В этом случае мы просто перекрашиваем S в красный. В результате все пути, проходящие через S, но не проходящие через N, имеют на один чёрный узел меньше. Так как удаления отца N приводит к тому, что все пути, проходящие через N, содержат на один чёрный узел меньше, то такие действия выравнивают баланс. Тем не менее, все проходящие через P пути теперь содержать на один чёрный узел меньше, чем пути, которые через P не проходят, поэтому свойство 5 (все пути из любой вершины к её листовым узлам содержат одинаковое количество черных узлов) все ещё нарушено. Чтобы это исправить, мы применяем процедуру перебалансировки к P, начиная со случая 1.

void delete_case3(struct node *n)
{
        struct node *s = sibling(n);
 
        if ((n->parent->color == BLACK) &&
            (s->color == BLACK) &&
            (s->left->color == BLACK) &&
            (s->right->color == BLACK)) {
                s->color = RED;
                delete_case1(n->parent);
        } else
                delete_case4(n);
}

Случай 4: S и его дети — черные, но P — красный. В этом случае мы просто меняем цвета S и P. Это не влияет на количество черных узлов на путях, проходящих через S, но добавит один к числу черных узлов на путях, проходящих через N, восстанавливая тем самым влиянние удаленного чёрного узла.

void delete_case4(struct node *n)
{
        struct node *s = sibling(n);
 
        if ((n->parent->color == RED) &&
            (s->color == BLACK) &&
            (s->left->color == BLACK) &&
            (s->right->color == BLACK)) {
                s->color = RED;
                n->parent->color = BLACK;
        } else
                delete_case5(n);
}

Случай 5: S — чёрный, левый потомок S — красный, правый потомок S — чёрный, и N является левым потомков своего отца. В этом случае мы вращаем дерево вправо вокруг S. Таким образом левый потомок S становится его отцом и новым братом N. После этого мы меняем цвета у S и его нового отца. Все пути по прежнему содержат одинаковое количество черных узлов, но теперь у N есть чёрный брат с красным правым потомком, и мы переходим к случаю 6. Ни N, ни его отец не влияют на эту трансформацию. (Для случая 6 мы обозначим через S нового брата N.)

void delete_case5(struct node *n)
{
        struct node *s = sibling(n);
 
        if  (s->color == BLACK) { /* this if statement is trivial, 
due to case 2 (even though case 2 changed the sibling to a sibling's child, 
the sibling's child can't be red, since no red parent can have a red child). */
/* the following statements just force the red to be on the left of the left of the parent, 
   or right of the right, so case six will rotate correctly. */
                if ((n == n->parent->left) &&
                    (s->right->color == BLACK) &&
                    (s->left->color == RED)) { /* this last test is trivial too due to cases 2-4. */
                        s->color = RED;
                        s->left->color = BLACK;
                        rotate_right(s);
                } else if ((n == n->parent->right) &&
                           (s->left->color == BLACK) &&
                           (s->right->color == RED)) {/* this last test is trivial too due to cases 2-4. */
                        s->color = RED;
                        s->right->color = BLACK;
                        rotate_left(s);
                }
        }
        delete_case6(n);
}

Случай 6: S — чёрный, правый потомок S — красный, и N является левым потомком своего отца P. В этом случае мы вращаем дерево влево вокруг P, после чего S становится отцом P и своего правого потомка. Далее мы изменяем цвета у P и S, и делаем правого потомка S чёрным. Поддерево по прежнему имеет тот же цвет корня, поэтому свойства 4 (Оба потомка каждого красного узла — черные) и 5 (все пути из любой вершины к её листовым узлам содержат одинаковое количество черных узлов) не нарушаются. Тем не менее, у N теперь появился дополнительный чёрный предок: либо P стал чёрным, или он был чёрным и S был добавлен в качестве чёрного дедушки. Таким образом, проходящие через N пути проходят через один ополнительный чёрный узел.

Между тем, если путь не проходит через N, то есть 2 возможных варианта:

  • Он проходит через нового брата N. Тогда, он должен проходить через S и P, которые просто поменяли цвета и места. Поэтому путь содержит то же количество черных узлов.
  • Он проходит через нового дядю N, правого потомка S. Когда-то он проходил через S, отца S и правого потомка S (который был красным), но теперь он проходит только через S, который принял на себя цвет своего пержнего родителя, и правого потомка S, который был перекрашен из красного в чёрный (Предполагаем, что цвет S: чёрный). Net эффект заключается в том, что этот путь проходит через такое же количество черных узлов.

В любом случае, число черных узлов на этих путях не изменится. Поэтому, мы восстановили свойства 4 (Оба потомка каждого красного узла — черные) и 5 (все пути из любой вершины к её листовым узлам содержат одинаковое количество черных узлов). Белый узел на диаграмме может быть как красным так и чёрным, но должен указывать на тот же цвет как в начале, так и в конце трансформации.

void delete_case6(struct node *n)
{
        struct node *s = sibling(n);
 
        s->color = n->parent->color;
        n->parent->color = BLACK;
 
        if (n == n->parent->left) {
                s->right->color = BLACK;
                rotate_left(n->parent);
        } else {
                s->left->color = BLACK;
                rotate_right(n->parent);
        }
}

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

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

Сравнение со сбалансированным АВЛ-деревом

Высота дерева

Пускай высота дерева h, минимальное количество листьев N. Тогда:

Следовательно, при том же количестве листьев красно-чёрное дерево может быть выше АВЛ-дерева, но не более чем в раз.[3]

Поиск

Поскольку красно-чёрное дерево, в худшем случае, выше, поиск в нём медленнее, но проигрыш по времени не превышает 39 %.

Вставка

Вставка требует до 2 поворотов в обоих видах деревьев. Однако из-за большей высоты красно-чёрного дерева вставка может занимать больше времени.

Удаление

Удаление из красно-чёрного дерева требует до 3 поворотов, в АВЛ-дереве оно может потребовать числа поворотов до глубины дерева (до корня). Поэтому удаление из красно-чёрного дерева быстрее, чем из АВЛ-дерева.

Память

АВЛ-дерево в каждом узле хранит разницу высот (целое число от -1 до +1, для кодирования нужно 2бита). Красно-чёрное дерево в каждом узле хранит цвет (1 бит). Таким образом, красно-чёрное дерево может быть экономичнее.

Однако, на практике в обоих типах деревьев используются целые числа, т.к. работа с битами требует дополнительных процессорных вычислений. Но тем не менее есть реализации красно-чёрного дерева, которые хранят значение цвета в бите. Пример — Boost Multiindex. Цель хранения цвета в бите — уменьшение потребления памяти красно-чёрным деревом (Ordered indices node compression). Бит цвета в такой реализации хранится не в отдельной переменной, а в одном из указателей узла дерева.

Доказательство асимптотических границ

Красно-чёрное дерево, которое содержит n внутренних узлов, имеет высоту .

Обозначения:

Лемма: Поддерево с корнем в узле имеет не менее внутренних узлов.

Доказательство леммы (индукцией по высоте):

Основание индукции: .

Если поддерево имеет нулевую высоту, то должен быть null, поэтому .

Итак:


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

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

внутренних узлов.

Используя эту лемму, мы можем показать, что дерево имеет логарифмическую высоту. Так как по крайней мере половина узлов в любом пути от корня до листа — черные (свойство 4 красно-чёрного дерева), чёрная высота корня не менее . По лемме имеем:

Поэтому высота корня .

См. также

Ссылки

Литература

  • Кормен Т., Лейзерсон Ч., Ривест Р., Штайн К. Алгоритмы: построение и анализ = Introduction to algorithms. — 2-е изд. — М.: Издательский дом «Вильямс», 2011. — С. 336-364. — ISBN 978-5-8459-0857-5

Источники

  1. Dr Dobbs — STL’s Red-Black Trees
  2. Класс TreeMap
  3. в пределе для большого числа листьев

dic.academic.ru

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *