Árbol rojo-negro | Grupo 3 (Borrar)

Hemos discutido los siguientes temas sobre el árbol rojo-negro en publicaciones anteriores. Recomendamos encarecidamente hacer referencia a la siguiente publicación como requisito previo de esta publicación.
Introducción al árbol rojo y negro Árbol rojo y  
negro Insertar
Inserción frente a eliminación: 
al igual que la inserción, el cambio de color y las rotaciones se utilizan para mantener las propiedades del rojo y el negro.
En la operación de inserción, comprobamos el color del tío para decidir el caso adecuado. En la operación de eliminación, comprobamos el color del hermano para decidir el caso apropiado.
La propiedad principal que viola después de la inserción son dos rojos consecutivos. En la eliminación, la principal propiedad violada es el cambio de la altura del negro en los subárboles, ya que la eliminación de un Node negro puede causar una reducción de la altura del negro en una ruta de raíz a hoja.
La eliminación es un proceso bastante complejo. Para entender la eliminación, se utiliza la noción de doble negro. Cuando se elimina un Node negro y se reemplaza por un hijo negro, el hijo se marca como doble negro . La tarea principal ahora es convertir este doble negro en un solo negro.
Pasos de eliminación 
A continuación se detallan los pasos para la eliminación.
1) Realizar la eliminación estándar de BST. Cuando realizamos una operación de eliminación estándar en BST, siempre terminamos eliminando un Node que es una hoja o tiene solo un hijo (para un Node interno, copiamos el sucesor y luego recursivamente llamamos a eliminar para el sucesor, el sucesor es siempre un Node hoja o un Node con un hijo). Entonces, solo necesitamos manejar los casos en los que un Node es hoja o tiene un hijo. Sea v el Node que se eliminará y u el hijo que reemplaza a v (tenga en cuenta que u es NULL cuando v es una hoja y el color de NULL se considera negro).
2) Caso simple: si u o v son rojos, marcamos el niño reemplazado como negro (sin cambios en la altura del negro). Tenga en cuenta que tanto u como v no pueden ser rojos ya que v es el padre de u y no se permiten dos rojos consecutivos en el árbol rojo-negro. 
 

rbdelete11

3) Si tanto u como v son negros .
3.1) Color u como doble negro. Ahora nuestra tarea se reduce a convertir este doble negro en un solo negro. Tenga en cuenta que si v es una hoja, entonces u es NULL y el color de NULL se considera negro. Entonces, la eliminación de una hoja negra también provoca un doble negro.
 

rbdelete12_new

3.2) Haga lo siguiente mientras el Node actual u es doblemente negro y no es la raíz. Deje que el hermano del Node sea s
…. (a): Si el hermano s es negro y al menos uno de los hijos del hermano es , realice la(s) rotación(es). Que el niño rojo de s sea. Este caso se puede dividir en cuatro subcasos dependiendo de las posiciones de s y r.
………….. (i) Left Left Case (s es hijo izquierdo de su padre y r es hijo izquierdo de s o ambos hijos de s son rojos). Este es el espejo de la carcasa derecha derecha que se muestra en el siguiente diagrama.
………….. (ii) Caso Izquierda Derecha (s es el hijo izquierdo de su padre y r es el hijo derecho). Este es el espejo de la caja izquierda derecha que se muestra en el diagrama a continuación.
………….. (iii)Right Right Case (s es hijo derecho de su padre y r es hijo derecho de s o ambos hijos de s son rojos) 
 

rbdelete13New

………….. (iv) Caso derecho izquierdo (s es hijo derecho de su padre y r es hijo izquierdo de s) 
 

rbdelete14

….. (b): Si el hermano es negro y sus dos hijos son negros , realice el cambio de color y recurra para el padre si el padre es negro. 
 

rbdelete15

