Hello! I hope you are all enjoying your Memorial Day weekend. I am taking advantage of this long weekend to continue this series of posts regarding search algorithms. Last time, we discussed binary searches, and how these algorithms traverse down a binary search tree’s nodes to find a specified leaf. On the subject of tree traversal, I introduced in-order, post-order, and pre-order traversal methods that are commonly used when dealing with trees. Today I want to dive deeper, so to speak, into two widely used search methods for trees: _breadth-first search _and _depth-first search, _and use cases where one can be more optimal over another. Let’s get right into it.
Breadth-first search, or BFS, is a search algorithm that starts at the root of a binary search tree and traverses through each neighboring node, or rather, each child node, level-by-level. BFS visits each neighboring node in a level before advancing to the next depth level. In my previous article, I’ve shown how a binary search tree’s lookup() method can traverse through all left children before traversing all the right children. In this case, all children of the root level, which are one level below the root, will be checked, left to right, before the children at the next level down, and so on, until either we find our search value or we reach the lowest level of a tree where the nodes have no children, and the value isn’t found. We’ve seen this approach before when running lookup() algorithms, where the searched half of a tree was determined by the root’s value compared to the search value.
When running BFS, we can implement one of two options for node traversal. If a given search value is greater than a tree’s root node, then we can use post-order traversal to find the node value that equates to the former (remember that the search sequence for this is_ left, right, root_). If it is less than the root, we can use in-order traversal (sequence: root, left, right). As far as the search algorithm as a whole, we also have options. We can approach this with a brute force solution where we define the traversal order by iteratively adding the node values to a list using a queue to store the order to check for our search value. The other more widely used and optimal approach is done through recursion, using a callback function inside of itself (more on this later), and thus breaking down problems into smaller sub-problems that come together at rendering.
In the example above, BFS is executed by iterating through each node at the current level before moving deeper into the next level. This gives BFS, at worst, linear time complexity, O(n²), given the nested loops at each level. In terms of memory, BFS can be a better solution if we need to traverse a tree that is deeper than it is wide. Its traversal would be shorter than DFS in that situation. For example, say you need to find the nearest fast-food restaurant from a given location (More on BFS/DFS with graphs coming soon). Assuming you have Google Maps handy, it would be more optimal for Google Maps to check for the nearest restaurant based by checking for every block of distance, versus all the way down to the end of one street before moving on to any adjacent streets. In this kind of best-case scenario, BFS can run its operations in O(n) time. Wider trees would mean more memory would need to be allocated for traversing through each level of a tree.
#search #algorithms #trees