AVL वृक्ष: घूर्णन, सम्मिलन, विलोपन C++ उदाहरण

⚡ स्मार्ट सारांश

AVL ट्री स्व-संतुलन वाले बाइनरी सर्च ट्री होते हैं, जहां प्रत्येक नोड के बाएं और दाएं सबट्री के बीच ऊंचाई का अंतर -1, 0 या +1 के भीतर रहता है, जो O(log n) सर्च प्रदर्शन की गारंटी देता है।

  • 🌲 परिभाषा: एक बाइनरी सर्च ट्री जिसमें प्रत्येक नोड का संतुलन कारक {-1, 0, +1} के बीच होता है, जिसका नाम आविष्कारकों एडेलसन-वेल्स्की और लैंडिस के नाम पर रखा गया है।
  • संतुलन कारक: इसकी गणना ऊंचाई (बाएं) - ऊंचाई (दाएं) के रूप में की जाती है; {-1, 0, +1} के बाहर के मान संतुलन बहाल करने के लिए घूर्णन को ट्रिगर करते हैं।
  • 🔄 घुमाव: चार मामलों — एलएल, आरआर, एलआर और आरएल — में असंतुलित प्रविष्टियों या विलोपन के बाद नोड्स को पुनः व्यवस्थित किया जाता है ताकि वृक्ष की ऊंचाई लघुगणकीय बनी रहे।
  • प्रविष्टि: मानक बीएसटी प्रविष्टि के बाद एक ऊपर की ओर चलने की प्रक्रिया होती है जो संतुलन कारकों की पुनर्गणना करती है और अधिकतम एक या दो घूर्णन करती है।
  • विलोपन: यह BST विलोपन के समान है, लेकिन इससे वृक्ष में कई घुमावों की श्रृंखला बन सकती है क्योंकि प्रत्येक पूर्वज पर उपवृक्ष की ऊंचाई कम हो सकती है।
  • 🚀 आवेदन: डेटाबेस, इन-मेमोरी इंडेक्स, फाइलसिस्टम मेटाडेटा और एआई सर्च स्ट्रक्चर तेजी से क्रमबद्ध लुकअप के लिए एवीएल ट्री का उपयोग करते हैं।

एवीएल पेड़

AVL वृक्ष क्या हैं?

एवीएल पेड़ बाइनरी सर्च ट्री (बीएसटी) में प्रत्येक नोड के बाएं और दाएं सबट्री के बीच ऊंचाई का अंतर -1, 0 या +1 होता है। ये स्व-संतुलित बीएसटी होते हैं जो लॉगरिदमिक सर्च टाइम बनाए रखते हैं, और इनका नाम आविष्कारकों एडेलसन-वेल्स्की और लैंडिस (एवीएल) के नाम पर रखा गया है।

AVL ट्री कैसे काम करता है?

AVL ट्री क्यों मौजूद हैं, यह समझने के लिए, देखें कि एक साधारण AVL ट्री में क्या गड़बड़ी होती है। बाइनरी सर्च ट्रीइन कुंजियों को दिए गए क्रम में डालने पर विचार करें:

AVL वृक्ष कार्य

AVL वृक्ष दृश्यावलोकन

कुंजीयाँ बढ़ते क्रम में आने पर वृक्ष रैखिक रूप से बढ़ता है, जिससे खोज समय O(n) तक सीमित हो जाता है। यह एक संतुलित वृक्ष (BST) के उद्देश्य को ही विफल कर देता है — केवल एक संतुलित वृक्ष ही खोज समय को लघुगणकीय बनाए रखता है। अब उन्हीं कुंजियों को अलग क्रम में डालने पर विचार करें।

AVL वृक्ष कार्य

समान कुंजी, अलग-अलग सम्मिलन क्रम से एक उथली आकृति बनती है, इसलिए प्रत्येक खोज O(log n) समय में पूरी होती है। AVL ट्री प्रत्येक सम्मिलन पर ऊंचाई की निगरानी करके और BST क्रम को तोड़े बिना असंतुलन को ठीक करके उस आकृति को बनाए रखते हैं।

AVL वृक्षों में संतुलन कारक

संतुलन कारक (बीएफ) tracप्रत्येक नोड की ऊंचाई को समायोजित करें ताकि ट्री चलते-फिरते स्वयं को संतुलित कर सके।

बैलेंस फैक्टर के गुण

AVL वृक्षों में संतुलन कारक

संतुलन कारक AVL वृक्ष

  • संतुलन कारक बाएं सबट्री की ऊंचाई और दाएं सबट्री की ऊंचाई के बीच का अंतर है।
  • Balance factor(node) = height(node->left) − height(node->right)
  • केवल अनुमत मान -1, 0 और +1 हैं।
  • -1 का मान यह दर्शाता है कि दाएँ उपवृक्ष में एक अतिरिक्त स्तर है - नोड दाएँ-भारी है।
  • +1 का मान यह दर्शाता है कि बाएं उपवृक्ष में एक अतिरिक्त स्तर है - नोड बाएँ ओर अधिक भार वाला है।
  • 0 का मान यह दर्शाता है कि दोनों भुजाओं की ऊंचाई बराबर है — नोड पूरी तरह से संतुलित है।