En este caso, si el padre era rojo, entonces no necesitábamos recurrir al padre, simplemente podemos hacerlo negro (rojo + negro doble = negro simple)
….. (c): Si el hermano es , realice una rotación para mover al hermano mayor hacia arriba, volver a colorear al hermano mayor y al padre. El nuevo hermano siempre es negro (ver el diagrama a continuación). Esto convierte principalmente el árbol en un caso hermano negro (por rotación) y conduce al caso (a) o (b). Este caso se puede dividir en dos subcasos. 
………….. (i) Caso Izquierdo (s es hijo izquierdo de su padre). Este es el espejo de la carcasa derecha derecha que se muestra en el siguiente diagrama. Giramos a la derecha el padre p. 
………….. (ii) Caso derecho (s es hijo derecho de su padre). Dejamos rotar el padre p. 
 

rbdelete16

3.3) Si u es raíz, hágalo solo negro y regrese (la altura negra del árbol completo se reduce en 1).
a continuación se muestra la implementación en C++ del enfoque anterior: 
 

CPP

#include <iostream>
#include <queue>
using namespace std;
 
enum COLOR { RED, BLACK };
 
class Node {
public:
  int val;
  COLOR color;
  Node *left, *right, *parent;
 
  Node(int val) : val(val) {
    parent = left = right = NULL;
 
    // Node is created during insertion
    // Node is red at insertion
    color = RED;
  }
 
  // returns pointer to uncle
  Node *uncle() {
    // If no parent or grandparent, then no uncle
    if (parent == NULL or parent->parent == NULL)
      return NULL;
 
    if (parent->isOnLeft())
      // uncle on right
      return parent->parent->right;
    else
      // uncle on left
      return parent->parent->left;
  }
 
  // check if node is left child of parent
  bool isOnLeft() { return this == parent->left; }
 
  // returns pointer to sibling
  Node *sibling() {
    // sibling null if no parent
    if (parent == NULL)
      return NULL;
 
    if (isOnLeft())
      return parent->right;
 
    return parent->left;
  }
 
  // moves node down and moves given node in its place
  void moveDown(Node *nParent) {
    if (parent != NULL) {
      if (isOnLeft()) {
        parent->left = nParent;
      } else {
        parent->right = nParent;
      }
    }
    nParent->parent = parent;
    parent = nParent;
  }
 
  bool hasRedChild() {
    return (left != NULL and left->color == RED) or
           (right != NULL and right->color == RED);
  }
};
 
class RBTree {
  Node *root;
 
  // left rotates the given node
  void leftRotate(Node *x) {
    // new parent will be node's right child
    Node *nParent = x->right;
 
    // update root if current node is root
    if (x == root)
      root = nParent;
 
    x->moveDown(nParent);
 
    // connect x with new parent's left element
    x->right = nParent->left;
    // connect new parent's left element with node
    // if it is not null
    if (nParent->left != NULL)
      nParent->left->parent = x;
 
    // connect new parent with x
    nParent->left = x;
  }
 
  void rightRotate(Node *x) {
    // new parent will be node's left child
    Node *nParent = x->left;
 
    // update root if current node is root
    if (x == root)
      root = nParent;
 
    x->moveDown(nParent);
 
    // connect x with new parent's right element
    x->left = nParent->right;
    // connect new parent's right element with node
    // if it is not null
    if (nParent->right != NULL)
      nParent->right->parent = x;
 
    // connect new parent with x
    nParent->right = x;
  }
 
  void swapColors(Node *x1, Node *x2) {
    COLOR temp;
    temp = x1->color;
    x1->color = x2->color;
    x2->color = temp;
  }
 
  void swapValues(Node *u, Node *v) {
    int temp;
    temp = u->val;
    u->val = v->val;
    v->val = temp;
  }
 
  // fix red red at given node
  void fixRedRed(Node *x) {
    // if x is root color it black and return
    if (x == root) {
      x->color = BLACK;
      return;
    }
 
    // initialize parent, grandparent, uncle
    Node *parent = x->parent, *grandparent = parent->parent,
         *uncle = x->uncle();
 
    if (parent->color != BLACK) {
      if (uncle != NULL && uncle->color == RED) {
        // uncle red, perform recoloring and recurse
        parent->color = BLACK;
        uncle->color = BLACK;
        grandparent->color = RED;
        fixRedRed(grandparent);
      } else {
        // Else perform LR, LL, RL, RR
        if (parent->isOnLeft()) {
          if (x->isOnLeft()) {
            // for left right
            swapColors(parent, grandparent);
          } else {
            leftRotate(parent);
            swapColors(x, grandparent);
          }
          // for left left and left right
          rightRotate(grandparent);
        } else {
          if (x->isOnLeft()) {
            // for right left
            rightRotate(parent);
            swapColors(x, grandparent);
          } else {
            swapColors(parent, grandparent);
          }
 
          // for right right and right left
          leftRotate(grandparent);
        }
      }
    }
  }
 
