Loading learning content...
In the previous page, we established that a tree is a connected acyclic graph—a precise mathematical definition. But when engineers discuss tree algorithms, they don't speak in terms of "connected acyclic graphs." They speak of roots, parents, children, siblings, ancestors, and descendants.
This vocabulary isn't just convenient terminology—it's a mental model that transforms how we think about trees. Rather than viewing a tree as an abstract collection of vertices and edges, this vocabulary imposes a hierarchical perspective that aligns with how tree algorithms actually work.
By the end of this page, you will have complete fluency in tree vocabulary. You'll understand the precise meaning of every term, how they relate to each other, and how designating a root transforms an abstract tree into a navigable hierarchy.
This vocabulary comes from an analogy to family trees—and like all good analogies, it illuminates while occasionally misleading. We'll embrace the intuition while being rigorous about the definitions.
The formal tree definition (connected acyclic graph) describes what we call a free tree or unrooted tree. In an unrooted tree, no vertex is special—all vertices are structurally equivalent. There's no "up" or "down," no "parent" or "child."
But algorithms need direction. We need to know where to start, which way to traverse, and how to organize our thinking. This is where rooted trees enter.
Definition of a Rooted Tree:
A rooted tree is a tree with one designated vertex called the root. The root establishes direction: all edges are considered to point away from the root (toward the "leaves").
The diagram shows the same underlying tree rooted at two different vertices. Notice how the same five vertices and four edges produce different hierarchies depending on which vertex we designate as the root.
Key insight: Choosing a root doesn't change the tree's structure—it changes our perspective on the structure. The connected acyclic graph remains the same; only our interpretation changes.
Different root choices can dramatically affect algorithm efficiency and code simplicity. A well-chosen root often simplifies the problem. We'll see examples of this when we study tree algorithms in later modules.
The root is the designated starting vertex of a rooted tree. It establishes the entire hierarchical structure—every other concept (parent, child, etc.) is defined relative to the root.
Formal definition of Root:
The root is a distinguished vertex in a rooted tree from which all other vertices can be reached by following edges in a single direction (downward in conventional drawings).
In the diagram, the root (highlighted in green) sits at the top. All other nodes are below it in the hierarchy. Notice that:
Convention: Trees are almost always drawn with the root at the top and leaves at the bottom—opposite to how biological trees grow. This convention matches how we read (top to bottom) and became standard in computer science.
The parent-child relationship is the atomic unit of tree structure. Every edge in a rooted tree represents exactly one parent-child relationship.
Formal definitions:
For any edge (u, v) in a rooted tree where u is closer to the root:
- u is the parent of v
- v is the child of u
Alternatively:
If we can reach v by going downward (away from the root) from u in exactly one step, then u is v's parent and v is u's child.
Accessing parent and children in code:
Tree representations store these relationships explicitly:
class TreeNode {
constructor(value) {
this.value = value;
this.parent = null; // Pointer to parent (may be null for root)
this.children = []; // Array of child nodes
}
}
For binary trees specifically:
class BinaryTreeNode {
constructor(value) {
this.value = value;
this.parent = null; // Optional: not always stored
this.left = null; // Left child
this.right = null; // Right child
}
}
Many tree implementations don't store parent pointers because they require extra memory and maintenance. Algorithms that need to go 'upward' typically pass the parent as a parameter during recursion instead.
Just like in a family, siblings are nodes that share the same parent.
Formal definition:
Two distinct vertices are siblings if and only if they have the same parent.
This is a symmetric relationship: if A is a sibling of B, then B is a sibling of A.
In the diagram:
Important properties of siblings:
In binary trees, siblings have a special distinction: one is the 'left child' and one is the 'right child.' This ordering is meaningful and affects algorithms. In general trees, children may or may not have a defined order.
An ancestor is any node on the path from a given node up to the root. This extends the parent relationship across multiple generations.
Formal definition:
Vertex u is an ancestor of vertex v if u lies on the unique path from v to the root (inclusive of v and the root).
Alternative recursive definition:
A vertex v is an ancestor of itself.<br> If u is an ancestor of v, and w is the parent of u, then w is also an ancestor of v.
Note: Under this definition, every node is an ancestor of itself. Some texts use "proper ancestor" to exclude the node itself:
Vertex u is a proper ancestor of vertex v if u is an ancestor of v and u ≠ v.
In the diagram, the node G (red) has the following ancestors (green path):
The ancestors form a straight line from any node to the root—this is the unique path guaranteed by the tree structure.
Finding ancestors algorithmically:
function getAncestors(node) {
const ancestors = [];
let current = node;
while (current !== null) {
ancestors.push(current);
current = current.parent; // Move up toward root
}
return ancestors; // [node, parent, grandparent, ..., root]
}
This runs in O(h) time where h is the height of the tree (maximum depth of any node).
A crucial algorithm in tree processing is finding the Lowest Common Ancestor of two nodes—the deepest node that is an ancestor of both. This enables efficient queries about relationships between nodes.
A descendant is the inverse of an ancestor. While ancestors look upward toward the root, descendants look downward toward the leaves.
Formal definition:
Vertex v is a descendant of vertex u if u is an ancestor of v.
Equivalently:
Vertex v is a descendant of u if the unique path from v to the root passes through u.
In the diagram, the node A (red) has the following descendants (blue):
Notice that the descendants of A form a complete subtree—a tree in their own right with A as the root.
Finding descendants algorithmically:
function getDescendants(node) {
const descendants = [];
function collectDescendants(current) {
descendants.push(current);
for (const child of current.children) {
collectDescendants(child); // Recursive DFS
}
}
collectDescendants(node);
return descendants; // [node, all nodes in subtree]
}
This runs in O(k) time where k is the number of nodes in the subtree—we must visit every descendant.
Understanding how these relationships connect is crucial for algorithmic thinking:
Inverse relationships:
Every relationship has an inverse. When you say "A is B's parent," you're simultaneously saying "B is A's child." These are two views of the same edge.
| Relationship | Count from a Node | Notes |
|---|---|---|
| Parents | 0 (root) or 1 (others) | Maximum one parent per node |
| Children | 0 to many | Depends on node; 0 for leaves |
| Siblings | 0 to many | All other children of same parent |
| Ancestors | 0 (root) to h | h = depth of the node |
| Descendants | 0 (leaf) to n-1 | Root has all n-1 other nodes |
When solving tree problems, it often helps to think about the relationship from both directions. 'Find all descendants of X' and 'find the ancestor of every node and check if it's X' are equivalent approaches with different trade-offs.
Before leaving core vocabulary, we must define the degree of a node—a concept often confused with children count.
In graph theory:
The degree of a vertex is the number of edges incident to it.
In rooted trees, we distinguish:
The degree (or branching factor) of a node in a rooted tree is typically defined as the number of its children.
Be careful: in the unrooted graph sense, a non-root node has degree equal to (number of children + 1) because the parent edge counts too. But in rooted tree discussions, 'degree' usually means just the number of children. Always check context!
In the diagram, each node is labeled with its degree (number of children):
The degree of the tree is often defined as the maximum degree of any node. This tree has degree 3.
Let's solidify these concepts with a concrete example. Consider this file system tree:
C:\
├── Users\
│ ├── Alice\
│ │ ├── Documents\
│ │ └── Downloads\
│ └── Bob\
│ └── Desktop\
├── Program Files\
│ ├── Chrome\
│ └── VSCode\
└── Windows\
└── System32\
| Term | Example(s) |
|---|---|
| Root | C:\ |
| Parent of 'Documents' | Alice |
| Children of 'Users' | Alice, Bob |
| Siblings of 'Chrome' | VSCode |
| Ancestors of 'System32' | Windows, C:\ (and System32 itself) |
| Descendants of 'Users' | Alice, Bob, Documents, Downloads, Desktop |
| Leaves | Documents, Downloads, Desktop, Chrome, VSCode, System32 |
| Internal nodes | C:, Users, Alice, Bob, Program Files, Windows |
| Depth of 'Downloads' | 3 (C:\ → Users → Alice → Downloads) |
| Degree of 'C:' | 3 (Users, Program Files, Windows) |
Your computer's file system is literally a tree data structure. Folders are internal nodes, files are leaves, and the drive root is the root. Every folder has exactly one parent (you can't put a folder in two places), and there are no cycles.
You now have complete fluency in the language of trees. Let's consolidate:
What's next:
With the hierarchical vocabulary established, we can now distinguish between two critical types of nodes: leaf nodes (with no children) and internal nodes (with at least one child). These categories are fundamental to tree algorithms and traversal strategies.
You can now speak the language of trees fluently. When you read 'traverse all descendants of node X' or 'find the lowest common ancestor,' you know exactly what these terms mean. This vocabulary is the foundation for understanding tree algorithms.