Шаг 165.
Основы языка Haskell. Рекурсивные типы данных. Красно-чёрные деревья, AA-деревья. Реализация красно-чёрных деревьев на языке C
На этом шаге мы рассмотрим приложение, реализующее красно-черные деревья.
Опишем реализацию АТД "Красно-чёрные деревья" на языке C.
Для красно-чёрных деревьев в каждом узле необходимо хранить:
- (1) указатели на левого (left) и правого (right) потомка;
- (2) указатель (parent) на предка;
- (3) цвет узла (поле color); он может принимать значения либо RED, либо BLACK;
- (4) данные в поле data.
Для хранения цвета достаточно одного бита (однако из-за необходимости "выравнивания" структур, требуемого для эффективности доступа, как правило, будет потрачено больше памяти).
Таким образом, каждый узел красно-чёрного дерева требует памяти для размещения 4-х указателей.
Замечание.
При реализации можно сохранять указатели на проходимые при движении по дереву узлы (например в стеке) и таким образом обойтись без указателя на отца, но наличие указателя на отца делает код значительно проще.
Все листья дерева являются "сторожевыми", что значительно упрощает коды обработки граничных условий: вместо NIL используется фиктивный элемент (англ. sentinal).
Для красно-чёрного дерева T фиктивный элемент nil(T) имеет те же поля, что и обычный узел дерева; его цвет чёрный, а остальным полям (left, right, parent, data) могут быть присвоены любые значения.
Будем считать, что в красно-чёрном дереве все указатели NIL заменены на nil(T).
Благодаря фиктивным элементам можно считать NIL-лист, являющийся ребёнком вершины x, обычной вершиной, родитель которой есть x.
Узел root является корнем дерева и в самом начале является "сторожевым".
Основные функции АТД "Красно-чёрные деревья" таковы:
- (1) функция insertNode() запрашивает память под новый узел, устанавливает нужные значения его полей и вставляет в дерево;
- (2) функция insertNode() вызывает функцию insertFixup(), которая наблюдает за сохранением красно-чёрных свойств дерева;
- (3) функция deleteNode() удаляет узел из дерева;
- (4) функция deleteNode() вызывает функцию deleteFixup(), которая восстанавливает красно-чёрные свойства дерева;
- (5) функция findNode() ищет в дереве нужный узел.
Приведем текст приложения.
Раскрыть/скрыть текст приложения.
/* red-black tree */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
typedef int T; /* type of item to be stored */
#define compLT(a,b) (a < b)
#define compEQ(a,b) (a == b)
/* Red-Black tree description */
typedef enum { BLACK, RED } nodeColor;
typedef struct Node_ {
struct Node_ *left; /* left child */
struct Node_ *right; /* right child */
struct Node_ *parent; /* parent */
nodeColor color; /* node color (BLACK, RED) */
T node_data; /* data stored in node */
} Node;
#define NIL &sentinel /* all leafs are sentinels */
Node sentinel = { NIL, NIL, 0, BLACK, 0};
//=============================================================================
// The RB-Tree management functions.
//
// Note: The internal functions are declared as static
//=============================================================================
//-----------------------------------------------------------------------------
/// rotate node x to left
//-----------------------------------------------------------------------------
static void rotateLeft(Node *x, Node**p_root)
{
Node *y = x->right;
/* establish x->right link */
x->right = y->left;
if (y->left != NIL) y->left->parent = x;
/* establish y->parent link */
if (y != NIL) y->parent = x->parent;
if (x->parent)
{
if (x == x->parent->left)
x->parent->left = y;
else
x->parent->right = y;
}
else
{
*p_root = y;
}
/* link x and y */
y->left = x;
if (x != NIL) x->parent = y;
}
//-----------------------------------------------------------------------------
/// rotate node x to right
//-----------------------------------------------------------------------------
static void rotateRight(Node *x, Node**p_root)
{
Node *y = x->left;
/* establish x->left link */
x->left = y->right;
if (y->right != NIL) y->right->parent = x;
/* establish y->parent link */
if (y != NIL) y->parent = x->parent;
if (x->parent)
{
if (x == x->parent->right)
x->parent->right = y;
else
x->parent->left = y;
}
else
{
*p_root = y;
}
/* link x and y */
y->right = x;
if (x != NIL) x->parent = y;
}
//-----------------------------------------------------------------------------
/// maintain Red-Black tree balance after inserting node x
//-----------------------------------------------------------------------------
static void insertFixup(Node *x, Node**p_root)
{
/* check Red-Black properties */
while (x != *p_root && x->parent->color == RED)
{
/* we have a violation */
if (x->parent == x->parent->parent->left)
{
Node *y = x->parent->parent->right;
if (y->color == RED)
{
/* uncle is RED */
x->parent->color = BLACK;
y->color = BLACK;
x->parent->parent->color = RED;
x = x->parent->parent;
}
else
{
/* uncle is BLACK */
if (x == x->parent->right)
{
/* make x a left child */
x = x->parent;
rotateLeft(x, p_root);
}
/* recolor and rotate */
x->parent->color = BLACK;
x->parent->parent->color = RED;
rotateRight(x->parent->parent, p_root);
}
}
else
{
/* mirror image of above code */
Node *y = x->parent->parent->left;
if (y->color == RED)
{
/* uncle is RED */
x->parent->color = BLACK;
y->color = BLACK;
x->parent->parent->color = RED;
x = x->parent->parent;
}
else
{
/* uncle is BLACK */
if (x == x->parent->left)
{
x = x->parent;
rotateRight(x, p_root);
}
x->parent->color = BLACK;
x->parent->parent->color = RED;
rotateLeft(x->parent->parent, p_root);
}
}
}
(*p_root)->color = BLACK;
}
//-----------------------------------------------------------------------------
/// insert new node in the tree as a child of given parent (after find was made)
/// \param[in] x - new node to insert
/// \param[in] parent - the parent of new node. This ptr is calculated by findNode
/// function
/// \param[in] p_root - the root of the tree
//-----------------------------------------------------------------------------
void insertNodeToParent(Node *x, Node*parent, Node**p_root)
{
x->parent = parent;
x->left = NIL;
x->right = NIL;
x->color = RED;
/* insert node in tree */
if(parent) {
if(compLT(x->node_data, parent->node_data))
parent->left = x;
else
parent->right = x;
} else {
*p_root = x;
}
insertFixup(x, p_root);
return;
}
//-----------------------------------------------------------------------------
/// maintain Red-Black tree balance after deleting node x
//-----------------------------------------------------------------------------
static void deleteFixup(Node *x, Node *x_par, Node**p_root)
{
while (x != *p_root && x->color == BLACK) {
if (x == x_par->left) {
Node *w = x_par->right;
if (w->color == RED) {
w->color = BLACK;
x_par->color = RED;
rotateLeft(x_par, p_root);
w = x_par->right;
}
if (w->left->color == BLACK && w->right->color == BLACK) {
w->color = RED;
x = x_par;
x_par = x->parent;
} else {
if (w->right->color == BLACK) {
w->left->color = BLACK;
w->color = RED;
rotateRight(w, p_root);
w = x_par->right;
}
w->color = x_par->color;
x_par->color = BLACK;
w->right->color = BLACK;
rotateLeft (x_par, p_root);
x = *p_root;
x_par = NULL;
}
} else {
Node *w = x_par->left;
if (w->color == RED) {
w->color = BLACK;
x_par->color = RED;
rotateRight (x_par, p_root);
w = x_par->left;
}
if (w->right->color == BLACK && w->left->color == BLACK) {
w->color = RED;
x = x_par;
x_par = x->parent;
} else {
if (w->left->color == BLACK) {
w->right->color = BLACK;
w->color = RED;
rotateLeft (w, p_root);
w = x_par->left;
}
w->color = x_par->color;
x_par->color = BLACK;
w->left->color = BLACK;
rotateRight (x_par, p_root);
x = *p_root;
x_par = NULL;
}
}
}
x->color = BLACK;
}
//-----------------------------------------------------------------------------
/// detach node from tree
/// \param[in] z -- the node to detach
/// \param[in] p_root - the root of the tree
//-----------------------------------------------------------------------------
void detachNode(Node *z, Node**p_root)
{
Node *x, *y, *x_par;
int f;
if(!*p_root)return;
if (!z || z == NIL) return;
if (z->left == NIL || z->right == NIL) {
/* y has a NIL node as a child */
y = z;
} else {
/* find tree successor with a NIL node as a child */
y = z->right;
while (y->left != NIL) y = y->left;
}
/* x is y's only child */
if (y->left != NIL)
x = y->left;
else
x = y->right;
/* remove y from the parent chain */
x_par = y->parent;
if(x!=NIL) x->parent = x_par;
if (y->parent)
if (y == y->parent->left)
y->parent->left = x;
else
y->parent->right = x;
else
*p_root = x;
f = (y->color == BLACK);
if (y != z)
{ /* replace z with y */
y->color = z->color ;
y->left = z->left;
y->right = z->right;
y->parent = z->parent;
if (y->parent)
if (z == y->parent->left)
y->parent->left = y;
else
y->parent->right = y;
else
*p_root = y;
if(y->left != NIL)
y->left->parent = y;
if(y->right != NIL)
y->right->parent = y;
if(x_par==z) x_par=y;
}
if(f)
deleteFixup (x, x_par, p_root);
}
//-----------------------------------------------------------------------------
/// find node containing data and place to insert of not found
/// \param[in] d -- data to find
/// \param[in] p_root -- refers to the tree root
/// \param[out]p_insert_parent -- where to insert the new node if not found:
/// - *p_insert_parent := parent
/// - if node found, this ptr is unchanged!
/// - if no parent (empty tree) 0 is returned in this
/// ptr this value should be passed to
/// insertNodeToParent() function
/// \return ptr to node found, or 0 if not found.
//-----------------------------------------------------------------------------
Node *findNode(T d, Node**p_root, Node**p_insert_parent)
{
if(*p_root)
{ Node *current = *p_root;
Node *parent = 0;
while(current != NIL)
if(compEQ(d, current->node_data))
return (current);
else
{ parent=current;
current = compLT (d, current->node_data) ? current->left
: current->right;
}
if(p_insert_parent) *p_insert_parent = parent;
return(0);
}
else
{ if(p_insert_parent) *p_insert_parent = 0;
return(0);
}
}
//==============================================================================
// Test code
//==============================================================================
Node *root = NIL; /* root of Red-Black tree */
void main(int argc, char **argv) {
int a, maxnum, ct;
Node *t,*ins_pos;
/* command-line:
*
* rbt maxnum
*
* rbt 2000
* process 2000 records
*
*/
maxnum = atoi(argv[1]);
for (ct = maxnum; ct; ct--) {
a = rand() % 9 + 1;
if ((t = findNode(a, &root, &ins_pos)) != NULL) {
detachNode(t, &root);
free(t);
} else {
if ((t = malloc (sizeof(Node))) == 0) {
printf ("insufficient memory (insertNode)\n");
exit(1);
}
t->node_data = a;
insertNodeToParent(t, ins_pos, &root);
}
}
}
Файл с текстом приложения можно взять
здесь.
На следующем шаге мы рассмотрим AA-деревья.
Предыдущий шаг
Содержание
Следующий шаг