एवीएल रोटेशन

जब भी कोई प्रविष्टि या विलोपन संतुलन कारक नियम को तोड़ता है, तो घूर्णन प्रक्रिया चलती है। ये चार स्थितियाँ हैं: LL, RR, LR और RL।

बायाँ – बायाँ घुमाव

यह रोटेशन तब किया जाता है जब बाएं उपवृक्ष के बाएं संतान में एक नया नोड डाला जाता है।

AVL ट्री बायाँ – बायाँ रोटेशन

AVL ट्री बायाँ – बायाँ रोटेशन

एक बार दाएँ ओर घूर्णन किया जाता है। यह स्थिति तब उत्पन्न होती है जब किसी नोड का BF +2 हो और उसके बाएँ चाइल्ड का BF +1 हो।

दायाँ-दायाँ घुमाव

यह रोटेशन तब किया जाता है जब दाएं उपवृक्ष के दाएं संतान में एक नया नोड डाला जाता है।

AVL ट्री राइट – राइट रोटेशन

एक बार बाएँ ओर घूर्णन किया जाता है। यह स्थिति तब उत्पन्न होती है जब किसी नोड का BF −2 हो और उसके दाएँ चाइल्ड का BF −1 हो।

दायाँ-बायाँ घुमाव

यह रोटेशन तब किया जाता है जब दाएं उपवृक्ष के बाएं संतान में एक नया नोड डाला जाता है।

AVL ट्री दायाँ – बायाँ रोटेशन

यह तब सक्रिय होता है जब BF(नोड) = −2 और BF(दायां चाइल्ड) = +1 हो। दाएं चाइल्ड को दाईं ओर घुमाएं, फिर नोड को बाईं ओर घुमाएं।

बाएँ-दाएँ घुमाव

यह रोटेशन तब किया जाता है जब बाएं उपवृक्ष के दाएं संतान में एक नया नोड डाला जाता है।

AVL ट्री बाएँ – दाएँ रोटेशन

यह तब सक्रिय होता है जब BF(नोड) = +2 और BF(बायां चाइल्ड) = −1 हो। पहले बाएं चाइल्ड को बाईं ओर घुमाएं, फिर नोड को दाईं ओर घुमाएं।

AVL वृक्षों में सम्मिलन

इंसर्शन प्रक्रिया लगभग एक सामान्य BST इंसर्शन के समान है। प्रत्येक इंसर्शन के बाद, ट्री ऊपर की ओर बढ़ता है और पुनः संतुलन स्थापित करता है। इंसर्शन प्रक्रिया सबसे खराब स्थिति में O(log n) समय में पूरी हो जाती है।

AVL वृक्षों में सम्मिलन

AVL वृक्ष सम्मिलन कार्यान्वयन

चरण १: मानक BST एल्गोरिदम का उपयोग करके नोड डालें। ऊपर दिए गए उदाहरण में, 160 डालें।

चरण १: सम्मिलन पथ के साथ प्रत्येक पूर्वज के संतुलन कारक को अद्यतन करें।

चरण १: यदि कोई पूर्वज संतुलन कारक सीमा का उल्लंघन करता है, तो मिलान रोटेशन निष्पादित करें। उदाहरण में, नोड 350 का संतुलन कारक उल्लंघन करता है, इसलिए एक एलएल रोटेशन संतुलन को बहाल करता है।

  1. If BF(node) = +2 और BF(left-child) = +1एलएल रोटेशन करें।
  2. If BF(node) = −2 और BF(right-child) = −1आरआर रोटेशन करें।
  3. If BF(node) = −2 और BF(right-child) = +1आरएल रोटेशन निष्पादित करें।
  4. If BF(node) = +2 और BF(left-child) = −1LR रोटेशन करें।

AVL वृक्षों में विलोपन

विलोपन की प्रक्रिया एक सामान्य बीएसटी के समान ही होती है और बाद में पुनः संतुलित हो जाती है।

चरण १: पेड़ में तत्व ढूंढें.

चरण १: मानक बीएसटी विलोपन विधि का उपयोग करके नोड को हटाएँ।

चरण १: दो स्थितियाँ संभव हैं।

प्रकरण 1: दाएँ उपवृक्ष से हटाया जा रहा है.

  • 1A. If BF(node) = +2 और BF(left-child) = +1एलएल रोटेशन करें।
  • 1B। If BF(node) = +2 और BF(left-child) = −1LR रोटेशन करें।
  • 1C। If BF(node) = +2 और BF(left-child) = 0एलएल रोटेशन करें।

AVL वृक्षों में विलोपन

प्रकरण 2: बाएँ उपवृक्ष से हटाना।

  • 2A. If BF(node) = −2 और BF(right-child) = −1आरआर रोटेशन करें।
  • 2B। If BF(node) = −2 और BF(right-child) = +1आरएल रोटेशन निष्पादित करें।
  • 2C। If BF(node) = −2 और BF(right-child) = 0आरआर रोटेशन करें।

