Trees: Terminology, Traversals & Representation

Tree — পরিভাষা, ট্রাভার্সাল

Read: ~35 min Intermediate 7 practice problems Live C++ runner

1. Why Trees? (গাছ কেন?)

Up to now we have studied linear structures — arrays, linked lists, stacks, queues. They are good when data flows in a straight line. But the real world is rarely linear: a file system has folders inside folders, an HTML document has tags inside tags, a Bangladeshi family has parents and children and grandchildren. To model this we need a structure that branches. That structure is a tree.

এতদিন আমরা শুধু linear structure পড়েছি — array, linked list, stack, queue। কিন্তু বাস্তব জগৎ linear না: file system-এ folder-এর ভিতরে folder, HTML document-এ tag-এর ভিতরে tag, একটি বাংলাদেশি পরিবারে দাদা–বাবা–ছেলে–নাতি। এমন hierarchy বোঝাতে দরকার এমন structure যা শাখা–প্রশাখা ছড়ায়। সেটিই tree।
Big idea — Tree আসলে cycle-হীন একটি graph। কিন্তু hierarchy এত উপকারী যে আমরা একে আলাদা category-তে রাখি। একটি tree-তে n node থাকলে edge সংখ্যা ঠিক n−1।

2. Vocabulary You Must Memorise (পরিভাষা)

TermMeaningবাংলায়
RootThe topmost node — has no parent.সবচেয়ে উপরের node, যার কোনো parent নেই।
Parent / ChildA direct ancestor / descendant by one edge.একটি edge দূরত্বের সরাসরি পূর্বপুরুষ / উত্তরসূরি।
SiblingNodes sharing the same parent.একই parent-এর সন্তানরা — ভাই-বোন।
LeafA node with no children.যার কোনো সন্তান নেই — পাতা।
Internal nodeA node with at least one child.অন্তত একটি সন্তান আছে এমন node।
Depth of nodeEdges from root to that node.root থেকে ওই node পর্যন্ত edge সংখ্যা।
Height of treeMaximum depth among all nodes.সব node-এর মধ্যে সর্বোচ্চ depth।
SubtreeA node together with all its descendants.একটি node ও তার সব উত্তরসূরি মিলিয়ে।
Binary treeEach node has at most 2 children (left, right).প্রতিটি node-এর সর্বোচ্চ ২টি সন্তান।
একটি বহুল ভুল: কেউ কেউ বলেন "একটিমাত্র node-ও tree না"। আসলে একটিমাত্র node-ও একটি বৈধ tree (height = 0)। শূন্য node-ও একটি বৈধ "empty tree"।

3. A Picture Worth 1000 Lines

Here is a binary tree of depth 3. Below the picture, look at how each traversal "walks" the same tree differently.

1 2 3 4 5 6 7 8 9 Pre-order: 1 2 4 8 9 5 3 6 7 In-order: 8 4 9 2 5 1 6 3 7 Post-order: 8 9 4 5 2 6 7 3 1 Level-order: 1 2 3 4 5 6 7 8 9 Figure 16.1 — একই tree-তে চারটি traversal-এর order।
একই tree-কে চারভাবে "ঘোরা" যায়। এর প্রতিটি order এক একটি কাজে লাগে: pre-order copy/serialize-এ, in-order BST-তে sorted output পেতে, post-order delete বা expression evaluate-এ, আর level-order shortest path বা width বের করতে।

4. Representation — Pointers vs Array

There are two common ways to store a tree in memory.

✅ Pointer-based (সাধারণ পদ্ধতি)

  • Each node is a struct with left, right pointers.
  • Easy to insert / delete arbitrary nodes.
  • Memory used = O(n).
  • Used in BST, AVL, RB, expression trees.

⚙️ Array-based (implicit tree)

  • For node at index i: left = 2i+1, right = 2i+2, parent = (i−1)/2.
  • Used in heaps and segment trees.
  • Wastes space if tree is sparse / unbalanced.
  • No pointer overhead, very cache-friendly.
Common bug — recursion-এ NULL check ভুলে গেলে segmentation fault। প্রতিটি traversal-এর শুরুতেই if (!root) return; রাখুন।

5. Build a Tree & Recursive Traversals — Live

Below we build the tree from Figure 16.1, then run all three depth-first traversals using simple recursion.

tree_traversal.cpp
#include <bits/stdc++.h>
using namespace std;

// একটি node — value আর দুটি child pointer।
struct Node {
    int val;
    Node *left, *right;
    Node(int v) : val(v), left(nullptr), right(nullptr) {}
};

void preorder(Node* r) {
    if (!r) return;
    cout << r->val << ' ';
    preorder(r->left);
    preorder(r->right);
}
void inorder(Node* r) {
    if (!r) return;
    inorder(r->left);
    cout << r->val << ' ';
    inorder(r->right);
}
void postorder(Node* r) {
    if (!r) return;
    postorder(r->left);
    postorder(r->right);
    cout << r->val << ' ';
}

