Я реализую красно-черное дерево. В данный момент застрял на севооборотах. Когда я поворачиваюсь и назначаю новых левых / правых детей, я падаю и сгораю. Способ, которым я научился делать левые или правые повороты в двоичном дереве, выглядит примерно так (в c ++):
void right_rotation(node *&root)
{
auto *temp = root->left;
root->left = temp->right;
temp->right = root;
root = temp;
}
Это прекрасно работает для дерева AVL.
Вот РБ-дерево. Я постараюсь опубликовать минимум, который требуется, чтобы воспроизвести это
#include <stdio.h>
struct foo
{
int key;
foo *parent;
foo *left;
foo *right;
int rb; // 0 black, 1 red
foo(int k, foo *p, foo *l, foo *r, int _rb) : key(k), parent(p), left(l), right(r), rb(_rb) {}
};
class rbtree
{
public:
foo *root{};
void insert(int key)
{
if (root != nullptr)
insert(root, root, key);
else
root = new foo(key, nullptr, nullptr, nullptr, 0);
}
void insert(foo *&node, foo *&parent, int key)
{
if (!node) {
node = new foo(key, parent, nullptr, nullptr, 1);
rebalance(node);
} else if (key <= node->key) {
insert(node->left, node, key);
} else {
insert(node->right, node, key);
}
}
void rebalance(foo *&node)
{
if (!node)
return;
if (root == node) {
root->rb = 0;
return;
}
if (node->rb == 1 && node->parent->rb == 1) {
auto *grand_p = node->parent->parent;
foo *aunt;
if (grand_p->left != node->parent)
aunt = grand_p->left;
else
aunt = grand_p->right;
if (!aunt || aunt->rb == 0)
rotate(node, grand_p);
else
color_flip(node);
}
// if there is no parent to the root
if (!node->parent)
root = node;
rebalance(node->parent);
}
void rotate(foo *&node, foo *&grand_parent)
{
if (grand_parent->right->left == node) {
right_left_rot(node);
} // else the rest is not critical
}
void right_rot(foo *&root)
{
auto *grand_p = root->parent;
auto *tmp = root->left;
if (!tmp->left)
printf("\nI am about to crash");
root->left = tmp->right; // segfault here
// the rest of the rotation logic
tmp->right = root;
root->parent = tmp;
if (root->left)
root->left->parent = root;
if (grand_p) {
if (root == grand_p->left)
grand_p->left = tmp;
else if (root == grand_p->right)
grand_p->right = tmp;
}
tmp->parent = grand_p;
}
void right_left_rot(foo *&node)
{
right_rot(node->parent);
// rest not important
}
void color_flip(foo *&node)
{
node->parent->parent->rb = 1;
node->parent->parent->left->rb = 0;
node->parent->parent->right->rb = 0;
if (root->rb != 0)
root->rb = 0;
}
};
int main()
{
rbtree rbt;
rbt.insert(3);
printf("\n%s%d", "Added successfully ", 3);
rbt.insert(1);
printf("\n%s%d", "Added successfully ", 1);
rbt.insert(5);
printf("\n%s%d", "Added successfully ", 5);
rbt.insert(7);
printf("\n%s%d", "Added successfully ", 7);
rbt.insert(6);
printf("\n%s%d", "Added successfully ", 6);
return 0;
}
Из того, что я знаю, tmp->left
- это nullptr
, поэтому, когда я назначаю его на root->left
, это нормально для сегфо. Как мне преодолеть это и оба выполнить этот шаг, а не завершить?
Я обыскал SO и другие интернет-уголки и увидел, что люди используют этот подход, и они не жалуются на этот segfault.
Если я проверю if (tmp->right) root->left = tmp->right;
, тогда код не выполняется, и я пропускаю критический шаг алгоритма. С помощью этого оператора if
я получаю дерево, в котором некоторые узлы указывают на себя, и оно становится действительно грязным.
Пример дела
Чтобы получить эту ситуацию, я вставляю 3 (корень) -> 1 (слева от 3) -> 5 (справа от 3) -> 7 (справа от 5) -> 6 (слева от 7) , Баланс должен быть сделан в 5-> 7-> 6. Логика заключается в том, чтобы сделать вращение вправо-влево. При правильном вращении эта ситуация происходит