AVL Ağaçları: Döndürme, Ekleme, Silme C++ Örnek E-posta

⚡ Akıllı Özet

AVL Ağaçları, her düğümün sol ve sağ alt ağaçları arasındaki yükseklik farkının -1, 0 veya +1 içinde kaldığı, O(log n) arama performansını garanti eden, kendi kendini dengeleyen ikili arama ağaçlarıdır.

  • 🌲 Tanım: Her düğümün denge faktörünün {-1, 0, +1} aralığında yer aldığı ikili arama ağacı, mucitleri Adelson-Velsky ve Landis'in adıyla anılmaktadır.
  • 🇧🇷 Denge Faktörü: Yükseklik(sol) − yükseklik(sağ) olarak hesaplanır; {-1, 0, +1} dışındaki değerler dengeyi sağlamak için bir döndürme işlemini tetikler.
  • 🔄 Rotasyonlar: Dört durum (LL, RR, LR ve RL), dengesiz eklemeler veya silmelerden sonra ağacın yüksekliğinin logaritmik kalmasını sağlamak için düğümleri yeniden hizalar.
  • Yerleştirme: Standart BST eklentisini takiben, denge faktörlerini yeniden hesaplayan ve en fazla bir veya iki dönüş gerçekleştiren yukarı doğru bir yürüyüş yapılır.
  • Silme: İkili arama ağacındaki silme işlemiyle aynıdır, ancak alt ağaç yüksekliği her atanın seviyesinde küçülebileceğinden, ağaçta yukarı doğru birden fazla dönüş meydana gelebilir.
  • ???? Uygulamalar: Veritabanları, bellek içi indeksler, dosya sistemi meta verileri ve yapay zeka arama yapıları, hızlı ve sıralı aramalar için AVL Ağaçlarını kullanır.

AVL Ağaçları

AVL Ağaçları nedir?

AVL Ağaçları İkili arama ağaçları, her düğümün sol ve sağ alt ağacı arasındaki yükseklik farkının -1, 0 veya +1 olduğu ağaçlardır. Logaritmik arama süresini koruyan, kendi kendini dengeleyen ikili arama ağaçlarıdır ve mucitleri Adelson-Velsky ve Landis'in (AVL) adını taşırlar.

AVL Ağacı nasıl çalışır?

AVL ağaçlarının neden var olduğunu anlamak için, düz bir yapıda nelerin yanlış gittiğine bakın. İkili Arama AğacıAşağıdaki tuşların verilen sırayla yerleştirildiğini göz önünde bulundurun:

AVL Ağacı çalışması

AVL ağaç görselleştirmesi

Anahtarlar artan sırada geldiğinde ağaç doğrusal olarak büyür ve arama karmaşıklığı O(n)'ye düşer. Bu, ikili arama ağacının amacını ortadan kaldırır; yalnızca dengeli bir ağaç aramayı logaritmik tutar. Şimdi aynı anahtarların farklı bir sırada eklenmesine bakalım.

AVL Ağacı çalışması

Aynı anahtarlar, farklı ekleme sırası daha sığ bir şekil oluşturur, bu nedenle her arama O(log n) sürede çalışır. AVL Ağaçları, her eklemede yüksekliği izleyerek ve BST sıralamasını bozmadan dengesizliği düzelterek bu şekli sağlar.

AVL Ağaçlarında Denge Faktörü

Denge faktörü (BF) tracHer düğümün yüksekliğini ks cinsinden belirterek ağacın anlık olarak kendi kendini dengeleyebilmesini sağlar.

Denge Faktörünün Özellikleri

AVL Ağaçlarında Denge Faktörü

Denge faktörü AVL ağacı

  • Denge faktörü, sol alt ağacın yüksekliği ile sağ alt ağacın yüksekliği arasındaki farktır.
  • Balance factor(node) = height(node->left) − height(node->right)
  • İzin verilen tek değerler -1, 0 ve +1'dir.
  • -1 değeri, sağ alt ağacın fazladan bir seviye içerdiği anlamına gelir; yani düğüm sağ ağırlıklıdır.
  • +1 değeri, sol alt ağacın fazladan bir seviye içerdiği anlamına gelir; yani düğüm sol ağırlıklıdır.
  • 0 değeri, her iki tarafın da eşit yüksekliğe sahip olduğu anlamına gelir; düğüm mükemmel bir şekilde dengededir.

AVL Rotasyonları

Ekleme veya silme işlemi denge faktörü kuralını bozduğunda rotasyonlar çalışır. Dört durum LL, RR, LR ve RL'dir.