int main() {
    Node* root = new Node(1);
    root->left  = new Node(2);
    root->right = new Node(3);
    root->left->left  = new Node(4);
    root->left->right = new Node(5);
    root->right->left  = new Node(6);
    root->right->right = new Node(7);
    root->left->left->left  = new Node(8);
    root->left->left->right = new Node(9);

    cout << "Pre  : "; preorder(root);  cout << '\n';
    cout << "In   : "; inorder(root);   cout << '\n';
    cout << "Post : "; postorder(root); cout << '\n';
    return 0;
}

6. Level-Order Traversal — BFS with a Queue

Depth-first traversals naturally use recursion (a stack). Level-order goes level-by-level — that needs a queue. This is also called BFS on a tree and is the foundation of "shortest-path" thinking that you will see again with Dijkstra in Phase 6.

DFS (pre/in/post)-এ stack লাগে, BFS-এ queue। Level-order মানে আগে level 0, তারপর level 1, level 2, এইভাবে। Codeforces-এর tree problem-গুলোতে এটি বহু ব্যবহৃত প্যাটার্ন।
level_order.cpp
#include <bits/stdc++.h>
using namespace std;

struct Node { int v; Node *l,*r; Node(int x):v(x),l(nullptr),r(nullptr){} };

void levelOrder(Node* root) {
    if (!root) return;
    queue<Node*> q;
    q.push(root);
    int level = 0;
    while (!q.empty()) {
        int sz = q.size();
        cout << "Level " << level++ << ": ";
        while (sz--) {
            Node* n = q.front(); q.pop();
            cout << n->v << ' ';
            if (n->l) q.push(n->l);
            if (n->r) q.push(n->r);
        }
        cout << '\n';
    }
}

int main() {
    Node* root = new Node(1);
    root->l = new Node(2);
    root->r = new Node(3);
    root->l->l = new Node(4);
    root->l->r = new Node(5);
    root->r->r = new Node(7);
    levelOrder(root);
    return 0;
}

7. A Tiny Proof — Why edges = n − 1

Claim. A non-empty tree with n nodes has exactly n − 1 edges.
Proof (induction). Base: n = 1, no edges, 1 − 1 = 0. ✓ Inductive step: assume any tree with k nodes has k − 1 edges. Add a new leaf; that adds exactly one new edge connecting it to its parent. So (k + 1) nodes have k edges = (k + 1) − 1. ∎
এটি tree-র অন্যতম মৌলিক সম্পত্তি। একে cycle না থাকা ও connected হওয়ার সাথে মিলিয়ে graph theory-তে "tree"-এর সংজ্ঞা গঠিত হয়।

8. Common Bugs to Avoid

NULL deref
recursion-এ !root check ভুললে SIGSEGV।
Memory leak
delete না করলে heap বড় হতে থাকে — competitive-এ সমস্যা না, প্রোডাকশন-এ মারাত্মক।
Stack overflow
10⁵ গভীরতার skewed tree-তে recursion crash করতে পারে — iterative করুন।
Left/Right confusion
ছোট/বড় বা west/east — যেকোনো একটি নিয়ম মানুন consistently।

9. Practice Problems