AVL वृक्षों में विलोपन

C++ AVL वृक्षों का उदाहरण

नीचे एक है C++ AVL ट्री को लागू करने वाला प्रोग्राम:

#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);
}

ऊपर दिए गए कोड का उदाहरण:

  1. ऊपर दिए गए कोड को कॉपी करें और इसे एक फ़ाइल में सेव करें जिसका नाम है avl.cpp.
  2. कोड संकलित करें:
g++ avl.cpp -o run
  1. कोड चलाएँ.
./run

C++ AVL वृक्षों का उदाहरण

एवीएल वृक्षों के लाभ

  • AVL ट्री की ऊंचाई हमेशा संतुलित रहती है और कभी भी log N से अधिक नहीं बढ़ती है।
  • यह सर्च एक साधारण बाइनरी सर्च ट्री की तुलना में तेज़ है क्योंकि ट्री विकृत नहीं हो सकता।
  • स्व-संतुलन स्वचालित है — पुनर्निर्माण की कोई आवश्यकता नहीं है।
  • निश्चित प्रदर्शन वास्तविक समय प्रणालियों और इन-मेमोरी इंडेक्स के लिए उपयुक्त है।

अक्सर पूछे जाने वाले प्रश्न

एक AVL ट्री एक स्व-संतुलित बाइनरी सर्च ट्री है जहाँ प्रत्येक नोड का संतुलन कारक {-1, 0, +1} के बीच रहता है। रोटेशन प्रत्येक इंसर्ट या डिलीट पर इस अपरिवर्तनीयता को बहाल करता है।ping O(log n) पर खोजें, डालें और हटाएं।

किसी नोड का संतुलन कारक बाएँ उपवृक्ष की ऊँचाई में से दाएँ उपवृक्ष की ऊँचाई घटाने के बराबर होता है। मान {-1, 0, +1} के बीच होना चाहिए। +2 या -2 का संतुलन कारक यह दर्शाता है कि किसी प्रविष्टि या विलोपन ने उस नोड को असंतुलित कर दिया है और उसे घुमाने की आवश्यकता है।

चार रोटेशन हैं: LL, RR, LR और RL। LL में एक सिंगल राइट रोटेशन होता है, RR में एक सिंगल लेफ्ट रोटेशन होता है, और LR और RL डबल रोटेशन होते हैं जो चाइल्ड पर एक रोटेशन को नोड पर विपरीत रोटेशन के साथ जोड़ते हैं।

इंसर्शन मानक BST नियम का पालन करता है, फिर ट्री ऊँचाई को अपडेट करते हुए वापस ऊपर की ओर बढ़ता है। यदि कोई पूर्वज संतुलन नियम को तोड़ता है, तो एक या दो रोटेशन संतुलन को बहाल कर देते हैं। प्रति इंसर्शन अधिकतम एक रोटेशन की ही आवश्यकता होती है।

AVL ट्री एक सख्त संतुलित संरचना है जिसमें संतुलन कारक अधिकतम एक होता है, जिससे खोज प्रक्रिया तेज़ होती है। रेड-ब्लैक ट्री में संतुलन थोड़ा ढीला होता है, जिससे इंसर्ट और डिलीट ऑपरेशन सस्ते होते हैं लेकिन खोज थोड़ी धीमी हो जाती है। डेटाबेस लेखन संबंधी अधिक कार्यों के लिए रेड-ब्लैक ट्री को प्राथमिकता देते हैं।

AVL ट्री इन-मेमोरी डेटाबेस इंडेक्स, फाइलसिस्टम मेटाडेटा, प्रायोरिटी क्यू, फोनबुक लुकअप, स्पेल चेकर और किसी भी ऐसे वर्कलोड को शक्ति प्रदान करते हैं जिसके लिए नियतात्मक O(log n) खोज के साथ-साथ रेंज क्वेरी के लिए इन-ऑर्डर ट्रैवर्सल की आवश्यकता होती है।

जी हां। कृत्रिम बुद्धिमत्ता प्रणालियां सिंबल टेबल, क्रमबद्ध फीचर स्टोर, केडी ट्री बैलेंसिंग और संरचित डेटा पर निकटतम-पड़ोसी लुकअप के लिए एवीएल ट्री का उपयोग करती हैं। ये बुद्धिमान खोज पाइपलाइनों में रैंक किए गए पुनर्प्राप्ति सूचकांकों का आधार भी बनती हैं।

हाँ। GitHub Copilot और इसी तरह के AI सहायक इंसर्ट, डिलीट और रोटेशन रूटीन को तैयार करते हैं। C++, Javaया, Pythonऔर ऐसे यूनिट टेस्ट तैयार करें जो प्रत्येक ऑपरेशन पर बैलेंस फैक्टर इनवेरिएंट को सत्यापित करें।

इस पोस्ट को संक्षेप में इस प्रकार लिखें: