└── README.md /README.md: -------------------------------------------------------------------------------- 1 | # 100 Core Tree Data Structure Interview Questions in 2025 2 | 3 |
4 |

5 | 6 | data-structures-and-algorithms 7 | 8 |

9 | 10 | #### You can also find all 100 answers here 👉 [Devinterview.io - Tree Data Structure](https://devinterview.io/questions/data-structures-and-algorithms/tree-data-structure-interview-questions) 11 | 12 |
13 | 14 | ## 1. What is a _Tree Data Structure_? 15 | 16 | A **tree data structure** is a hierarchical collection of nodes, typically visualized with a root at the top. Trees are typically used for representing relationships, hierarchies, and facilitating efficient data operations. 17 | 18 | ### Core Definitions 19 | 20 | - **Node**: The basic unit of a tree that contains data and may link to child nodes. 21 | - **Root**: The tree's topmost node; no nodes point to the root. 22 | - **Parent / Child**: Nodes with a direct connection; a parent points to its children. 23 | - **Leaf**: A node that has no children. 24 | - **Edge**: A link or reference from one node to another. 25 | - **Depth**: The level of a node, or its distance from the root. 26 | - **Height**: Maximum depth of any node in the tree. 27 | 28 | ### Key Characteristics 29 | 30 | - **Hierarchical**: Organized in parent-child relationships. 31 | - **Non-Sequential**: Non-linear data storage ensures flexible and efficient access patterns. 32 | - **Directed**: Nodes are connected unidirectionally. 33 | - **Acyclic**: Trees do not have loops or cycles. 34 | - **Diverse Node Roles**: Such as root and leaf. 35 | 36 | ### Visual Representation 37 | 38 | ![Tree Data Structure](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/binary%20tree%2FTreedatastructure%20(1).png?alt=media&token=d6b820e4-e956-4e5b-8190-2f8a38acc6af&_gl=1*3qk9u9*_ga*OTYzMjY5NTkwLjE2ODg4NDM4Njg.*_ga_CW55HF8NVT*MTY5NzI4NzY1Ny4xNTUuMS4xNjk3Mjg5NDU1LjUzLjAuMA..) 39 | 40 | ### Common Tree Variants 41 | 42 | - **Binary Tree**: Each node has a maximum of two children. 43 | - **Binary Search Tree (BST)**: A binary tree where each node's left subtree has values less than the node and the right subtree has values greater. 44 | - **AVL Tree**: A BST that self-balances to optimize searches. 45 | - **B-Tree**: Commonly used in databases to enable efficient access. 46 | - **Red-Black Tree**: A BST that maintains balance using node coloring. 47 | - **Trie**: Specifically designed for efficient string operations. 48 | 49 | ### Practical Applications 50 | 51 | - **File Systems**: Model directories and files. 52 | - **AI and Decision Making**: Decision trees help in evaluating possible actions. 53 | - **Database Systems**: Many databases use trees to index data efficiently. 54 | 55 | ### Tree Traversals 56 | 57 | #### Depth-First Search 58 | 59 | - **Preorder**: Root, Left, Right. 60 | - **Inorder**: Left, Root, Right (specific to binary trees). 61 | - **Postorder**: Left, Right, Root. 62 | 63 | #### Breadth-First Search 64 | 65 | - **Level Order**: Traverse nodes by depth, moving from left to right. 66 | 67 | ### Code Example: Binary Tree 68 | 69 | Here is the Python code: 70 | 71 | ```python 72 | class Node: 73 | def __init__(self, data): 74 | self.left = None 75 | self.right = None 76 | self.data = data 77 | 78 | # Create a tree structure 79 | root = Node(1) 80 | root.left, root.right = Node(2), Node(3) 81 | root.left.left, root.right.right = Node(4), Node(5) 82 | 83 | # Inorder traversal 84 | def inorder_traversal(node): 85 | if node: 86 | inorder_traversal(node.left) 87 | print(node.data, end=' ') 88 | inorder_traversal(node.right) 89 | 90 | # Expected Output: 4 2 1 3 5 91 | print("Inorder Traversal: ") 92 | inorder_traversal(root) 93 | ``` 94 |
95 | 96 | ## 2. What is a _Binary Tree_? 97 | 98 | A **Binary Tree** is a hierarchical structure where each node has up to two children, termed as **left child** and **right child**. Each node holds a data element and pointers to its left and right children. 99 | 100 | ### Binary Tree Types 101 | 102 | - **Full Binary Tree**: Nodes either have two children or none. 103 | - **Complete Binary Tree**: Every level, except possibly the last, is completely filled, with nodes skewed to the left. 104 | - **Perfect Binary Tree**: All internal nodes have two children, and leaves exist on the same level. 105 | 106 | ### Visual Representation 107 | 108 | ![Binary Tree Types](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/binary%20tree%2Ftree-types.png?alt=media&token=847de252-5545-4a29-9e28-7a7e93c8e657) 109 | 110 | ### Applications 111 | 112 | - **Binary Search Trees**: Efficient in lookup, addition, and removal operations. 113 | - **Expression Trees**: Evaluate mathematical expressions. 114 | - **Heap**: Backbone of priority queues. 115 | - **Trie**: Optimized for string searches. 116 | 117 | ### Code Example: Binary Tree & In-order Traversal 118 | 119 | Here is the Python code: 120 | 121 | ```python 122 | class Node: 123 | """Binary tree node with left and right child.""" 124 | def __init__(self, data): 125 | self.left = None 126 | self.right = None 127 | self.data = data 128 | 129 | def insert(self, data): 130 | """Inserts a node into the tree.""" 131 | if data < self.data: 132 | if self.left is None: 133 | self.left = Node(data) 134 | else: 135 | self.left.insert(data) 136 | elif data > self.data: 137 | if self.right is None: 138 | self.right = Node(data) 139 | else: 140 | self.right.insert(data) 141 | 142 | def in_order_traversal(self): 143 | """Performs in-order traversal and returns a list of nodes.""" 144 | nodes = [] 145 | if self.left: 146 | nodes += self.left.in_order_traversal() 147 | nodes.append(self.data) 148 | if self.right: 149 | nodes += self.right.in_order_traversal() 150 | return nodes 151 | 152 | 153 | # Example usage: 154 | # 1. Instantiate the root of the tree 155 | root = Node(50) 156 | 157 | # 2. Insert nodes (This will implicitly form a Binary Search Tree for simplicity) 158 | values_to_insert = [30, 70, 20, 40, 60, 80] 159 | for val in values_to_insert: 160 | root.insert(val) 161 | 162 | # 3. Perform in-order traversal 163 | print(root.in_order_traversal()) # Expected Output: [20, 30, 40, 50, 60, 70, 80] 164 | ``` 165 |
166 | 167 | ## 3. Explain _Height_ and _Depths_ in the context of a _Tree_. 168 | 169 | In tree data structures, the terms **height** and **depth** refer to different attributes of nodes. 170 | 171 | ### Height 172 | 173 | The **height** of a node is the number of edges on the longest downward path between that node and a leaf. 174 | 175 | - **Height of a Node**: Number of edges in the longest path from that node to any leaf. 176 | - **Height of a Tree**: Essentially the height of its root node. 177 | 178 | ### Depth 179 | 180 | The **depth** or **level** of a node represents the number of edges on the path from the root node to that node. 181 | 182 | For instance, in a binary tree, if a node is at depth 2, it means there are two edges between the root and that node. 183 | 184 | ### Visual Representation 185 | 186 | ![Height and Depths in a Tree Data Structure](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/trees%2Ftree-height-depths%20(1).png?alt=media&token=3c068810-5432-439e-af76-6a8b8dbb746a&_gl=1*1gwqb6o*_ga*OTYzMjY5NTkwLjE2ODg4NDM4Njg.*_ga_CW55HF8NVT*MTY5NzMwNDc2OC4xNTYuMS4xNjk3MzA1OTk1LjUwLjAuMA..) 187 | 188 | ### Code Example: Calculating Height and Depth 189 | 190 | Here is the Python code: 191 | 192 | ```python 193 | class Node: 194 | def __init__(self, data, parent=None): 195 | self.data = data 196 | self.left = None 197 | self.right = None 198 | self.parent = parent 199 | 200 | def height(node): 201 | if node is None: 202 | return -1 203 | left_height = height(node.left) 204 | right_height = height(node.right) 205 | return 1 + max(left_height, right_height) 206 | 207 | def depth(node, root): 208 | if node is None: 209 | return -1 210 | dist = 0 211 | while node != root: 212 | dist += 1 213 | node = node.parent 214 | return dist 215 | 216 | # Create a sample tree 217 | root = Node(1) 218 | root.left = Node(2, root) 219 | root.right = Node(3, root) 220 | root.left.left = Node(4, root.left) 221 | root.left.right = Node(5, root.left) 222 | 223 | # Test height and depth functions 224 | print("Height of tree:", height(root)) 225 | print("Depth of node 4:", depth(root.left.left, root)) 226 | print("Depth of node 5:", depth(root.left.right, root)) 227 | ``` 228 |
229 | 230 | ## 4. What is the difference between a _Tree_ and a _Graph_? 231 | 232 | **Graphs** and **trees** are both nonlinear data structures, but there are fundamental distinctions between them. 233 | 234 | ### Key Distinctions 235 | 236 | - **Uniqueness**: Trees have a single root, while graphs may not have such a concept. 237 | - **Topology**: Trees are **hierarchical**, while graphs can exhibit various structures. 238 | - **Focus**: Graphs center on relationships between individual nodes, whereas trees emphasize the relationship between nodes and a common root. 239 | 240 | ### Graphs: Versatile and Unstructured 241 | 242 | - **Elements**: Composed of vertices/nodes (denoted as V) and edges (E) representing relationships. Multiple edges and **loops** are possible. 243 | - **Directionality**: Edges can be directed or undirected. 244 | - **Connectivity**: May be **disconnected**, with sets of vertices that aren't reachable from others. 245 | - **Loops**: Can contain cycles. 246 | 247 | ### Trees: Hierarchical and Organized 248 | 249 | - **Elements**: Consist of nodes with parent-child relationships. 250 | - **Directionality**: Edges are strictly parent-to-child. 251 | - **Connectivity**: Every node is accessible from the unique root node. 252 | - **Loops**: Cycles are not allowed. 253 | 254 | ### Visual Representation 255 | 256 | ![Graph vs Tree](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/graph-theory%2Ftree-graph.jpg?alt=media&token=0362c5d3-e851-4cd2-bbb4-c632e77ccede&_gl=1*euedhq*_ga*OTYzMjY5NTkwLjE2ODg4NDM4Njg.*_ga_CW55HF8NVT*MTY5NzI4NzY1Ny4xNTUuMS4xNjk3Mjg5NjU2LjYwLjAuMA..) 257 |
258 | 259 | ## 5. Define _Leaf_ and _Internal_ nodes in a _Tree_. 260 | 261 | In the context of a **tree data structure**, nodes can take on distinct roles: 262 | 263 | ### Leaf Nodes 264 | 265 | - **Definition**: Nodes without children are leaf nodes. They are the tree endpoints. 266 | - **Properties**: 267 | - In a binary tree, leaf nodes have either one or no leaves. 268 | - They're the only nodes with a depth. 269 | - **Visual Representation**: 270 | - In a traditional tree visualization, leaf nodes are the ones at the "bottom" of the tree. 271 | 272 | ### Internal Nodes 273 | 274 | - **Definition**: Internal nodes, or non-leaf nodes, have at least one child. 275 | - **Properties**: 276 | - They have at least one child. 277 | - They're "in-between" nodes that connect other nodes in the tree. 278 | - **Visual Representation**: 279 | - In a tree diagram, any node that is not a leaf node is an internal node. 280 | - The root, which is often at the "top" in visual representations, is also an internal node. 281 |
282 | 283 | ## 6. What is a _Rooted Tree_, and how does it differ from an _Unrooted Tree_? 284 | 285 | In computer science, a **rooted tree** — often referred to as just a "tree" — is a data structure that consists of nodes connected by edges, typically in a top-down orientation. 286 | 287 | Each tree has exactly one root node, from which all other nodes are reachable. Rooted trees are distinct from **unrooted trees**, as the latter does not have a designated starting point. 288 | 289 | ### Key Concepts 290 | 291 | - **Root Node**: The unique starting node of the tree. 292 | - **Parent and Children**: Nodes are arranged in a hierarchical manner. The root is the parent of all other nodes, and each node can have multiple children but only one parent. 293 | - **Leaf Nodes**: Nodes that have no children. 294 | - **Depth**: The length of the path from a node to the root. The root has a depth of 0. 295 | - **Height**: The length of the longest path from a node to a leaf. 296 | 297 | ### Visual Comparison 298 | 299 | ![Rooted vs Unrooted Trees](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/strings%2Frooted-vs-unrooted-phylogenetic_tree.jpeg?alt=media&token=5d4e361d-0753-45b0-b324-f89e87cb6bd0) 300 | 301 | The left side represents a rooted tree with a clear root node. The right side features an unrooted tree, where no such distinction exists. 302 | 303 | ### Code Example: Rooted Tree 304 | 305 | Here is the Python code: 306 | 307 | ```python 308 | class Node: 309 | def __init__(self, data): 310 | self.data = data 311 | self.children = [] 312 | 313 | # Example of a rooted tree 314 | root = Node('A') 315 | child1 = Node('B') 316 | child2 = Node('C') 317 | 318 | root.children.append(child1) 319 | root.children.append(child2) 320 | 321 | child3 = Node('D') 322 | child1.children.append(child3) 323 | 324 | # Output: A -> B -> D and A -> C 325 | ``` 326 | 327 | ### Code Example: Unrooted Tree 328 | 329 | Here is the Python code: 330 | 331 | ```python 332 | class Node: 333 | def __init__(self, data): 334 | self.data = data 335 | self.neighbours = set() 336 | 337 | # Example of an unrooted tree 338 | node1 = Node('A') 339 | node2 = Node('B') 340 | node3 = Node('C') 341 | 342 | # Connections 343 | node1.neighbours |= {node2, node3} 344 | node2.neighbours.add(node3) 345 | 346 | # Output: A <-> B, A <-> C, B <-> C 347 | ``` 348 | 349 | ### Practical Applications 350 | 351 | - **File Systems**: Representing directory structures where each directory is a node of a rooted tree. 352 | - **HTML DOM**: Visualized as a tree with the HTML tag being the root. 353 | 354 | ### Unrooted Trees in Nature 355 | 356 | - **Phylogenetic Trees**: Used to represent the evolutionary relationships among a group of species without a clear ancestor. 357 | - **Stemmatology**: In textual criticism, they're used to describe textual relationships without identifying an original or "root" text. 358 |
359 | 360 | ## 7. What is a _N-ary Tree_, and how does it generalize a binary tree? 361 | 362 | An **N-ary Tree** is a data structure with nodes that can have up to $N$ children, allowing for more than two child nodes. This property provides the tree with a more flexible hierarchical structure compared to the strict two-child policy observed in **binary trees**, permitting either a **binary** or **non-binary** organization of nodes, as per the figure below. 363 | 364 | ### N-ary Tree Representation 365 | 366 | ![N-ary Tree](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/strings%2Fnary_tree_example.png?alt=media&token=6d0ab117-342f-4d7c-9ec7-caafe7ac34c5) 367 | 368 | In **N-ary Trees**, nodes can have a dynamic number of child nodes, dictated by the length of a list or array where these nodes are stored. This contrasts with the binary tree, where nodes have a fixed, often predefined, number of children (either 0, 1, or 2). 369 | 370 | Some embodiments conveniently use an array, where each element corresponds to a child. While this allows for $O(1)$ child lookups, **a la binary heap**. It does mean that every internal node has *N* slots, wasting memory on nodes with fewer children. 371 | 372 | ### Code Example: N-ary Tree Node 373 | 374 | Here is the Python code: 375 | 376 | ```python 377 | class Node: 378 | def __init__(self, data, children=None): 379 | self.data = data 380 | self.children = children if children else [] 381 | 382 | def add_child(self, child): 383 | self.children.append(child) 384 | ``` 385 | 386 | ### Use of N-ary Trees 387 | 388 | 1. **File Systems**: Represent directories and files, where a directory can have multiple subdirectories or files. 389 | 2. **Abstract Syntax Trees (ASTs)**: Used in programming languages to represent the structure of source code. A node in the AST can correspond to various constructs in the code, such as an expression, statement, or declaration. 390 | 3. **Multi-way Trees**: Employed in the management of organized data, particularly in the **index structures** of databases or data warehouses. 391 | 392 | 4. **User Interfaces**: Structures with multiple child components, like list views, trees, or tabbed panels, exemplify the role of n-ary trees in this domain. 393 | 5. **Data Analytics and Machine Learning**: Classification and decision-making processes often entail using multi-way trees, such as **N-ary decision trees**. 394 |
395 | 396 | ## 8. Discuss the properties of a _Full Binary Tree_. 397 | 398 | A **Full Binary Tree**, often referred to as a "strictly binary tree," is a rich mathematical structure that presents a distinctive set of characteristics. 399 | 400 | It is a **tree data structure** in which each node has either zero or two children. Every **leaf node** is found at the same **level**, and the tree is perfectly balanced, reaching its most compact organizational form. 401 | 402 | ### Full Binary Tree Properties 403 | 404 | #### Node Count 405 | - The number of nodes in a Full Binary Tree, $N$, is odd for a finite tree due to the one-root node. 406 | - With $N+1$ nodes, a Full Binary Tree may still be complete (not full). 407 | - The $n$th level holds between $\frac{n}{2}+1$ and $n$ nodes, with all levels either full or skipping just the last node. 408 | 409 | #### Relationship between Node Count and Tree Height 410 | - A Full Binary Tree's height, $h$, can range from $\log_2(N+1) - 1$ (for balanced trees) to $N - 1$ (for degenerate trees). 411 | 412 | #### External Nodes (Leaves) 413 | - The number of leaves, $L$, is: 414 | - even when $N$ is one less than a power of two. 415 | - odd when $N$ is equal to a power of two. 416 | - A Full Binary Tree always has one more non-leaf node than leaf nodes. 417 | 418 | #### Parent-Child Relationship 419 | 420 | - The parent of the $n$th node in a Full Binary Tree is given by: 421 | 422 | $$ \text{parent}(n) = \Bigg \lceil \frac{n}{2} \Bigg \rceil - 1 $$ 423 | 424 | - The $n$th node's children are at the $2n+1$ and $2n+2$ positions, respectively. 425 | 426 | - Parent and child relationships are computationally efficient in Full Binary Trees due to direct relationships without needing to search or iterate. 427 | 428 | #### Expression Evaluation & Parentheses Distribution 429 | 430 | - Full Binary Trees excel at two commonly encountered applications: 431 | 1. Efficient expression evaluation, especially arithmetic expressions. 432 | 2. Parentheses management, commonly employed for nested logic or mathematical expressions. These associations are usually implemented using Binary Operators. 433 | 434 | #### Array Representation 435 | 436 | With position $n$ adjusting from 0: 437 | - The root is at index 0. 438 | - The left child of node $n$ is at index $2n+1$. 439 | - The right child of node $n$ is at index $2n+2$. 440 |
441 | 442 | ## 9. What is the significance of the _degree of a node_ in a tree? 443 | 444 | The **degree of a tree** is determined by its most prominent node's degree, which is also the maximum degree of any of its nodes. 445 | 446 | In practical terms, the degree of a tree provides insights into its structure, with numerous applications in computer science, networking, and beyond. 447 | 448 | ### Degree of a Node 449 | 450 | The **degree of a node** in a tree is the count of its children, often referred to simply as "children" or "subtrees". Nodes are categorized based on their degrees: 451 | 452 | - **Leaf nodes** have a degree of zero as they lack children. 453 | - **Non-terminal or internal nodes** (which are not leaves) have a degree greater than zero. 454 | 455 | In tree nomenclature, an internal node with $k$ children is called a node of degree $k$. For example, a node with three children is a node of degree 3. 456 | 457 | ### Code Example: Node Degree 458 | 459 | Here is the Python code: 460 | 461 | ```python 462 | # Node with degree 3 463 | class NodeDegree3: 464 | def __init__(self, children): 465 | self.children = children 466 | 467 | node_degree_3 = NodeDegree3([1, 2, 3]) # Example of a node with degree 3 468 | 469 | # Node with degree 0 470 | class NodeDegree0: 471 | def __init__(self, value): 472 | self.value = value 473 | 474 | node_degree_0 = NodeDegree0(5) # Example of a node with degree 0 475 | ``` 476 | 477 | ### Tree Degrees 478 | 479 | The **degree of a tree** is the maximum of the degrees of its nodes. Every individual node’s degree is less than or equal to the tree degree. 480 | 481 | By extension, if a tree's maximum degree is $k$, then: 482 | 483 | - Each level in the tree contains at most $k$ nodes. 484 | - The number of leaves at any level $h$ (with $h < k$) is at most $1 + 1 + 1 + \ldots + 1 = k$. 485 | 486 | The above properties show how the degree of a tree provides a powerful handle on its structure. 487 | 488 | ### Code Example: Tree Degree 489 | 490 | Here is the Python code: 491 | 492 | ```python 493 | # Tree 494 | class Tree: 495 | def __init__(self, root): 496 | self.root = root 497 | 498 | def get_degree(self): 499 | def get_node_degree(node): 500 | if not node: 501 | return 0 502 | return len(node.children) 503 | 504 | max_degree = 0 505 | nodes_to_process = [self.root] 506 | 507 | while nodes_to_process: 508 | current_node = nodes_to_process.pop(0) 509 | if current_node: 510 | current_degree = get_node_degree(current_node) 511 | max_degree = max(max_degree, current_degree) 512 | nodes_to_process.extend(current_node.children) 513 | 514 | return max_degree 515 | 516 | # Define a tree with root and nodes as per requirements and, then you can find the degree of the tree using the get_degree method 517 | # tree = Tree(...) 518 | 519 | # Example: tree.get_degree() will give you the degree of the tree 520 | ``` 521 |
522 | 523 | ## 10. Explain the concept of a _Path_ in a tree. 524 | 525 | A **path** in a tree is a sequence of connected nodes representing a traversal from one node to another. The path can be directed – from the root to a specific node – or undirected. It can also be the shortest distance between two nodes, often called a **geodesic path**. Several types of paths exist in trees, such as a **Downward Path**, a **Rooted Tree Path**, and an **Unrooted Tree Path**. 526 | 527 | ### Path Types 528 | 529 | #### Downward Path 530 | This type of path travels from a node to one of its descendants, and each edge in the path is in the same direction. 531 | 532 | #### Upward Path 533 | This is the reversed variant of a Downward Path, which goes from a node to one of its ancestors. 534 | 535 | #### Rooted Tree Paths 536 | These types of paths connect nodes starting from the root. Paths may originate from root and end in any other node. When paths move from the root to a specific node, they're often called **ancestral paths**. 537 | 538 | #### Unrooted Tree Paths 539 | Contrary to Rooted Tree Paths, Unrooted Tree Paths can be considered in rooted trees but not binary trees. They do not necessarily involve the root. 540 | 541 | #### Specific Tree Path Types 542 | - **Siblings**: Connects two sibling nodes or nodes that are children of the same parent. 543 | - **Ancestor-Descendant**: Represents a relationship between an ancestor and a descendant node. 544 | - **Prefix-Suffix**: These paths are specifically defined for binary trees, and they relate nodes in the tree based on their arrangement in terms of children from a particular node or based on their position in the binary tree. 545 | 546 | 547 | ### Code Example: Identifying Path Types 548 | 549 | Here is the Python code: 550 | 551 | ```python 552 | class Node: 553 | def __init__(self, value): 554 | self.value = value 555 | self.children = [] 556 | 557 | def path_from_root(node): 558 | path = [node.value] 559 | while node.parent: 560 | node = node.parent 561 | path.append(node.value) 562 | return path[::-1] 563 | 564 | def find_direction(node, child_value): 565 | return "down" if any(c.value == child_value for c in node.children) else "up" 566 | 567 | # Sample usage 568 | root = Node("A") 569 | root.children = [Node("B"), Node("C")] 570 | root.children[0].children = [Node("D"), Node("E")] 571 | root.children[1].children = [Node("F")] 572 | 573 | # Path 'A' -> 'B' -> 'E' is a Downward Path 574 | print([n.value for n in path_from_root(root.children[0].children[1])]) 575 | # Output: ['A', 'B', 'E'] 576 | 577 | # Path 'C' -> 'F' is a Sibling Path (Downward Path constrained to siblings) 578 | print(find_direction(root, "F")) 579 | # Output: down 580 | ``` 581 |
582 | 583 | ## 11. What is a _Binary Search Tree_ (BST)? 584 | 585 | A **Binary Search Tree** (BST) is a binary tree optimized for quick lookup, insertion, and deletion operations. A BST has the distinct property that each node's left subtree contains values smaller than the node, and its right subtree contains values larger. 586 | 587 | ### Key Characteristics 588 | 589 | - **Sorted Elements**: Enables efficient searching and range queries. 590 | - **Recursive Definition**: Each node and its subtrees also form a BST. 591 | - **Unique Elements**: Generally, BSTs do not allow duplicates, although variations exist. 592 | 593 | ### Visual Representation 594 | 595 | ![Binary Tree vs BST](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/binary%20tree%2Fvalid-binary-search-tree-example.png?alt=media&token=5821a405-7991-4c92-976b-b187a5a25fe3) 596 | 597 | ### Formal Properties 598 | 599 | For any node $N$ in the BST: 600 | 601 | $$ 602 | $$ 603 | \forall L \in \text{Left-Subtree}(N) & : \text{Value}(L) < \text{Value}(N) \\ 604 | \forall R \in \text{Right-Subtree}(N) & : \text{Value}(R) > \text{Value}(N) 605 | $$ 606 | $$ 607 | 608 | ### Practical Applications 609 | 610 | - **Databases**: Used for efficient indexing. 611 | - **File Systems**: Employed in OS for file indexing. 612 | - **Text Editors**: Powers auto-completion and suggestions. 613 | 614 | ### Time Complexity 615 | 616 | - **Search**: $O(\log n)$ in balanced trees; $O(n)$ in skewed trees. 617 | - **Insertion**: Averages $O(\log n)$; worst case is $O(n)$. 618 | - **Deletion**: Averages $O(\log n)$; worst case is $O(n)$. 619 | 620 | ### Code Example: Validating a BST 621 | 622 | Here is the Python code: 623 | 624 | ```python 625 | def is_bst(node, min=float('-inf'), max=float('inf')): 626 | if node is None: 627 | return True 628 | if not min < node.value < max: 629 | return False 630 | return (is_bst(node.left, min, node.value) and 631 | is_bst(node.right, node.value, max)) 632 | ``` 633 |
634 | 635 | ## 12. Explain the difference between a _Binary Tree_ and a _Binary Search Tree_ (BST). 636 | 637 | While **Binary Trees** and **Binary Search Trees** (BSTs) share a tree-like structure, they are differentiated by key features such as node ordering and operational efficiency. 638 | 639 | ### Key Distinctions 640 | 641 | #### Node Ordering 642 | 643 | - **Binary Tree**: No specific ordering rules between parent and child nodes. 644 | - **BST**: Nodes are ordered—left children are smaller, and right children are larger than the parent node. 645 | 646 | #### Efficiency in Searching 647 | 648 | - **Binary Tree**: $O(n)$ time complexity due to the need for full traversal in the worst case. 649 | - **BST**: Improved efficiency with $O(\log n)$ time complexity in balanced trees. 650 | 651 | #### Node Insertion and Deletion 652 | 653 | - **Binary Tree**: Flexible insertion without constraints. 654 | - **BST**: Ordered insertion and deletion to maintain the tree's structure. 655 | 656 | #### Tree Balancing 657 | 658 | - **Binary Tree**: Generally, balancing is not required. 659 | - **BST**: Balancing is crucial for optimized performance. 660 | 661 | #### Use Cases 662 | 663 | - **Binary Tree**: Often used in heaps, tries, and tree traversal algorithms. 664 | - **BST**: Commonly used in dynamic data handling scenarios like maps or sets in standard libraries. 665 | 666 | ### Visual Comparison 667 | 668 | #### Binary Tree 669 | 670 | In this **Binary Tree**, there's no specific ordering. For instance, 6 is greater than its parent node, 1, but is on the left subtree. 671 | 672 | ```plaintext 673 | 5 674 | / \ 675 | 1 8 676 | / \ 677 | 6 3 678 | ``` 679 | 680 | #### Binary Search Tree 681 | 682 | Here, the **Binary Search Tree** maintains the ordering constraint. All nodes in the left subtree (3, 1) are less than 5, and all nodes in the right subtree (8) are greater than 5. 683 | 684 | ```plaintext 685 | 5 686 | / \ 687 | 3 8 688 | / \ 689 | 1 4 690 | ``` 691 | 692 | ### Key Takeaways 693 | 694 | - **BSTs** offer enhanced efficiency in lookups and insertions. 695 | - **Binary Trees** provide more flexibility but can be less efficient in searches. 696 | - Both trees are comparable in terms of memory usage. 697 |
698 | 699 | ## 13. What is a _Complete Binary Tree_? 700 | 701 | The **Complete Binary Tree** (CBT) strikes a balance between the stringent hierarchy of full binary trees and the relaxed constraints of general trees. In a CBT, all levels, except possibly the last, are completely filled with nodes, which are as far left as possible. 702 | 703 | This structural constraint makes CBTs amenable for array-based representations with efficient storage and speeding up operations like insertions by maintaining the complete level configuration. 704 | 705 | ### Visual Representation 706 | 707 | ![Complete Binary Tree](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/strings%2Fcomplete-binary-tree.webp?alt=media&token=dd12edf8-a3cc-44f9-ae6e-5481968d75aa) 708 | 709 | ### Characteristics 710 | 711 | - A binary tree is "complete" if, for every level $l$ less than the **height $h$** of the tree: 712 | - **All** of its nodes are at the **leftmost side** at level $l$ (When the left side of level $l$ is filled, and the level $l-1$ is complete). 713 | - **None** of these nodes are at a deeper level. 714 | 715 | ### Key Properties 716 | 717 | - A CBT has a minimal **possible height** for a given number of nodes, $n$. This height ranges from $\lfloor \log_2(n) \rfloor$ to $\lceil \log_2(n) \rceil$. 718 | - Behaviorally, except for the last level, a CBT behaves like a _full binary tree_. 719 | - The **number of levels** in a CBT is either the **height** $h$ or $h-1$. The tree is traversed until one of these levels. 720 | 721 | ### Examples 722 | 723 | In both "Before" and "After" trees, the properties of being "complete" and the behavior of the last level are consistently maintained. 724 | 725 | #### Before -> complete 726 | ```plaintext 727 | A **Level 0** (height - 0) 728 | / \ 729 | B C **Level 1** (height - 1) 730 | / \ / \ 731 | D E f G **Level 2** (height - 2) 732 | 733 | The height of this tree is 2. 734 | ``` 735 | 736 | #### After -> complete 737 | 738 | ```plaintext 739 | A **Level 0** (height - 0) 740 | / \ 741 | B C **Level 1** (height - 1) 742 | / \ / \ 743 | D E F G **Level 2** (height - 2) 744 | / \ 745 | H I **level 3** (height 3) 746 | 747 | The height of this tree is 3. 748 | ``` 749 | 750 | ### Visual Inspection for Completeness 751 | 752 | Here are some guidelines for identifying whether a given $binary tree$ is "complete": 753 | 754 | - Work from the root, keeping track of the **last encountered node**. 755 | - At each level: 756 | - If a node is **empty**, it and all its children should be the **last nodes** seen 757 | - If a node is **non-empty**, add its children to the **queue of nodes to be inspected.** 758 | 759 | - Continue this process: 760 | - either you'll reach the end of the tree (identified as complete so far) 761 | - or you'll find a level for which "completeness" is violated. 762 | 763 | If the latter is the case, the tree is not "complete." 764 | 765 | #### Code example: Complete Binary Tree Verification 766 | 767 | Here is the Python code: 768 | 769 | ```python 770 | def is_complete(root): 771 | if root is None: 772 | return True 773 | 774 | is_leaf = lambda node: not (node.left or node.right) 775 | 776 | queue = [root] 777 | while queue: 778 | current = queue.pop(0) 779 | if current.left: 780 | if is_leaf(current.left) and current.right: 781 | return False 782 | queue.append(current.left) 783 | if current.right: 784 | if is_leaf(current.right): 785 | return False 786 | queue.append(current.right) 787 | return True 788 | ``` 789 |
790 | 791 | ## 14. Define a _Perfect Binary Tree_ and its characteristics. 792 | 793 | A **Perfect Binary Tree**, also known as a **strictly binary tree**, is a type of **binary tree** where each internal node has exactly two children, and all leaf nodes are at the same level. 794 | 795 | The tree is "full" or "complete" at every level, and the number of nodes in the tree is $2^{h+1} - 1$, where $h$ is the height of the tree. Each level $d$ of the tree contains $2^d$ nodes. 796 | 797 | ### Characteristics 798 | 799 | - **Node Count**: $2^{h+1} - 1$ nodes. 800 | - **Level of Nodes**: All levels, apart from the last, are completely filled. 801 | - **Height-Node Relationship**: A perfect binary tree's height $h$ is given by $\log_2 (n+1) - 1$ and vice versa. 802 | 803 | ### Visual Representation 804 | 805 | ![Perfect Binary Tree](https://firebasestorage.googleapis.com/v0/b/dev-stack-app.appspot.com/o/strings%2Fperfect-binary-tree.webp?alt=media&token=6793c489-29a4-46c5-b9a4-ddbb52c7e415) 806 | 807 | ### Code Example: Checking for Perfect Binary Tree 808 | 809 | Here is the Python code: 810 | 811 | ```python 812 | # Helper function to calculate the height of the tree 813 | def tree_height(root): 814 | if root is None: 815 | return -1 816 | left_height = tree_height(root.left) 817 | right_height = tree_height(root.right) 818 | return 1 + max(left_height, right_height) 819 | 820 | # Function to check if the tree is perfect 821 | def is_perfect_tree(root): 822 | height = tree_height(root) 823 | node_count = count_nodes(root) 824 | return node_count == 2**(height+1) - 1 825 | ``` 826 |
827 | 828 | ## 15. Explain what a _Degenerate (or Pathological) Tree_ is and its impact on operations. 829 | 830 | A **Degenerate Tree** refers to a tree structure where each parent node has only one associated child node. Consequently, the tree effectively becomes a linked list. 831 | 832 | ### Tree Traversal Efficiency 833 | 834 | The nature of degenerate trees directly influences traversal efficiency: 835 | 836 | - **In-Order**: Optimal only for a sorted linked list. 837 | - **Pre-Order and Post-Order**: In these lists, trees are consistently better. Thus, pre-order and post-order strategies remain dependable. 838 | - **Level-Order (BFS)**: This method accurately depicts tree hierarchy, rendering it robust. Nonetheless, it may demand excessive memory for large trees. 839 | 840 | ### Applications 841 | 842 | While degenerate trees might seem limited, they offer utility in various contexts: 843 | 844 | - **Text Parsing**: They are fundamental in efficient string searches and mutable string operations. 845 | - **Arithmetic Expression Trees**: Serve as the basis for implementing mathematical formulae due to their linear property. 846 | - **Database Indexing**: Prerequisite for rapid and indexed I/O operations in databases. 847 | 848 | ### Commonly Used Techniques 849 | 850 | Several strategies mitigate challenges posed by degenerate trees: 851 | 852 | - **Rebalancing**: Techniques such as "AVL Trees" and "Red-Black Trees" facilitate periodic restoration of tree balance. 853 | - **Perfect Balancing**: Schemes like "Full k-Ary Trees" adjust branches or bind multiple nodes to a single parent, restoring balance. 854 | - **Multiway Trees**: Tactics involving trees with multiple children per node (e.g., B-Trees) can offset tree linearization. 855 | 856 | ### Code Example: Degenerate Tree 857 | 858 | Here is the Python code: 859 | 860 | ```python 861 | class Node: 862 | def __init__(self, value): 863 | self.value = value 864 | self.left = None 865 | self.right = None 866 | 867 | # Create a degenerate tree 868 | root = Node(1) 869 | root.left = Node(2) 870 | root.left.left = Node(3) 871 | root.left.left.left = Node(4) 872 | ``` 873 |
874 | 875 | 876 | 877 | #### Explore all 100 answers here 👉 [Devinterview.io - Tree Data Structure](https://devinterview.io/questions/data-structures-and-algorithms/tree-data-structure-interview-questions) 878 | 879 |
880 | 881 | 882 | data-structures-and-algorithms 883 | 884 |

885 | 886 | --------------------------------------------------------------------------------