Sol – Sola Dönüş

Bu döndürme, sol alt ağacın sol çocuğuna yeni bir düğüm eklendiğinde gerçekleştirilir.

AVL Ağacı Sola – Sola Döndürme

AVL Ağacı Sola – Sola Döndürme

Tek bir sağa döndürme işlemi gerçekleştirilir. Bu durum, bir düğümün BF +2 değerine sahip olması ve sol çocuğunun BF +1 değerine sahip olması durumunda tetiklenir.

Sağ – Sağ Döndürme

Bu döndürme, sağ alt ağacın sağ çocuğuna yeni bir düğüm eklendiğinde gerçekleştirilir.

AVL Ağacı Sağ – Sağa Döndürme

Tek bir sola döndürme işlemi gerçekleştirilir. Bu durum, bir düğümün BF değeri -2 ve sağ çocuğunun BF değeri -1 olduğunda tetiklenir.

Sağa – Sola Dönüş

Bu döndürme, sağ alt ağacın sol çocuğuna yeni bir düğüm eklendiğinde gerçekleştirilir.

AVL Ağacı Sağ – Sola Dönüş

BF(düğüm) = −2 ve BF(sağ çocuk) = +1 olduğunda tetiklenir. Sağ çocuğu sağa döndürün, ardından düğümü sola döndürün.

Sol – Sağa Döndürme

Bu döndürme, sol alt ağacın sağ çocuğuna yeni bir düğüm eklendiğinde gerçekleştirilir.

AVL Ağacı Sol – Sağa Döndürme

BF(düğüm) = +2 ve BF(sol çocuk) = −1 olduğunda tetiklenir. Sol çocuğu sola döndürün, ardından düğümü sağa döndürün.

AVL Ağaçlarına Ekleme

Ekleme işlemi, düz bir BST ekleme işlemine neredeyse tamamen benzer. Her eklemeden sonra ağaç yukarı doğru ilerler ve yeniden dengelenir. Ekleme işlemi en kötü durumda O(log n) sürede çalışır.

AVL Ağaçlarına Ekleme

AVL ağaç ekleme uygulaması

1 Adım: Düğümü standart BST algoritmasını kullanarak ekleyin. Yukarıdaki örnekte, 160'ı ekleyin.

2 Adım: Ekleme yolu boyunca her bir atanın denge faktörünü güncelleyin.

3 Adım: Eğer herhangi bir üst düğüm denge faktörü aralığını ihlal ederse, eşleşen döndürme işlemini gerçekleştirin. Örnekte, 350 numaralı düğümün denge faktörü ihlal edildiğinden, bir LL döndürme işlemi dengeyi yeniden sağlar.

  1. If BF(node) = +2 hem de BF(left-child) = +1LL rotasyonunu gerçekleştirin.
  2. If BF(node) = −2 hem de BF(right-child) = −1RR rotasyonunu gerçekleştirin.
  3. If BF(node) = −2 hem de BF(right-child) = +1RL rotasyonunu gerçekleştirin.
  4. If BF(node) = +2 hem de BF(left-child) = −1LR rotasyonunu gerçekleştirin.

AVL Ağaçlarında Silme

Silme işlemi, düz bir ikili arama ağacındakiyle aynı mantığı izler ve sonrasında yeniden dengeleme yapar.

1 Adım: Ağaçtaki öğeyi bulun.

2 Adım: Düğümü standart BST silme yöntemini kullanarak silin.

3 Adım: İki olasılık söz konusudur.

Olgu 1: Sağ alt ağaçtan silme.

  • 1A. If BF(node) = +2 hem de BF(left-child) = +1LL rotasyonunu gerçekleştirin.
  • 1B. If BF(node) = +2 hem de BF(left-child) = −1LR rotasyonunu gerçekleştirin.
  • 1C. If BF(node) = +2 hem de BF(left-child) = 0LL rotasyonunu gerçekleştirin.

AVL Ağaçlarında Silme

Olgu 2: Sol alt ağaçtan silme işlemi.

  • 2A. If BF(node) = −2 hem de BF(right-child) = −1RR rotasyonunu gerçekleştirin.
  • 2B. If BF(node) = −2 hem de BF(right-child) = +1RL rotasyonunu gerçekleştirin.
  • 2C. If BF(node) = −2 hem de BF(right-child) = 0RR rotasyonunu gerçekleştirin.

AVL Ağaçlarında Silme

C++ AVL Ağaçları Örneği

Aşağıda ise C++ AVL Ağaçlarını uygulayan program:

