This week while diving further into coding interview questions, I took a deeper look at data structures, particularly trees. In programming, trees are a non linear data structure, they instead maintain data in a hierarchical way. Similar to other structures however, they are comprised of a bunch of nodes that contain pieces of data. Every tree has a “root node” and from this root node, children nodes are branched off and connected by edges. Once we traverse to the bottom of a tree, these bottom level nodes are called leaves. You can picture this data structure as a real life tree but the properties are upside down, with the root being on the top vs the bottom. Every node, other than this root has one parent, however they can have a varying amount of children nodes. Children can then have sibling nodes which are horizontal to them, as well as grandparent nodes which are two levels up.
- Root — The top node in a tree.
- Child — A node directly connected to another node when moving away from the root.
- Parent — The converse notion of a child.
- Siblings — A group of nodes with the same parent.
- Leaf — A node with no children.
- Edge — The connection between one node and another.
Following the names of parent, sibling and child, a common real life example of this data structure is a family tree. Even if you aren’t familiar with trees, as a programmer you’ve likely worked with one before, the DOM. The DOM also organizes its data (the elements of a page) in a hierarchical way. There are, however, many different types of trees in programming and in this article I will quickly summarize some of the most common Binary trees. The different types of trees we’ll discuss include Binary Search, AVL, and Red-Black trees. Each tree type has its own use case and distinctions.
Starting with the basic Binary tree, its main distinction is that each node can have, at most, 2 children nodes. This tree is one of the most common ways you’ll see data organized in a tree structure and all the other trees we’ll cover are subsets of this tree. A binary tree can be considered either complete, full or perfect. A perfect binary tree is when all nodes have 2 children nodes and all leaves end at the same level. An example of a complete binary tree has every level other than maybe the last completely filled. In a full binary tree every node has either 0 or 2 children. A binary tree is the most basic form of a tree data structure. and there are many more types than just the 3 we’ll cover.
A binary search tree (BST) is an extension of a binary tree with additional restrictions. Each node still has a max of two children like the binary tree, however the values and where they’re placed matter. The left child of the parent should always be less than or equal to that parent node. And vice verse for the right side, it should always be greater than or equal to the parent. The fact that the tree keeps its data organized makes searching operations entirely more efficient, hence the name binary search.
An AVL tree is a self balancing binary search tree. The name comes from the creators Adelson-Velshi and Landis who helped create the first tree that balances dynamically. A balancing factor is added to each node in the tree based on if the tree is balanced or not (-1, 0, 1). In this tree, the heights of the two children subtrees can’t differ by more than 1. If the tree gains a new node and it causes the heights to differ by more, then it’ll be rotated to make sure the tree remains balanced. Operations such as access, removal and insertion with an AVL tree all maintain a good Big O runtime of O(log n). The tree is most beneficial when used for lookup operations.
Finally red-black trees are very similar to AVL trees. A red-black tree is also self balancing as well as a subset of the binary search tree. Each node in this tree is either red or black which is useful to ensure the tree remains somewhat balanced. When new nodes are inserted or deleted, the nodes will be rearranged to maintain its balance. The balancing isn’t perfect but allows us to still expect a decent runtime. This tree maintains a solid time complexity of O(log n).