  // find node that do not have a left child
  // in the subtree of the given node
  Node *successor(Node *x) {
    Node *temp = x;
 
    while (temp->left != NULL)
      temp = temp->left;
 
    return temp;
  }
 
  // find node that replaces a deleted node in BST
  Node *BSTreplace(Node *x) {
    // when node have 2 children
    if (x->left != NULL and x->right != NULL)
      return successor(x->right);
 
    // when leaf
    if (x->left == NULL and x->right == NULL)
      return NULL;
 
    // when single child
    if (x->left != NULL)
      return x->left;
    else
      return x->right;
  }
 
  // deletes the given node
  void deleteNode(Node *v) {
    Node *u = BSTreplace(v);
 
    // True when u and v are both black
    bool uvBlack = ((u == NULL or u->color == BLACK) and (v->color == BLACK));
    Node *parent = v->parent;
 
    if (u == NULL) {
      // u is NULL therefore v is leaf
      if (v == root) {
        // v is root, making root null
        root = NULL;
      } else {
        if (uvBlack) {
          // u and v both black
          // v is leaf, fix double black at v
          fixDoubleBlack(v);
        } else {
          // u or v is red
          if (v->sibling() != NULL)
            // sibling is not null, make it red"
            v->sibling()->color = RED;
        }
 
        // delete v from the tree
        if (v->isOnLeft()) {
          parent->left = NULL;
        } else {
          parent->right = NULL;
        }
      }
      delete v;
      return;
    }
 
    if (v->left == NULL or v->right == NULL) {
      // v has 1 child
      if (v == root) {
        // v is root, assign the value of u to v, and delete u
        v->val = u->val;
        v->left = v->right = NULL;
        delete u;
      } else {
        // Detach v from tree and move u up
        if (v->isOnLeft()) {
          parent->left = u;
        } else {
          parent->right = u;
        }
        delete v;
        u->parent = parent;
        if (uvBlack) {
          // u and v both black, fix double black at u
          fixDoubleBlack(u);
        } else {
          // u or v red, color u black
          u->color = BLACK;
        }
      }
      return;
    }
 
    // v has 2 children, swap values with successor and recurse
    swapValues(u, v);
    deleteNode(u);
  }
 
  void fixDoubleBlack(Node *x) {
    if (x == root)
      // Reached root
      return;
 
    Node *sibling = x->sibling(), *parent = x->parent;
    if (sibling == NULL) {
      // No sibiling, double black pushed up
      fixDoubleBlack(parent);
    } else {
      if (sibling->color == RED) {
        // Sibling red
        parent->color = RED;
        sibling->color = BLACK;
        if (sibling->isOnLeft()) {
          // left case
          rightRotate(parent);
        } else {
          // right case
          leftRotate(parent);
        }
        fixDoubleBlack(x);
      } else {
        // Sibling black
        if (sibling->hasRedChild()) {
          // at least 1 red children
          if (sibling->left != NULL and sibling->left->color == RED) {
            if (sibling->isOnLeft()) {
              // left left
              sibling->left->color = sibling->color;
              sibling->color = parent->color;
              rightRotate(parent);
            } else {
              // right left
              sibling->left->color = parent->color;
              rightRotate(sibling);
              leftRotate(parent);
            }
          } else {
            if (sibling->isOnLeft()) {
              // left right
              sibling->right->color = parent->color;
              leftRotate(sibling);
              rightRotate(parent);
            } else {
              // right right
              sibling->right->color = sibling->color;
              sibling->color = parent->color;
              leftRotate(parent);
            }
          }
          parent->color = BLACK;
        } else {
          // 2 black children
          sibling->color = RED;
          if (parent->color == BLACK)
            fixDoubleBlack(parent);
          else
            parent->color = BLACK;
        }
      }
    }
  }
 
