You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: slides/graphtheory/trees.txt
+8-8Lines changed: 8 additions & 8 deletions
Original file line number
Diff line number
Diff line change
@@ -25,13 +25,13 @@ We now know what trees are, but where do they appear in computer science and in
25
25
26
26
Now we need to talk about how we actually store and represent these undirected trees.
27
27
28
-
First, you will need to label the nodes of your tree by indexing it, preferably from 0 to n non-inclusive like the one on the left of this slide.
28
+
First, you should label the nodes of your tree by indexing them from 0 to n non-inclusive like the tree on the left of this slide.
29
29
30
30
A simple way to store a tree is as an edge list, which is simply a list of undirected edges indicating which two nodes have an edge between them. The great thing about this representation is that it's super fast to iterate over and cheap to store.
31
31
32
32
The downside however, is that storing your tree as a list lacks the structure to efficiently query all the neighbors of a node.
33
33
34
-
This is why the adjacency list is usually a more popular representation to store an undirected tree. In this representation, you store a mapping between a node and all its neighbors.
34
+
This is why the adjacency list is usually a more popular representation for storing an undirected tree. In this representation, you store a mapping between a node and all its neighbors.
35
35
36
36
For example, node 4 has the neighbors 1, 5 and 8 so in the adjacency list, node 4 maps to the list containing 1, 5 and 8 respectively.
37
37
@@ -40,25 +40,25 @@ You could also store a tree as an adjacency matrix of size n by n where having a
40
40
However, in practice I would say to always avoid storing a tree as an adjacency matrix because it's a huge waste of space. You would not ever want to allocate n squared memory and only use roughly 2n of the matrix cells, it just doesn't make sense.
41
41
42
42
43
-
Alright, I can't keep talking about trees without mentioning rooted trees which are trees with a designated root node. I have highlighted the root node in orange, and most rooted trees as you'll notice have directed edges which point away from the root node, however it's also possible to have edges which point towards the root node, but those trees are much rarer in my experience.
44
-
Generally speaking, rooted trees are far easier to work with than undirected trees because they have a well defined structure and allow for recursive algorithm implementations.
43
+
Alright, I can't keep talking about trees without mentioning rooted trees which are trees with a designated root node. I have highlighted the root node in orange, and most rooted trees as you'll notice have directed edges which point away from the root node, however it's also possible to have edges which point towards the root node, but those trees are much rarer from my experience.
44
+
Generally speaking, rooted trees are far easier to work with than undirected trees because they have this well defined structure that allows for easy recursive algorithm implementations.
45
45
46
46
Related to rooted trees are binary trees which are trees for which every node has at most two child nodes. The first two trees on this slide are binary trees, but the last one is not because it has a node with more than 2 child nodes.
47
47
You don't often see binary trees manifest themselves in the real world, for the most part binary trees are artificially created and integrated as part of data structures to guarantee efficient insertions, removals and access to data.
48
48
49
49
Now related to binary trees are binary search trees which are trees which satisfy the BST invariant. The BST invariant states that for every node x the values in the left subtree are less than or equal to x and that the values in the right subtree are greater than or equal to x. This nice little property enables you to quickly search through a tree and retrieve the values you want, which is particularly handy. All the trees on this slide are BSTs except for the last one, because 1 is *not* greater than or equal to 3.
50
50
51
-
It's often useful to require uniqueness on the values of your tree so that you don't end up with duplicate values in the tree. To resolve this issue of duplicate values you can change the invariant to be strictly less than rather than less than or equal to.
51
+
It's often useful to require uniqueness on the values of your binary search tree so that you don't end up with duplicate values. To resolve this issue of duplicate values you can change the invariant to be strictly less than rather than less than or equal to.
52
52
53
53
Now let's talk about how to store these rooted trees. Rooted trees are naturally defined recursively in a top down manner.
54
54
55
55
In practice, you always maintain a pointer reference to the root node so that you can access the tree and its contents.
56
56
57
57
Then each node also has access to a list of all its children -- which are also called "child nodes". In this slides, the orange node is the current node we have a reference to, and the purple nodes are all its children. All the bottom or leaf nodes don't have any children.
58
58
59
-
It's also sometimes useful to also maintain a pointer to a node’s parent node in case you need to traverse up the tree. This effectively makes the edges in the tree bidirectional. Again, if the current node is the orange node, than the pink node in the case in the parent node.
59
+
It's also sometimes useful to also maintain a pointer to a node’s parent node in case you need to traverse up the tree. This effectively makes the edges in the tree bidirectional. Again, if the current node is the orange node, than the pink node in the case in the parent node of the orange node.
60
60
61
-
However, maintaining an explicit reference to the parent node isn’t usually necessary because you can access a node’s parent on a recursive function's callback.
61
+
However, maintaining an explicit reference to the parent node isn’t usually necessary because you can access a node’s parent on a recursive function's callback as you pop frames off the stack.
62
62
63
63
64
64
Another really neat way of storing a rooted tree if it is a binary tree is in a flattened array.
@@ -72,7 +72,7 @@ mapped back to a unique position in the "index tree" as I call it.
72
72
73
73
In this format, the root node is always at index 0 in the array, so you always know where your starting point is. Another advantage of this format is that the child nodes of node i can be access relative to position i.
74
74
75
-
For example, if we're at position 2 in the array, we know that the left and right children of the node at index 2 is given by 2 times i plus 1 and 2 times i plus 2. Therefore the children of the node at index 2 can be found at positions 5 and 6. Reciprocally, this means if we have a node we know what the index of the parent node should be which is also very useful.
75
+
For example, if we're at position 2 in the array, we know that the left and right children of the node at index 2 is given by 2 times i plus 1 and 2 times i plus 2. Therefore the children of the node at index 2 can be found at positions 5 and 6. Reciprocally, this means if we have a node we know what the index of the parent node should be, which is also very useful.
76
76
77
77
Alright, that's all I have for this video, thank you for watching, please like and subscribe and I'll see you in the next one.
0 commit comments