// 2.8 Given a circular linked list, implement an algorithm that returns the node at the
        // beginning of the loop.
        //     DEFINITION
        // Circular linked list: A (corrupt) linked list in which a node's next pointer points to an earlier node, so
        // as to make a loop in the linked list.
        //     EXAMPLE
        //     Input: A -> B -> C -> D -> E -> C [the same C as earlier]
        // Output: C
        public static int LoopDetection(LinkListNode root)
        {
            var set     = new HashSet <int>();
            var current = root;

            while (current != null && set.Add(current.Value))
            {
                current = current.Next;
            }

            return(current?.Value ?? -1);
        }
        // 2.4 Write code to partition a linked list around a value x, such that all nodes less than x come
        // before all nodes greater than or equal to x. If x is contained within the list, the values of x only need
        //     to be after the elements less than x (see below). The partition element x can appear anywhere in the
        // "right partition"; it does not need to appear between the left and right partitions.
        //     EXAMPLE
        // Input: 3 -> 5 -> 8 -> 5 -> 10 -> 2 -> 1 [partition= 5]
        // Output: 3 -> 1 -> 2 -> 10 -> 5 -> 5 -> 8
        public static LinkListNode Partition(LinkListNode root, int partition)
        {
            LinkListNode leftPartRoot     = null,
                         rightPartRoot    = null,
                         leftPartPointer  = null,
                         rightPartPointer = null;

            var current = root;

            while (current != null)
            {
                var next = current.Next;

                if (current.Value < partition)
                {
                    if (leftPartRoot == null)
                    {
                        leftPartRoot    = current;
                        leftPartPointer = current;
                    }
                    else
                    {
                        leftPartPointer.Next = current;
                        leftPartPointer      = leftPartPointer.Next;
                    }
                }
                else
                {
                    if (rightPartRoot == null)
                    {
                        rightPartRoot    = current;
                        rightPartPointer = current;
                    }
                    else
                    {
                        rightPartPointer.Next = current;
                        rightPartPointer      = rightPartPointer.Next;
                    }
                }

                current = next;
            }

            leftPartPointer.Next  = rightPartRoot;
            rightPartPointer.Next = null;

            return(leftPartRoot);
        }
        // 2.3 Implement an algorithm to delete a node in the middle (i.e., any node but
        //     the first and last node, not necessarily the exact middle) of a singly linked list, given only access to
        // that node.
        //     EXAMPLE
        // Input: the node c from the linked list a->b->c->d->e->f
        //     Result: nothing is returned, but the new linked list looks like a->b->d->e->f
        public static void DeleteTheMiddle(LinkListNode root)
        {
            var          current = root;
            var          runner  = root;
            LinkListNode prev    = null;

            while (runner != null)
            {
                runner  = runner.Next?.Next;
                prev    = current;
                current = current.Next;
            }

            //Remove Current
            prev.Next = current.Next;
        }
        // 2.1 Write code to remove duplicates from an unsorted linked list.
        //     FOLLOW UP
        //     How would you solve this problem if a temporary buffer is not allowed?
        public static void RemoveDuplicatesUsingSet(LinkListNode root)
        {
            var          set  = new HashSet <int>();
            var          node = root;
            LinkListNode prev = null;

            while (node != null)
            {
                if (!set.Add(node.Value))
                {
                    prev.Next = node.Next;
                }
                else
                {
                    prev = node;
                }
                node = node.Next;
            }
        }
        // 4.3 Given a binary tree, design an algorithm which creates a linked list of all the nodes
        // at each depth (e.g., if you have a tree with depth D, you'll have D linked lists).
        public static IEnumerable <LinkListNode> ConstructLinkListNodes(TreeNode root)
        {
            var list  = new List <LinkListNode>();
            var queue = new Queue <TreeNode>();

            queue.Enqueue(root);
            while (queue.Count != 0)
            {
                var          children     = new Queue <TreeNode>();
                LinkListNode rootListNode = null;
                var          current      = rootListNode;
                while (queue.Count != 0)
                {
                    var node = queue.Dequeue();
                    if (rootListNode == null)
                    {
                        rootListNode = new LinkListNode(node.Val);
                        current      = rootListNode;
                    }
                    else
                    {
                        current.Next = new LinkListNode(node.Val);
                        current      = current.Next;
                    }

                    if (node.Left != null)
                    {
                        children.Enqueue(node.Left);
                    }
                    if (node.Right != null)
                    {
                        children.Enqueue(node.Right);
                    }
                }

                list.Add(rootListNode);
                queue = children;
            }

            return(list);
        }
        // 2.2 Implement an algorithm to find the kth to last element of a singly linked list.
        public static int ReturnLast(LinkListNode root, int k)
        {
            var stack   = new Stack <int>();
            var current = root;

            while (current != null)
            {
                stack.Push(current.Value);
                current = current.Next;
            }

            int count = 1;

            while (count != k)
            {
                stack.Pop();
                count++;
            }

            return(stack.Pop());
        }
        public static int ReturnLastWithRecursion(LinkListNode root, int k)
        {
            var res = 0;

            Dfs(root);
            return(res);

            int Dfs(LinkListNode node)
            {
                if (node == null)
                {
                    return(0);
                }
                int depth = Dfs(node.Next) + 1;

                if (depth == k)
                {
                    res = node.Value;
                }
                return(depth);
            }
        }
        // 2.6 Implement a function to check if a linked list is a palindrome.
        public static bool IsPalindrome(LinkListNode root)
        {
            var fromStart = root;
            int?depth     = null;

            return(Dfs(root));

            bool Dfs(LinkListNode current, int dep = 0)
            {
                if (current.Next == null)
                {
                    depth = dep;
                    if (fromStart.Value != current.Value)
                    {
                        return(false);
                    }
                    fromStart = fromStart.Next;
                    return(true);
                }

                if (!Dfs(current.Next, dep + 1))
                {
                    return(false);
                }

                if (dep < depth / 2)
                {
                    return(true);
                }
                if (fromStart.Value != current.Value)
                {
                    return(false);
                }
                fromStart = fromStart.Next;

                return(true);
            }
        }
        // 2.5 You have two numbers represented by a linked list, where each node contains a single
        // digit. The digits are stored in reverse order, such that the 1 's digit is at the head of the list. Write a
        //     function that adds the two numbers and returns the sum as a linked list.
        //     EXAMPLE
        //     Input: (7-> 1 -> 6) + (5 -> 9 -> 2).That is,617 + 295.
        // Output: 2 -> 1 -> 9. That is, 912.
        // FOLLOW UP
        // Suppose the digits are stored in forward order. Repeat the above problem.
        //     EXAMPLE
        //     lnput:(6 -> 1 -> 7) + (2 -> 9 -> 5).That is,617 + 295.
        // Output: 9 -> 1 -> 2. That is, 912.
        public static int SumLists(LinkListNode root1, LinkListNode root2)
        {
            var builder1 = new StringBuilder();
            var builder2 = new StringBuilder();

            var current = root1;

            while (current != null)
            {
                builder1.Append(current.Value);
                current = current.Next;
            }

            current = root2;
            while (current != null)
            {
                builder2.Append(current.Value);
                current = current.Next;
            }

            return(int.Parse(string.Join("", builder1.ToString().Reverse())) +
                   int.Parse(string.Join("", builder2.ToString().Reverse())));
        }
        public static void RemoveDuplicatesWithoutTemporaryBuffer(LinkListNode root)
        {
            var current = root;

            while (current != null)
            {
                var runner = current.Next;
                var prev   = current;
                while (runner != null)
                {
                    if (runner.Value == current.Value)
                    {
                        prev.Next = runner.Next;
                    }
                    else
                    {
                        prev = runner;
                    }
                    runner = runner.Next;
                }

                current = current.Next;
            }
        }