  // prints level order for given node
  void levelOrder(Node *x) {
    if (x == NULL)
      // return if node is null
      return;
 
    // queue for level order
    queue<Node *> q;
    Node *curr;
 
    // push x
    q.push(x);
 
    while (!q.empty()) {
      // while q is not empty
      // dequeue
      curr = q.front();
      q.pop();
 
      // print node value
      cout << curr->val << " ";
 
      // push children to queue
      if (curr->left != NULL)
        q.push(curr->left);
      if (curr->right != NULL)
        q.push(curr->right);
    }
  }
 
  // prints inorder recursively
  void inorder(Node *x) {
    if (x == NULL)
      return;
    inorder(x->left);
    cout << x->val << " ";
    inorder(x->right);
  }
 
public:
  // constructor
  // initialize root
  RBTree() { root = NULL; }
 
  Node *getRoot() { return root; }
 
  // searches for given value
  // if found returns the node (used for delete)
  // else returns the last node while traversing (used in insert)
  Node *search(int n) {
    Node *temp = root;
    while (temp != NULL) {
      if (n < temp->val) {
        if (temp->left == NULL)
          break;
        else
          temp = temp->left;
      } else if (n == temp->val) {
        break;
      } else {
        if (temp->right == NULL)
          break;
        else
          temp = temp->right;
      }
    }
 
    return temp;
  }
 
  // inserts the given value to tree
  void insert(int n) {
    Node *newNode = new Node(n);
    if (root == NULL) {
      // when root is null
      // simply insert value at root
      newNode->color = BLACK;
      root = newNode;
    } else {
      Node *temp = search(n);
 
      if (temp->val == n) {
        // return if value already exists
        return;
      }
 
      // if value is not found, search returns the node
      // where the value is to be inserted
 
      // connect new node to correct node
      newNode->parent = temp;
 
      if (n < temp->val)
        temp->left = newNode;
      else
        temp->right = newNode;
 
      // fix red red voilaton if exists
      fixRedRed(newNode);
    }
  }
 
  // utility function that deletes the node with given value
  void deleteByVal(int n) {
    if (root == NULL)
      // Tree is empty
      return;
 
    Node *v = search(n), *u;
 
    if (v->val != n) {
      cout << "No node found to delete with value:" << n << endl;
      return;
    }
 
    deleteNode(v);
  }
 
  // prints inorder of the tree
  void printInOrder() {
    cout << "Inorder: " << endl;
    if (root == NULL)
      cout << "Tree is empty" << endl;
    else
      inorder(root);
    cout << endl;
  }
 
  // prints level order of the tree
  void printLevelOrder() {
    cout << "Level order: " << endl;
    if (root == NULL)
      cout << "Tree is empty" << endl;
    else
      levelOrder(root);
    cout << endl;
  }
};
 
int main() {
  RBTree tree;
 
  tree.insert(7);
  tree.insert(3);
  tree.insert(18);
  tree.insert(10);
  tree.insert(22);
  tree.insert(8);
  tree.insert(11);
  tree.insert(26);
  tree.insert(2);
  tree.insert(6);
  tree.insert(13);
 
  tree.printInOrder();
  tree.printLevelOrder();
 
  cout<<endl<<"Deleting 18, 11, 3, 10, 22"<<endl;
 
  tree.deleteByVal(18);
  tree.deleteByVal(11);
  tree.deleteByVal(3);
  tree.deleteByVal(10);
  tree.deleteByVal(22);
 
  tree.printInOrder();
  tree.printLevelOrder();
  return 0;
}

Producción: 

Inorder: 
2 3 6 7 8 10 11 13 18 22 26 
Level order: 
10 7 18 3 8 11 22 2 6 13 26 

Deleting 18, 11, 3, 10, 22
Inorder: 
2 6 7 8 13 26 
Level order: 
13 7 26 6 8 2 

Referencias:  
https://www.cs.purdue.edu/homes/ayg/CS251/slides/chap13c.pdf  
Introducción a los algoritmos 3.ª edición por Clifford Stein, Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest
Por favor escriba comentarios si encuentra algo incorrecto, o si desea compartir más información sobre el tema tratado anteriormente
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *