Kiejtés

  • IPA: [ ˈpiroʃfɛkɛtɛfɒ]

Főnév

piros-fekete fa

  1. (matematika, gráfelmélet, algoritmusok) A számítástudományban a piros-fekete fa alatt egy önkiegyensúlyozó bináris keresőfát értünk. A szerkezete összetett, de a gyakorlatban hatékony, hiszen a keresés, beszúrás és törlés lépésszáma a legrosszabb esetben is O(log n), ahol n a fában levő elemek száma.

Piros-fekete fa

A piros-fekete fa egy bináris keresőfa, amely garantálja, hogy a beszúrás, törlés és keresés műveletek időkomplexitása (O(n)) legyen. A piros-fekete fa egy önkiegyensúlyozó bináris keresőfa, amely egyensúlyi feltételek segítségével korlátozza a fa magasságát.



Tulajdonságok

Egy piros-fekete fa minden csúcsa rendelkezik egy szín attribútummal, amely lehet piros vagy fekete, és a következő tulajdonságokat teljesíti:

  1. Minden csúcs vagy piros, vagy fekete.
  2. A gyökér mindig fekete.
  3. Minden levél (NIL) fekete.
    • A levelek olyan csúcsok, amelyekhez nem tartozik valódi érték.
  4. Egy piros csúcsnak nincsenek piros gyerekei.
    • Azaz a piros csúcsok nem helyezkedhetnek egymás mellett (a fa “dupla piros” állapota nem engedélyezett).
  5. Bármely csúcsból egy levélig vezető összes út azonos számú fekete csúcsot tartalmaz.
    • Ezt nevezzük fekete magasságnak.

Ezek a tulajdonságok biztosítják, hogy a fa kiegyensúlyozott maradjon, és a műveletek hatékonyan végrehajthatók legyenek.



Műveletek

1. Beszúrás

A beszúrás során először egy új csúcsot helyezünk el piros színnel. Ezután a fa tulajdonságainak megsértése esetén helyreállítjuk az egyensúlyt rotációkkal és színváltásokkal.

2. Törlés

A törlés során először eltávolítjuk a csúcsot, majd, ha szükséges, helyreállítjuk a tulajdonságokat hasonló rotációkkal és színváltásokkal.

3. Keresés

A keresés ugyanúgy működik, mint egy hagyományos bináris keresőfában, az időkomplexitása (O(n)).

4. Rotációk

  • Balra forgatás: Az aktuális csúcs jobb gyermekét helyezzük a csúcs fölé.
  • Jobbra forgatás: Az aktuális csúcs bal gyermekét helyezzük a csúcs fölé.



Időkomplexitás

  • Beszúrás, törlés, keresés: (O(n)), mert a fa magassága legfeljebb (2 (n+1)).



Pszeudokód

Beszúrás

Insert(T, z):
    helyezd el z-t a bináris keresőfa szabályai szerint
    színezd z-t pirosra
    amíg z nem a gyökér és z szülője piros:
        ha z szülője a nagyapa bal gyereke:
            ha z nagybátyja piros:
                színezd z szülőjét és nagybátyját feketére
                színezd z nagyapját pirosra
                z = z nagyapja
            különben:
                ha z z szülőjének jobb gyereke:
                    z = z szülője
                    balra forgatás(z)
                színezd z szülőjét feketére
                színezd z nagyapját pirosra
                jobbra forgatás(z nagyapja)
        különben (szimmetrikus):
            ugyanaz, mint fent, csak balra és jobbra cserélve
    színezd a gyökeret feketére

Python implementáció

class Node:
    def __init__(self, key, color="red", left=None, right=None, parent=None):
        self.key = key
        self.color = color
        self.left = left
        self.right = right
        self.parent = parent

class RedBlackTree:
    def __init__(self):
        self.TNULL = Node(0, color="black")
        self.root = self.TNULL

    def insert(self, key):
        new_node = Node(key, color="red", left=self.TNULL, right=self.TNULL)
        y = None
        x = self.root

        while x != self.TNULL:
            y = x
            if new_node.key < x.key:
                x = x.left
            else:
                x = x.right

        new_node.parent = y
        if y is None:
            self.root = new_node
        elif new_node.key < y.key:
            y.left = new_node
        else:
            y.right = new_node

        if new_node.parent is None:
            new_node.color = "black"
            return

        if new_node.parent.parent is None:
            return

        self._fix_insert(new_node)

    def _fix_insert(self, k):
        while k.parent.color == "red":
            if k.parent == k.parent.parent.right:
                u = k.parent.parent.left
                if u.color == "red":
                    u.color = "black"
                    k.parent.color = "black"
                    k.parent.parent.color = "red"
                    k = k.parent.parent
                else:
                    if k == k.parent.left:
                        k = k.parent
                        self._rotate_right(k)
                    k.parent.color = "black"
                    k.parent.parent.color = "red"
                    self._rotate_left(k.parent.parent)
            else:
                u = k.parent.parent.right
                if u.color == "red":
                    u.color = "black"
                    k.parent.color = "black"
                    k.parent.parent.color = "red"
                    k = k.parent.parent
                else:
                    if k == k.parent.right:
                        k = k.parent
                        self._rotate_left(k)
                    k.parent.color = "black"
                    k.parent.parent.color = "red"
                    self._rotate_right(k.parent.parent)
            if k == self.root:
                break
        self.root.color = "black"

    def _rotate_left(self, x):
        y = x.right
        x.right = y.left
        if y.left != self.TNULL:
            y.left.parent = x
        y.parent = x.parent
        if x.parent is None:
            self.root = y
        elif x == x.parent.left:
            x.parent.left = y
        else:
            x.parent.right = y
        y.left = x
        x.parent = y

    def _rotate_right(self, x):
        y = x.left
        x.left = y.right
        if y.right != self.TNULL:
            y.right.parent = x
        y.parent = x.parent
        if x.parent is None:
            self.root = y
        elif x == x.parent.right:
            x.parent.right = y
        else:
            x.parent.left = y
        y.right = x
        x.parent = y

# Példa használat
rbt = RedBlackTree()
rbt.insert(10)
rbt.insert(20)
rbt.insert(30)
rbt.insert(15)

C++

#include <iostream>
using namespace std;

enum Color { RED, BLACK };

struct Node {
    int data;
    Color color;
    Node* left;
    Node* right;
    Node* parent;

    Node(int data) : data(data), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};

class RedBlackTree {
private:
    Node* root;

    void rotateLeft(Node* x) {
        Node* y = x->right;
        x->right = y->left;

        if (y->left != nullptr) {
            y->left->parent = x;
        }
        y->parent = x->parent;

        if (x->parent == nullptr) {
            root = y;
        } else if (x == x->parent->left) {
            x->parent->left = y;
        } else {
            x->parent->right = y;
        }
        y->left = x;
        x->parent = y;
    }

    void rotateRight(Node* x) {
        Node* y = x->left;
        x->left = y->right;

        if (y->right != nullptr) {
            y->right->parent = x;
        }
        y->parent = x->parent;

        if (x->parent == nullptr) {
            root = y;
        } else if (x == x->parent->right) {
            x->parent->right = y;
        } else {
            x->parent->left = y;
        }
        y->right = x;
        x->parent = y;
    }

    void fixInsert(Node* k) {
        Node* uncle;
        while (k->parent != nullptr && k->parent->color == RED) {
            if (k->parent == k->parent->parent->left) {
                uncle = k->parent->parent->right;

                // Ha a nagybáty piros
                if (uncle != nullptr && uncle->color == RED) {
                    k->parent->color = BLACK;
                    uncle->color = BLACK;
                    k->parent->parent->color = RED;
                    k = k->parent->parent;
                } else {
                    // Ha a nagybáty fekete
                    if (k == k->parent->right) {
                        k = k->parent;
                        rotateLeft(k);
                    }
                    k->parent->color = BLACK;
                    k->parent->parent->color = RED;
                    rotateRight(k->parent->parent);
                }
            } else {
                uncle = k->parent->parent->left;

                // Szimmetrikus eset
                if (uncle != nullptr && uncle->color == RED) {
                    k->parent->color = BLACK;
                    uncle->color = BLACK;
                    k->parent->parent->color = RED;
                    k = k->parent->parent;
                } else {
                    if (k == k->parent->left) {
                        k = k->parent;
                        rotateRight(k);
                    }
                    k->parent->color = BLACK;
                    k->parent->parent->color = RED;
                    rotateLeft(k->parent->parent);
                }
            }
        }
        root->color = BLACK;
    }

public:
    RedBlackTree() : root(nullptr) {}

    void insert(int data) {
        Node* newNode = new Node(data);
        if (root == nullptr) {
            root = newNode;
            root->color = BLACK;
            return;
        }

        Node* current = root;
        Node* parent = nullptr;

        while (current != nullptr) {
            parent = current;
            if (newNode->data < current->data) {
                current = current->left;
            } else {
                current = current->right;
            }
        }

        newNode->parent = parent;
        if (newNode->data < parent->data) {
            parent->left = newNode;
        } else {
            parent->right = newNode;
        }

        fixInsert(newNode);
    }

    void inorderTraversal(Node* node) {
        if (node == nullptr) return;
        inorderTraversal(node->left);
        cout << node->data << " ";
        inorderTraversal(node->right);
    }

    void display() {
        inorderTraversal(root);
        cout << endl;
    }
};

int main() {
    RedBlackTree rbt;

    rbt.insert(10);
    rbt.insert(20);
    rbt.insert(30);
    rbt.insert(15);
    rbt.insert(25);
    rbt.insert(5);

    cout << "Piros-fekete fa inorder bejárása: ";
    rbt.display();

    return 0;
}

Összegzés

A piros-fekete fa garantáltan hatékony adatszerkezet keresésekhez, beszúrásokhoz és törlésekhez. Bár bonyolultabb, mint az egyszerű bináris keresőfa, a stabil (O(n)) időkomplexitása miatt széles körben használják, például szótárak és prioritási sorok megvalósításában (pl. C++ STL map és set).

Fordítások