Skip to content

Added Breadth First Tree Traversal #364

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Oct 29, 2022
93 changes: 93 additions & 0 deletions Algorithms.Tests/Graph/BreadthFirstTreeTraversalTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Algorithms.Graph;
using NUnit.Framework;
using DataStructures.BinarySearchTree;
using System;

namespace Algorithms.Tests.Graph
{
public static class BreadthFirstTreeTraversalTests
{
[Test]
public static void CorrectLevelOrderTraversal()
{
// Arrange
int[] correctPath = { 7, 4, 13, 2, 5, 11, 15, 14, 16 };
int[] insertionOrder = { 7, 13, 11, 15, 14, 4, 5, 16, 2 };
BinarySearchTree<int> testTree = new BinarySearchTree<int>();
foreach (int data in insertionOrder)
{
testTree.Add(data);
}

// Act
int[] levelOrder = BreadthFirstTreeTraversal<int>.LevelOrderTraversal(testTree);

// Assert
Assert.AreEqual(levelOrder, correctPath);
}

[Test]
public static void EmptyArrayForNullRoot()
{
// Arrange
BinarySearchTree<int> testTree = new BinarySearchTree<int>();

// Act
int[] levelOrder = BreadthFirstTreeTraversal<int>.LevelOrderTraversal(testTree);

// Assert
Assert.IsEmpty(levelOrder);
}

[Test]
[TestCase(new [] {7, 9, 5})]
[TestCase(new [] { 7, 13, 11, 15, 14, 4, 5, 16, 2 })]
public static void IncorrectLevelOrderTraversal(int[] insertion)
{
// Arrange
BinarySearchTree<int> testTree = new BinarySearchTree<int>();
foreach (int data in insertion)
{
testTree.Add(data);
}

// Act
int[] levelOrder = BreadthFirstTreeTraversal<int>.LevelOrderTraversal(testTree);

// Assert
Assert.AreNotEqual(levelOrder, insertion);
}

[Test]
public static void DeepestNodeInTree()
{
// Arrange
BinarySearchTree<int> testTree = new BinarySearchTree<int>();
int[] insertion = { 7, 13, 11, 15, 4, 5, 12, 2, 9 };
foreach (int data in insertion)
{
testTree.Add(data);
}

// Act
int deepest = BreadthFirstTreeTraversal<int>.DeepestNode(testTree);

// Assert
Assert.AreEqual(12, deepest);
}

[Test]
public static void DeepestNodeOfEmptyTree()
{
// Arrange
BinarySearchTree<int?> testTree = new BinarySearchTree<int?>();

// Act
int? deepest = BreadthFirstTreeTraversal<int?>.DeepestNode(testTree);

// Assert
Assert.IsNull(deepest);
}
}
}

90 changes: 90 additions & 0 deletions Algorithms/Graph/BreadthFirstTreeTraversal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using DataStructures.BinarySearchTree;

namespace Algorithms.Graph
{
/// <summary>
/// Breadth first tree traversal traverses through a binary tree
/// by iterating through each level first.
/// time complexity: O(n).
/// space complexity: O(w) where w is the max width of a binary tree.
/// </summary>
/// <typeparam name="TKey">Type of key held in binary search tree.</typeparam>
public static class BreadthFirstTreeTraversal<TKey>
{
/// <summary>
/// Level Order Traversal returns an array of integers in order
/// of each level of a binary tree. It uses a queue to iterate
/// through each node following breadth first search traversal.
/// </summary>
/// <param name="tree">Passes the binary tree to traverse.</param>
/// <returns>Returns level order traversal.</returns>
public static TKey[] LevelOrderTraversal(BinarySearchTree<TKey> tree)
{
BinarySearchTreeNode<TKey>? root = tree.Root;
TKey[] levelOrder = new TKey[tree.Count];
if (root is null)
{
return Array.Empty<TKey>();
}

Queue<BinarySearchTreeNode<TKey>> breadthTraversal = new Queue<BinarySearchTreeNode<TKey>>();
breadthTraversal.Enqueue(root);
for (int i = 0; i < levelOrder.Length; i++)
{
BinarySearchTreeNode<TKey> current = breadthTraversal.Dequeue();
levelOrder[i] = current.Key;
if (current.Left is not null)
{
breadthTraversal.Enqueue(current.Left);
}

if (current.Right is not null)
{
breadthTraversal.Enqueue(current.Right);
}
}

return levelOrder;
}

/// <summary>
/// Deepest Node return the deepest node in a binary tree. If more
/// than one node is on the deepest level, it is defined as the
/// right-most node of a binary tree. Deepest node uses breadth
/// first traversal to reach the end.
/// </summary>
/// <param name="tree">Tree passed to find deepest node.</param>
/// <returns>Returns the deepest node in the tree.</returns>
public static TKey? DeepestNode(BinarySearchTree<TKey> tree)
{
BinarySearchTreeNode<TKey>? root = tree.Root;
if (root is null)
{
return default(TKey);
}

Queue<BinarySearchTreeNode<TKey>> breadthTraversal = new Queue<BinarySearchTreeNode<TKey>>();
breadthTraversal.Enqueue(root);
TKey deepest = root.Key;
while (breadthTraversal.Count > 0)
{
BinarySearchTreeNode<TKey> current = breadthTraversal.Dequeue();
if (current.Left is not null)
{
breadthTraversal.Enqueue(current.Left);
}

if (current.Right is not null)
{
breadthTraversal.Enqueue(current.Right);
}

deepest = current.Key;
}

return deepest;
}
}
}
42 changes: 21 additions & 21 deletions DataStructures/BinarySearchTree/BinarySearchTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,20 @@ public class BinarySearchTree<TKey>
private readonly Comparer<TKey> comparer;

