Free tutorials & notes in Hindi & English · Clean code examples · Mobile friendly learning
📘 Lesson  ·  Lesson 80

Binary Search Tree (BST)

Written and reviewed by · Senior IT Faculty · 15+ years’ experience

Binary Search Tree Model and Invariant

A binary search tree (BST) is a binary tree with an ordering invariant. Under the distinct-key policy used here, every key in a node's left subtree is smaller than the node key, and every key in its right subtree is greater. The rule applies recursively to every subtree.

        50
      /    \
    30      70
   /  \    /  \
 20   40  60   80

The tree shape depends on insertion order. The same keys can form a short tree or a chain. Therefore “BST search is O(log n)” is not an unconditional guarantee.

Representing Nodes with unique_ptr

#include <memory>
#include <utility>

struct Node {
    int key;
    std::unique_ptr<Node> left;
    std::unique_ptr<Node> right;

    explicit Node(int value) : key(value) {}
};

Each node exclusively owns its children, so std::unique_ptr expresses the tree's ownership directly. When the root is destroyed, recursive member destruction releases all nodes. This avoids manual delete and the leak/double-delete risks of raw owning pointers. Learn the model in C++ smart pointers.

Non-owning observation may still use const Node* temporarily, but ownership remains with the unique pointers.

Insertion and Search

void insert(std::unique_ptr<Node>& root, int key) {
    if (!root) {
        root = std::make_unique<Node>(key);
    } else if (key < root->key) {
        insert(root->left, key);
    } else if (key > root->key) {
        insert(root->right, key);
    }
    // Equal keys are ignored by this explicit policy.
}

bool contains(const std::unique_ptr<Node>& root, int key) {
    if (!root) return false;
    if (key == root->key) return true;
    return key < root->key ? contains(root->left, key)
                           : contains(root->right, key);
}

Both operations follow one root-to-leaf path. Passing the owning pointer by reference lets insertion replace an empty child link. Search accepts a const reference because it does not change ownership or nodes.

Inorder, Preorder and Postorder Traversal

TraversalVisit orderTypical purpose
InorderLeft, node, rightAscending keys in a BST
PreorderNode, left, rightProcess root before subtrees
PostorderLeft, right, nodeProcess children before parent
Level orderBreadth by depthShape and shortest-edge levels
void inorder(const std::unique_ptr<Node>& root) {
    if (!root) return;
    inorder(root->left);
    std::cout << ' ' << root->key;
    inorder(root->right);
}

Each full traversal takes Θ(n) time because every node is visited once. Recursive auxiliary stack space is O(h).

The Three BST Deletion Cases

  1. No child: replace the owning link with null.
  2. One child: move that child into the target's owning link.
  3. Two children: copy the smallest key in the right subtree (inorder successor), then delete that key from the right subtree.
int minimum_key(const Node* node) {
    while (node->left) node = node->left.get();
    return node->key;
}

void erase(std::unique_ptr<Node>& root, int key) {
    if (!root) return;
    if (key < root->key) erase(root->left, key);
    else if (key > root->key) erase(root->right, key);
    else if (!root->left) root = std::move(root->right);
    else if (!root->right) root = std::move(root->left);
    else {
        const int successor = minimum_key(root->right.get());
        root->key = successor;
        erase(root->right, successor);
    }
}

Moving a unique pointer transfers ownership safely. The successor has no left child, so the recursive deletion reduces to a simpler case.

Height, Complexity and Balancing

OperationCost
SearchO(h)
InsertO(h)
DeleteO(h)
Min/maxO(h)
Full traversalΘ(n)
StorageΘ(n)

In a reasonably balanced tree, h=Θ(log n). Inserting already sorted keys into this unbalanced BST makes h=Θ(n), turning operations linear and risking deep recursion. AVL and red-black trees use rotations and balance metadata to control height. Standard std::set and std::map provide logarithmic search/insert/erase complexity requirements; use them instead of a teaching BST when their interface fits. See set and map.

Complete Modern C++ BST Lab

#include <iostream>
#include <memory>
#include <utility>

struct Node {
    int key;
    std::unique_ptr<Node> left, right;
    explicit Node(int value) : key(value) {}
};

void insert(std::unique_ptr<Node>& root, int key) {
    if (!root) root = std::make_unique<Node>(key);
    else if (key < root->key) insert(root->left, key);
    else if (key > root->key) insert(root->right, key);
}

bool contains(const std::unique_ptr<Node>& root, int key) {
    if (!root) return false;
    if (key == root->key) return true;
    return key < root->key ? contains(root->left, key)
                           : contains(root->right, key);
}

void inorder(const std::unique_ptr<Node>& root) {
    if (!root) return;
    inorder(root->left);
    std::cout << ' ' << root->key;
    inorder(root->right);
}

int minimum_key(const Node* node) {
    while (node->left) node = node->left.get();
    return node->key;
}

void erase(std::unique_ptr<Node>& root, int key) {
    if (!root) return;
    if (key < root->key) erase(root->left, key);
    else if (key > root->key) erase(root->right, key);
    else if (!root->left) root = std::move(root->right);
    else if (!root->right) root = std::move(root->left);
    else {
        int successor = minimum_key(root->right.get());
        root->key = successor;
        erase(root->right, successor);
    }
}

int main() {
    std::unique_ptr<Node> root;
    for (int key : {50, 30, 70, 20, 40, 60, 80}) insert(root, key);
    std::cout << "Inorder:"; inorder(root); std::cout << '\n';
    std::cout << "Contains 60: " << std::boolalpha
              << contains(root, 60) << '\n';
    erase(root, 30);
    std::cout << "After deleting 30:"; inorder(root); std::cout << '\n';
}
Inorder: 20 30 40 50 60 70 80 Contains 60: true After deleting 30: 20 40 50 60 70 80

Deleting 30 exercises the two-child case: 40 is its inorder successor.

Common Mistakes, Tests and Practice

MistakeCorrection
Applying ordering only to direct childrenMaintain it for entire subtrees
No duplicate policyDocument reject/count/side rule
Losing a child during deleteTransfer ownership with move
Calling every BST balancedAnalyze actual height
Raw owning pointers without cleanupUse RAII ownership
Dereferencing null rootCheck base case first

Test empty-tree search/delete, root deletion, leaf, one child, two children, absent key, duplicate insertion and skewed input. Practice iterative search, height calculation, validation with lower/upper bounds, predecessor/successor and level-order traversal.

Authoritative References

The definitions and ordering property were checked against these references. Continue with smart pointers and ordered containers.

BST Model और Invariant

Binary search tree में हर node की left subtree की सभी keys छोटी और right subtree की सभी keys बड़ी होती हैं। Rule हर subtree पर recursively लागू होता है। इस program में duplicate key ignore होती है।

        50
      /    \
    30      70
   /  \    /  \
 20   40  60   80

Insertion order tree shape बदलता है; हर BST balanced नहीं होता।

Node Ownership

struct Node {
    int key;
    std::unique_ptr<Node> left, right;
    explicit Node(int value) : key(value) {}
};

Node अपने children को exclusively own करता है। Root destroy होने पर पूरा tree automatic release होता है; manual delete की जरूरत नहीं। Smart pointers पढ़ें।

Insert और Search

Key छोटी हो तो left, बड़ी हो तो right path लें। Null link पर new node बनता है। Search भी एक root-to-leaf path follow करता है और null मिलने पर false देता है। Owning pointer reference insert को empty child replace करने देता है।

Tree Traversals

TraversalOrderUse
InorderLeft, node, rightBST की ascending keys
PreorderNode, left, rightRoot first
PostorderLeft, right, nodeChildren first
Level orderDepth-wiseTree shape

Full traversal Θ(n) time और recursive O(h) stack लेता है।

Deletion के तीन Cases

  1. Leaf: owning link null करें।
  2. One child: child ownership target link में move करें।
  3. Two children: right subtree की minimum key (successor) copy कर उसके original node को delete करें।

unique_ptr move ownership सुरक्षित transfer करता है।

Height और Complexity

OperationCost
Search/Insert/DeleteO(h)
Balanced heightΘ(log n)
Skewed heightΘ(n)
TraversalΘ(n)
StorageΘ(n)

Sorted insertion chain बना सकता है। Height guarantee के लिए AVL/red-black concept और practical ordered data के लिए std::set/std::map देखें।

Complete Modern C++ Lab

std::unique_ptr<Node> root;
for (int key : {50,30,70,20,40,60,80}) insert(root,key);
std::cout << "Inorder:"; inorder(root);
std::cout << std::boolalpha << contains(root,60);
erase(root,30);
Inorder: 20 30 40 50 60 70 80 Contains 60: true After deleting 30: 20 40 50 60 70 80

English section का full compile-ready program insert, search, inorder और three-case deletion implement करता है। 30 के successor 40 से replacement होता है।

Mistakes, Tests और Practice

  • Ordering केवल direct child पर नहीं, whole subtree पर लागू है।
  • Duplicate policy document करें।
  • Delete में child ownership न खोएं।
  • Actual height analyze करें; BST को automatic balanced न कहें।
  • Null base case पहले रखें।

Empty, leaf, one-child, two-child, root, absent और skewed cases test करें। Height और BST validator practice करें।

Authoritative संदर्भ

Definitions और ordering invariant authoritative sources से verify हैं।

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

BST और binary tree में क्या अंतर है?
Binary tree केवल हर node को maximum two children तक सीमित करता है। BST इसके साथ left/right subtrees पर ordering rule भी लगाता है।
Duplicate keys का क्या करें?
Policy साफ रखें: reject करें, count रखें, या consistently एक side रखें। इस tutorial का program duplicates ignore करता है।
Inorder traversal sorted keys क्यों देता है?
यह left subtree, node, फिर right subtree visit करता है; BST invariant के कारण यही ascending order बनता है।
BST operations की complexity क्या है?
Search, insert और delete O(h) हैं। Balanced tree में O(log n), skewed tree में O(n) हो सकते हैं।
BST deletion के तीन cases कौन से हैं?
Leaf, one child और two children। Two-child case में inorder successor/predecessor key लेकर उस replacement node को हटाते हैं।
← Back to C++ Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।