প্রতিটি প্রশ্নে runnable C++ answer দেওয়া হয়েছে। আগে নিজে চেষ্টা করুন।

  1. Write a function that returns the height of a binary tree.
    একটি binary tree-এর height return করুন।
    ✨ Show Answer
    height.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    int height(N* r){ return r? 1+max(height(r->l),height(r->r)) : 0; }
    int main(){
        N* r=new N(1); r->l=new N(2); r->r=new N(3); r->l->l=new N(4);
        cout << "Height = " << height(r);
    }

    Height বলতে এখানে node-সংখ্যা ধরা হয়েছে (অনেক বইয়ে edge-সংখ্যা ধরা হয় — দুটিই বৈধ, প্রশ্ন বুঝে ব্যবহার করুন)।

  2. Count total nodes and number of leaves in a binary tree.
    মোট node ও leaf-এর সংখ্যা গুনুন।
    ✨ Show Answer
    count.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    int cntNodes(N* r){ return r? 1+cntNodes(r->l)+cntNodes(r->r) : 0; }
    int cntLeaves(N* r){
        if(!r) return 0;
        if(!r->l && !r->r) return 1;
        return cntLeaves(r->l)+cntLeaves(r->r);
    }
    int main(){
        N* r=new N(1); r->l=new N(2); r->r=new N(3); r->l->l=new N(4); r->l->r=new N(5);
        cout << "Nodes=" << cntNodes(r) << " Leaves=" << cntLeaves(r);
    }
  3. Check whether a binary tree is height-balanced (|left height − right height| ≤ 1 for every node).
    প্রতিটি node-এ left ও right height-এর পার্থক্য ≤ 1 কিনা পরীক্ষা করুন।
    ✨ Show Answer
    balanced.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    // returns height; sets bal=false if unbalanced anywhere
    int chk(N* r, bool& bal){
        if(!r) return 0;
        int a=chk(r->l,bal), b=chk(r->r,bal);
        if(abs(a-b)>1) bal=false;
        return 1+max(a,b);
    }
    int main(){
        N* r=new N(1); r->l=new N(2); r->l->l=new N(3);
        bool b=true; chk(r,b);
        cout << (b? "Balanced" : "NOT balanced");
    }
  4. Mirror (invert) a binary tree.
    একটি binary tree-এর mirror তৈরি করুন।
    ✨ Show Answer
    mirror.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    void mirror(N* r){ if(!r) return; swap(r->l,r->r); mirror(r->l); mirror(r->r); }
    void in(N* r){ if(!r)return; in(r->l); cout<<r->v<<' '; in(r->r); }
    int main(){
        N* r=new N(1); r->l=new N(2); r->r=new N(3); r->l->l=new N(4);
        cout << "Before: "; in(r); cout << '\n';
        mirror(r);
        cout << "After : "; in(r);
    }
  5. Print level-order in a zig-zag pattern (left→right, then right→left, alternating).
    Zig-zag level order — এক level বাঁ থেকে ডান, পরের level ডান থেকে বাঁ।
    ✨ Show Answer
    zigzag.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    void zz(N* root){
        if(!root) return;
        deque<N*> dq; dq.push_back(root);
        bool ltr=true;
        while(!dq.empty()){
            int sz=dq.size();
            while(sz--){
                if(ltr){
                    N* n=dq.front(); dq.pop_front();
                    cout<<n->v<<' ';
                    if(n->l) dq.push_back(n->l);
                    if(n->r) dq.push_back(n->r);
                } else {
                    N* n=dq.back(); dq.pop_back();
                    cout<<n->v<<' ';
                    if(n->r) dq.push_front(n->r);
                    if(n->l) dq.push_front(n->l);
                }
            }
            cout<<'\n'; ltr=!ltr;
        }
    }
    int main(){
        N* r=new N(1); r->l=new N(2); r->r=new N(3);
        r->l->l=new N(4); r->l->r=new N(5); r->r->l=new N(6); r->r->r=new N(7);
        zz(r);
    }
  6. Iterative inorder traversal using an explicit stack.
    Recursion ছাড়া, explicit stack দিয়ে inorder।
    ✨ Show Answer
    iter_in.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    void iterIn(N* root){
        stack<N*> st; N* cur=root;
        while(cur || !st.empty()){
            while(cur){ st.push(cur); cur=cur->l; }
            cur=st.top(); st.pop();
            cout<<cur->v<<' ';
            cur=cur->r;
        }
    }
    int main(){
        N* r=new N(4); r->l=new N(2); r->r=new N(6);
        r->l->l=new N(1); r->l->r=new N(3);
        iterIn(r);
    }
  7. Serialize and deserialize a binary tree using pre-order with NULL markers.
    Pre-order ও NULL marker দিয়ে serialize/deserialize করুন।
    ✨ Show Answer
    serialize.cpp
    #include <bits/stdc++.h>
    using namespace std;
    struct N{int v;N*l,*r;N(int x):v(x),l(nullptr),r(nullptr){}};
    void ser(N* r, string& s){
        if(!r){ s+="# "; return; }
        s+=to_string(r->v)+' '; ser(r->l,s); ser(r->r,s);
    }
    N* des(stringstream& ss){
        string t; if(!(ss>>t)) return nullptr;
        if(t=="#") return nullptr;
        N* n=new N(stoi(t));
        n->l=des(ss); n->r=des(ss);
        return n;
    }
    void in(N* r){ if(!r)return; in(r->l); cout<<r->v<<' '; in(r->r); }
    int main(){
        N* r=new N(1); r->l=new N(2); r->r=new N(3); r->r->l=new N(4);
        string s; ser(r,s);
        cout << "Serial: " << s << '\n';
        stringstream ss(s); N* r2=des(ss);
        cout << "Inorder of rebuilt: "; in(r2);
    }

Summary — Module 16

A tree is a connected, acyclic structure with n nodes and n − 1 edges. Four traversals (pre/in/post/level) capture different orderings of the same nodes. Pointer-based representation is flexible; array-based is cache-friendly and used in heaps. Recursive traversals are concise but vulnerable to stack overflow on deep skewed trees — keep an iterative version in your toolkit.

Tree একটি connected ও cycle-হীন structure। চারটি প্রধান traversal (pre/in/post/level) এক tree-কে চারভাবে দেখায়। Pointer-based representation নমনীয়, array-based cache-friendly। Recursion-এর পাশাপাশি iterative version-ও জানতে হবে — গভীর tree-তে stack overflow এড়াতে।

Next Module → Binary Search Trees (BST) — যেখানে in-order সবসময় sorted।