/// <summary>
/// The root of the BST.
/// Gets the root of the BST.
/// </summary>
private BinarySearchTreeNode<TKey>? root;
public BinarySearchTreeNode<TKey>? Root { get; private set; }

public BinarySearchTree()
{
root = null;
Root = null;
Count = 0;
comparer = Comparer<TKey>.Default;
}

public BinarySearchTree(Comparer<TKey> customComparer)
{
root = null;
Root = null;
Count = 0;
comparer = customComparer;
}
Expand All @@ -56,13 +56,13 @@ public BinarySearchTree(Comparer<TKey> customComparer)
/// </exception>
public void Add(TKey key)
{
if (root is null)
if (Root is null)
{
root = new BinarySearchTreeNode<TKey>(key);
Root = new BinarySearchTreeNode<TKey>(key);
}
else
{
Add(root, key);
Add(Root, key);
}

Count++;
Expand All @@ -86,14 +86,14 @@ public void AddRange(IEnumerable<TKey> keys)
/// </summary>
/// <param name="key">The key to search for.</param>
/// <returns>The node with the specified key if it exists, otherwise a default value is returned.</returns>
public BinarySearchTreeNode<TKey>? Search(TKey key) => Search(root, key);
public BinarySearchTreeNode<TKey>? Search(TKey key) => Search(Root, key);

/// <summary>
/// Checks if the specified key is in the BST.
/// </summary>
/// <param name="key">The key to search for.</param>
/// <returns>true if the key is in the BST, false otherwise.</returns>
public bool Contains(TKey key) => Search(root, key) is not null;
public bool Contains(TKey key) => Search(Root, key) is not null;

/// <summary>
/// Removes a node with a key that matches <paramref name="key" />.
Expand All @@ -102,12 +102,12 @@ public void AddRange(IEnumerable<TKey> keys)
/// <returns>true if the removal was successful, false otherwise.</returns>
public bool Remove(TKey key)
{
if (root is null)
if (Root is null)
{
return false;
}

var result = Remove(root, root, key);
var result = Remove(Root, Root, key);
if (result)
{
Count--;
Expand All @@ -122,12 +122,12 @@ public bool Remove(TKey key)
/// <returns>The node if possible, a default value otherwise.</returns>
public BinarySearchTreeNode<TKey>? GetMin()
{
if (root is null)
if (Root is null)
{
return default;
}

return GetMin(root);
return GetMin(Root);
}

/// <summary>
Expand All @@ -136,31 +136,31 @@ public bool Remove(TKey key)
/// <returns>The node if possible, a default value otherwise.</returns>
public BinarySearchTreeNode<TKey>? GetMax()
{
if (root is null)
if (Root is null)
{
return default;
}

return GetMax(root);
return GetMax(Root);
}

/// <summary>
/// Returns all the keys in the BST, sorted In-Order.
/// </summary>
/// <returns>A list of keys in the BST.</returns>
public ICollection<TKey> GetKeysInOrder() => GetKeysInOrder(root);
public ICollection<TKey> GetKeysInOrder() => GetKeysInOrder(Root);

/// <summary>
/// Returns all the keys in the BST, sorted Pre-Order.
/// </summary>
/// <returns>A list of keys in the BST.</returns>
public ICollection<TKey> GetKeysPreOrder() => GetKeysPreOrder(root);
public ICollection<TKey> GetKeysPreOrder() => GetKeysPreOrder(Root);

/// <summary>
/// Returns all the keys in the BST, sorted Post-Order.
/// </summary>
/// <returns>A list of keys in the BST.</returns>
public ICollection<TKey> GetKeysPostOrder() => GetKeysPostOrder(root);
public ICollection<TKey> GetKeysPostOrder() => GetKeysPostOrder(Root);

/// <summary>
/// Recursive method to add a key to the BST.
Expand Down Expand Up @@ -261,7 +261,7 @@ private bool Remove(BinarySearchTreeNode<TKey>? parent, BinarySearchTreeNode<TKe
else
{
var predecessorNode = GetMax(node.Left);
Remove(root, root, predecessorNode.Key);
Remove(Root, Root, predecessorNode.Key);
replacementNode = new BinarySearchTreeNode<TKey>(predecessorNode.Key)
{
Left = node.Left,
Expand All @@ -271,9 +271,9 @@ private bool Remove(BinarySearchTreeNode<TKey>? parent, BinarySearchTreeNode<TKe

// Replace the relevant node with a replacement found in the previous stages.
// Special case for replacing the root node.
if (node == root)
if (node == Root)
{
root = replacementNode;
Root = replacementNode;
}
else if (parent.Left == node)
{
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ find more than one implementation for the same objective but using different alg
* [Minimum Spanning Tree](./Algorithms/Graph/MinimumSpanningTree)
* [Prim's Algorithm (Adjacency Matrix)](./Algorithms/Graph/MinimumSpanningTree/PrimMatrix.cs)
* [Kruskal's Algorithm](./Algorithms/Graph/MinimumSpanningTree/Kruskal.cs)
* [BreadthFirstTreeTraversal](./Algorithms/Graph/BreadthFirstTreeTraversal.cs)
* [BreadthFirstSearch](./Algorithms/Graph/BreadthFirstSearch.cs)
* [DepthFirstSearch](./Algorithms/Graph/DepthFirstSearch.cs)
* [Dijkstra Shortest Path](./Algorithms/Graph/Dijkstra/DijkstraAlgorithm.cs)
Expand Down