// Use a level order traversal to get the max number of nodes at a level
        public static int GetMaxWidth(this BinarySearchTree.Node root)
        {
            if (root == null)
            {
                return(0);
            }

            var width    = 0;
            var maxWidth = width;
            var queue    = new Queue <BinarySearchTree.Node>();

            queue.Enqueue(root);
            while (queue.Count != 0)
            {
                width    = queue.Count;
                maxWidth = width > maxWidth ? width : maxWidth;

                // For each item in the queue, dequeue the current node and enqueue its children
                for (int i = 0; i < width; i++)
                {
                    var node = queue.Dequeue();
                    if (node.Left != null)
                    {
                        queue.Enqueue(node.Left);
                    }
                    if (node.Right != null)
                    {
                        queue.Enqueue(node.Right);
                    }
                }
            }

            return(maxWidth);
        }
        public static BinarySearchTree.Node BreadthFirstSearch(this BinarySearchTree.Node root, int dataToFind)
        {
            if (root == null)
            {
                return(null);
            }

            var queue = new Queue <BinarySearchTree.Node>();

            queue.Enqueue(root);
            while (queue.Count != 0)
            {
                var width = queue.Count;
                // For each item in the queue, dequeue the current node and enqueue its children
                for (int i = 0; i < width; i++)
                {
                    var node = queue.Dequeue();
                    if (node.Data == dataToFind)
                    {
                        return(node);
                    }
                    if (node.Left != null)
                    {
                        queue.Enqueue(node.Left);
                    }
                    if (node.Right != null)
                    {
                        queue.Enqueue(node.Right);
                    }
                }
            }

            // Return null if we didn't find anything
            return(null);
        }
        public static void GetInOrder(this BinarySearchTree.Node node, List <int> nodes)
        {
            if (node == null)
            {
                return;
            }

            node.Left.GetInOrder(nodes);
            nodes.Add(node.Data);
            node.Right.GetInOrder(nodes);
        }
        public static int GetMaxDepth(this BinarySearchTree.Node node)
        {
            if (node == null)
            {
                return(0);
            }

            var leftMax  = node.Left.GetMaxDepth();
            var rightMax = node.Right.GetMaxDepth();

            return(leftMax >= rightMax ? leftMax + 1 : rightMax + 1);
        }
        public static BinarySearchTree.Node DepthFirstSearch(this BinarySearchTree.Node root, int dataToFind)
        {
            if (root == null)
            {
                return(null);
            }

            // Create a table of visitied vertices
            var visited = new Dictionary <int, bool>();

            // Create a stack for DFS
            var stack = new Stack <BinarySearchTree.Node>();

            // Push the current source node
            stack.Push(root);

            while (stack.Count != 0)
            {
                // Pop a vertex from stack and print it
                var node = stack.Pop();

                // If we have found our vertex, return it
                if (node.Data == dataToFind)
                {
                    return(node);
                }

                // Add the current node to the visitied table since the stack can have
                // the same node twice
                if (!visited.ContainsKey(node.Data))
                {
                    visited.Add(node.Data, true);
                }

                // Get all child vertices of the current node.
                // If a adjacent has not been visited, then push it
                // to the stack.
                if (node.Left != null && !visited.ContainsKey(node.Left.Data))
                {
                    stack.Push(node.Left);
                }
                if (node.Right != null && !visited.ContainsKey(node.Right.Data))
                {
                    stack.Push(node.Right);
                }
            }

            return(null);
        }
        private static bool IsSameNodeAs(this BinarySearchTree.Node node, BinarySearchTree.Node otherNode)
        {
            if (node == null && otherNode == null)
            {
                return(true);
            }
            else if (node == null || otherNode == null)
            {
                return(false);
            }

            return(node.Data == otherNode.Data &&
                   node.Left.IsSameNodeAs(otherNode.Left) &&
                   node.Right.IsSameNodeAs(otherNode.Right));
        }
        public static bool IsSubTreeOf(this BinarySearchTree.Node subNode, BinarySearchTree.Node mainNode)
        {
            if (subNode == null)
            {
                return(true);
            }
            else if (mainNode == null)
            {
                return(false);
            }
            else if (subNode.IsSameNodeAs(mainNode))
            {
                return(true);
            }

            return(subNode.IsSubTreeOf(mainNode.Left) || subNode.IsSubTreeOf(mainNode.Right));
        }
        public static int Diameter(BinarySearchTree.Node node)
        {
            if (node == null)
            {
                return(0);
            }

            // Get the height of left and right sub trees
            int lHeight = node.Left.GetMaxDepth();
            int rHeight = node.Right.GetMaxDepth();

            // Get the diameter of left and right subtrees
            int lDiameter = Diameter(node.Left);
            int rDiameter = Diameter(node.Right);

            // Return max of following three
            // 1) Diameter of left subtree
            // 2) Diameter of right subtree
            // 3) Height of left subtree + height of right subtree + 1
            return(Math.Max(lHeight + rHeight + 1, Math.Max(lDiameter, rDiameter)));
        }