#include <iostream>
#include <queue>
#include <unordered_map>
using namespace std;

struct node {
    struct node *left;
    int data;
    int height;
    struct node *right;
};

class AVL {
public:
    struct node *root;

    AVL() {
        this->root = NULL;
    }

    int calheight(struct node *p) {
        if (p->left && p->right) {
            if (p->left->height < p->right->height)
                return p->right->height + 1;
            else
                return p->left->height + 1;
        }
        else if (p->left && p->right == NULL) {
            return p->left->height + 1;
        }
        else if (p->left == NULL && p->right) {
            return p->right->height + 1;
        }
        return 0;
    }

    int bf(struct node *n) {
        if (n->left && n->right)
            return n->left->height - n->right->height;
        else if (n->left && n->right == NULL)
            return n->left->height;
        else if (n->left == NULL && n->right)
            return -n->right->height;
        return 0;
    }

    struct node *llrotation(struct node *n) {
        struct node *p = n;
        struct node *tp = p->left;
        p->left = tp->right;
        tp->right = p;
        return tp;
    }

    struct node *rrrotation(struct node *n) {
        struct node *p = n;
        struct node *tp = p->right;
        p->right = tp->left;
        tp->left = p;
        return tp;
    }

    struct node *rlrotation(struct node *n) {
        struct node *p = n;
        struct node *tp = p->right;
        struct node *tp2 = p->right->left;
        p->right = tp2->left;
        tp->left = tp2->right;
        tp2->left = p;
        tp2->right = tp;
        return tp2;
    }

    struct node *lrrotation(struct node *n) {
        struct node *p = n;
        struct node *tp = p->left;
        struct node *tp2 = p->left->right;
        p->left = tp2->right;
        tp->right = tp2->left;
        tp2->right = p;
        tp2->left = tp;
        return tp2;
    }

    struct node *insert(struct node *r, int data) {
        if (r == NULL) {
            r = new struct node;
            r->data = data;
            r->left = r->right = NULL;
            r->height = 1;
            return r;
        }
        if (data < r->data)
            r->left = insert(r->left, data);
        else
            r->right = insert(r->right, data);

        r->height = calheight(r);

        if (bf(r) == 2 && bf(r->left) == 1)       r = llrotation(r);
        else if (bf(r) == -2 && bf(r->right) == -1) r = rrrotation(r);
        else if (bf(r) == -2 && bf(r->right) == 1)  r = rlrotation(r);
        else if (bf(r) == 2 && bf(r->left) == -1)   r = lrrotation(r);

        return r;
    }

    void levelorder_newline() {
        if (this->root == NULL) {
            cout << "\nEmpty tree\n";
            return;
        }
        levelorder_newline(this->root);
    }

    void levelorder_newline(struct node *v) {
        queue<struct node *> q;
        struct node *cur;
        q.push(v);
        q.push(NULL);
        while (!q.empty()) {
            cur = q.front();
            q.pop();
            if (cur == NULL && q.size() != 0) {
                cout << "\n";
                q.push(NULL);
                continue;
            }
            if (cur != NULL) {
                cout << " " << cur->data;
                if (cur->left != NULL)  q.push(cur->left);
                if (cur->right != NULL) q.push(cur->right);
            }
        }
    }

    struct node *deleteNode(struct node *p, int data) {
        if (p->left == NULL && p->right == NULL) {
            if (p == this->root) this->root = NULL;
            delete p;
            return NULL;
        }
        struct node *q;
        if (p->data < data)      p->right = deleteNode(p->right, data);
        else if (p->data > data) p->left  = deleteNode(p->left, data);
        else {
            if (p->left != NULL) {
                q = inpre(p->left);
                p->data = q->data;
                p->left = deleteNode(p->left, q->data);
            } else {
                q = insuc(p->right);
                p->data = q->data;
                p->right = deleteNode(p->right, q->data);
            }
        }

        if (bf(p) == 2 && bf(p->left) == 1)         p = llrotation(p);
        else if (bf(p) == 2 && bf(p->left) == -1)    p = lrrotation(p);
        else if (bf(p) == 2 && bf(p->left) == 0)     p = llrotation(p);
        else if (bf(p) == -2 && bf(p->right) == -1)  p = rrrotation(p);
        else if (bf(p) == -2 && bf(p->right) == 1)   p = rlrotation(p);
        else if (bf(p) == -2 && bf(p->right) == 0)   p = rrrotation(p);

        return p;
    }

    struct node *inpre(struct node *p) {
        while (p->right != NULL) p = p->right;
        return p;
    }

    struct node *insuc(struct node *p) {
        while (p->left != NULL) p = p->left;
        return p;
    }

    ~AVL() {}
};

int main() {
    AVL b;
    int c, x;
    do {
        cout << "\n1.Display levelorder on newline";
        cout << "\n2.Insert";
        cout << "\n3.Delete\n";
        cout << "\n0.Exit\n";
        cout << "\nChoice: ";
        cin >> c;
        switch (c) {
        case 1: b.levelorder_newline(); break;
        case 2:
            cout << "\nEnter no. "; cin >> x;
            b.root = b.insert(b.root, x);
            break;
        case 3:
            cout << "\nWhat to delete? "; cin >> x;
            b.root = b.deleteNode(b.root, x);
            break;
        case 0: break;
        }
    } while (c != 0);
}

Yukarıdaki kodun çalışma örneği:

  1. Yukarıdaki kodu kopyalayın ve bir dosyaya kaydedin. avl.cpp.
  2. Kodu derleyin:
g++ avl.cpp -o run
  1. Kodu çalıştırın.
./run

C++ AVL Ağaçları Örneği

AVL Ağaçlarının Avantajları

  • AVL ağacının yüksekliği her zaman dengelidir ve asla N kütüğünün ötesine geçmez.
  • Arama, ağacın dejenere olmaması nedeniyle düz bir ikili arama ağacından daha hızlıdır.
  • Otomatik dengeleme işlemi gerçekleşir; yeniden yapılandırma adımı gerekmez.
  • Deterministik performans, gerçek zamanlı sistemler ve bellek içi indeksler için uygundur.

SSS

AVL ağacı, her düğümün denge faktörünün {-1, 0, +1} aralığında kaldığı, kendi kendini dengeleyen bir ikili arama ağacıdır. Döndürmeler, her ekleme veya silme işleminde bu değişmezliği geri kazandırır.ping Arama, ekleme ve silme işlemleri O(log n) karmaşıklığında gerçekleştirilir.

Bir düğümün denge faktörü, yükseklik(sol alt ağaç) eksi yükseklik(sağ alt ağaç)'a eşittir. Değerler {-1, 0, +1} aralığında olmalıdır. Denge faktörünün +2 veya -2 olması, bir ekleme veya silme işleminin o düğümü dengesiz hale getirdiğini ve bir döndürme işleminin gerekli olduğunu gösterir.

Dört döndürme işlemi LL, RR, LR ve RL'dir. LL tek bir sağ döndürme kullanır, RR tek bir sol döndürme kullanır, LR ve RL ise çocuk üzerindeki bir döndürmeyi düğüm üzerindeki zıt bir döndürmeyle birleştiren çift döndürmelerdir.

Ekleme işlemi standart BST kuralına göre yapılır, ardından ağaç yükseklikleri güncelleyerek yukarı doğru ilerler. Herhangi bir üst üye denge kuralını bozarsa, tek veya çift döndürme işlemi dengeyi yeniden sağlar. Her ekleme için en fazla bir döndürme işlemine ihtiyaç duyulur.

AVL ağaçları, en fazla bir denge faktörüyle kesinlikle dengelenmiştir ve bu da daha hızlı arama işlemleri sağlar. Kırmızı-Siyah ağaçlar daha gevşek bir dengeye izin verir; bu da ekleme ve silme işlemlerini daha ucuz hale getirir ancak aramayı biraz daha yavaşlatır. Veritabanları, yoğun yazma işlemleri için kırmızı-siyah ağaçları tercih eder.

AVL Ağaçları, bellek içi veritabanı indekslerini, dosya sistemi meta verilerini, öncelik kuyruklarını, telefon rehberi aramalarını, yazım denetleyicilerini ve aralık sorguları için deterministik O(log n) arama ve sıralı geçiş gerektiren her türlü iş yükünü destekler.

Evet. Yapay zeka sistemleri, sembol tabloları, sıralı özellik depoları, kd ağacı dengelemesi ve yapılandırılmış veriler üzerinde en yakın komşu aramaları için AVL Ağaçlarını kullanır. Ayrıca, akıllı arama işlem hatlarında sıralı erişim indekslerinin temelini oluştururlar.

Evet. GitHub Copilot ve benzeri yapay zeka asistanları, ekleme, silme ve döndürme işlemlerini oluşturan rutinlerin iskeletini oluşturur. C++, Javaya da Pythonve her işlemde denge faktörünün değişmezliğini doğrulayan birim testleri oluşturun.

Bu yazıyı şu şekilde özetleyin: