Thursday, August 19, 2021

[LeetCode] Maximum Product of Splitted Binary Tree

Problem: Given the root of a binary tree, split the binary tree into two subtrees by removing one edge such that the product of the sums of the subtrees is maximized.

Return the maximum product of the sums of the two subtrees. Since the answer may be too large, return it modulo 10^9 + 7.

Note that you need to maximize the answer before taking the mod and not after taking it.

Example:

Input: root = [1,2,3,4,5,6]
Output: 110
Explanation: Remove the red edge and get 2 binary trees with sum 11 and 10. Their product is 110 (11*10)

Input: root = [1,null,2,3,4,null,null,5,6]
Output: 90
Explanation: Remove the red edge and get 2 binary trees with sum 15 and 6.Their product is 90 (15*6)
Input: root = [2,3,9,10,7,8,6,5,4,11,1]
Output: 1025
Input: root = [1,1]
Output: 1

Constraints:

  1. The number of nodes in the tree is in the range [2, 5 * 104].
  2. 1 <= Node.val <= 104


Approach: We need to look at the problem in this way, say if we know the total sum of the tree and then at every node we can check if the sum of current sub tree * (total sum - sum of current sub tree) is greater than maximum product then we reassign the maximum product to sum of current sub tree * (total sum - sum of current sub tree). 

Now to get the total sum we can apply any traversal and get the total sum but to get the sum of current sub tree we need to use post order traversal. In that way we can get our answer but if we see we need to traverse the tree 2 times. Let's see if we can do better.

If we use post order traversal to calculate the total sum then we can basically use a HashSet / List to store all the intermediate sums. Now we just need to visit every element in HashSet/List and do the following:

maxProduct = MAX (maxProduct, setElement * (total sum - setElement))

I think traversing a List / HashSet is much faster than traversing a binary tree. However the time complexity remains same and second approach will require more space so I would like to go with 1st approach.


Implementation in C#:

Approach 1:

    public int MaxProduct(TreeNode root) 

    {

        int mod = (int) 1e9 + 7;

        long totalSum = this.GetBinaryTreeSum(root);

        long maxProduct = 0;

        this.GetMaxProduct(root, totalSum, ref maxProduct);

        return (int) (maxProduct % mod);

    }


    private long GetMaxProduct(TreeNode node, long totalSum, ref long maxProduct)

    {

        if (node == null)

        {

            return 0;

        }

        long sumOfCurrSubTree = this.GetMaxProduct(node.left, totalSum, ref maxProduct) +

            this.GetMaxProduct(node.right, totalSum, ref maxProduct) + node.val;

        long currProduct = sumOfCurrSubTree * (totalSum - sumOfCurrSubTree);\

        maxProduct = Math.Max(currProduct, maxProduct);

        return sumOfCurrSubTree;   

    }

    

    private long GetBinaryTreeSum(TreeNode node)

    {

        if (node == null)

        {

            return 0;

        }

        return node.val + this.GetBinaryTreeSum(node.left) + this.GetBinaryTreeSum(node.right);

    }


Approach 2:

    public int MaxProduct(TreeNode root) 

    {

        int mod = (int) 1e9 + 7;

        HashSet<long> allSubTreeSums = new HashSet<long>();

        long totalSum = this.GetBinaryTreeSumAndCollectAllSum(root, allSubTreeSums);

        return (int) (this.GetMaxProduct(totalSum, allSubTreeSums) % mod);

    }

    

    private long GetMaxProduct(long totalSum, HashSet<long> allSubTreeSums)

    {

        long maxProduct = 0;

        foreach (long sum in allSubTreeSums)

        {

            maxProduct = Math.Max(maxProduct, (totalSum - sum) * sum);

        }

        return maxProduct;

    }

    

    private long GetBinaryTreeSumAndCollectAllSum(TreeNode node, HashSet<long> allSubTreeSums)

    {

        if (node == null)

        {

            return 0;

        }

        long currSum = this.GetBinaryTreeSumAndCollectAllSum(node.left, allSubTreeSums) +

            this.GetBinaryTreeSumAndCollectAllSum(node.right, allSubTreeSums) +

            node.val;

        allSubTreeSums.Add(currSum);

        return currSum;

    }


Complexity: O(n) for both approaches.

Tuesday, August 17, 2021

[LeetCode] Sqrt(x)

Problem: Given a non-negative integer x, compute and return the square root of x.

Since the return type is an integer, the decimal digits are truncated, and only the integer part of the result is returned.

Note: You are not allowed to use any built-in exponent function or operator, such as pow(x, 0.5) or x ** 0.5.

Example:

Input: x = 4
Output: 2
Input: x = 8
Output: 2
Explanation: The square root of 8 is 2.82842..., and since the decimal part is truncated, 2 is returned.

Constraints:

  1. 0 <= x <= 2^31 - 1


Approach: The obvious approach is to have a loop say i = 1 to x and if we find i * i is equal to x then i is our answer. If x is not a perfect square then in the loop whenever we see the first time i * i is more than x then return i - 1. 

Let's try to do better as here we are searching if we see the range is 1 to x and in sorted order, we can apply binary search here. You can just look at the code to understand the rest.


Implementation in C#:

    public int MySqrt(int x) 

    {

        if (x <= 3)

        {

            return x == 0 ? 0 : 1;

        }        

        long low = 2, high = (long)(x / 2);

        while (low <= high)

        {

            long mid = low + ((high - low) / 2);

            // Take long as mid * mid can overflow.

            long sq = mid * mid;

            if (sq == x)

            {

                return (int)mid;

            }

            else if (sq < x)

            {

                low = mid + 1;

            }

            else

            {

                high = mid - 1;

            }

        }

        return (int)high;

    }


Complexity: O(logx)

[LeetCode] Count Good Nodes in Binary Tree

Problem: Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X.

Return the number of good nodes in the binary tree.

Example:

Input: root = [3,1,4,3,null,1,5]
Output: 4
Explanation: Nodes in blue are good.
Root Node (3) is always a good node.
Node 4 -> (3,4) is the maximum value in the path starting from the root.
Node 5 -> (3,4,5) is the maximum value in the path
Node 3 -> (3,1,3) is the maximum value in the path.

Input: root = [3,3,null,4,2]
Output: 3
Explanation: Node 2 -> (3, 3, 2) is not good, because "3" is higher than it.
Input: root = [1]
Output: 1
Explanation: Root is considered as good.

Constraints:

  • The number of nodes in the binary tree is in the range [1, 10^5].
  • Each node's value is between [-10^4, 10^4].


Approach: We can use Preorder traversal here. Basically in each recursive call, we maintain the maximum number from root to current node and if we find the value at current node is greater than or equal to the max number we maintained then we increase the count of good nodes by 1 (max will also be updated) otherwise the count remains same.

 

Implementation in C#:

        public int GoodNodes(TreeNode root)

    {
        if (root == null)
        {
            return 0;
        }
        return this.GetNumOfGoodNodes(root, int.MinValue);
    }

    private int GetNumOfGoodNodes(TreeNode node, int maxOfPath)
    {
        if (node == null)
        {
            return 0;
        }
        int currVal = 0;
        if (node.val >= maxOfPath)
        {
            currVal = 1;
            maxOfPath = node.val;
        }
        return currVal +
               this.GetNumOfGoodNodes(node.left, maxOfPath) +
               this.GetNumOfGoodNodes(node.right, maxOfPath);
    }


Complexity: O(n)

Wednesday, August 11, 2021

[LeetCode] Array of Doubled Pairs

Problem: Given an array of integers arr of even length, return true if and only if it is possible to reorder it such that arr[2 * i + 1] = 2 * arr[2 * i] for every 0 <= i < len(arr) / 2.

Example:

Input: arr = [3,1,3,6]
Output: false
Input: arr = [2,1,2,6]
Output: false
Input: arr = [4,-2,2,-4]
Output: true
Explanation: We can take two groups, [-2,-4] and [2,4] to form [-2,-4,2,4] or [2,4,-2,-4].
Input: arr = [1,2,4,16,8,4]
Output: false

Constraints:

  • 0 <= arr.length <= 3 * 104
  • arr.length is even.
  • -105 <= arr[i] <= 105


Approach: If you see closely the condition arr[2*i + 1] = 2 * arr[2 * i] is telling us that foreach i = 0, 2, 4, 6,......n-1; 2 * arr[i] should be equal to arr[i + 1]. This means we just need to find 2 * n (twice of n) for every number n of half of the array. 

We can use hashing to find 2*n efficiently.


Implementation in C#:

    public bool CanReorderDoubled(int[] arr) 

    {

        Dictionary<int, int> freqMap = new Dictionary<int, int>();

        foreach (int n in arr)

        {

            if (!freqMap.ContainsKey(n))

            {

                freqMap[n] = 0;

            }

            ++freqMap[n];

        }

        Array.Sort(arr, (n1, n2) => {

            return Math.Abs(n1).CompareTo(Math.Abs(n2));

        });            

        foreach (int n in arr)

        {

            if (!freqMap.ContainsKey(n))

            {

                continue;

            }

            --freqMap[n];

            if (freqMap[n] == 0)

            {

                freqMap.Remove(n);

            }

            if (!freqMap.ContainsKey(2 * n))

            {

                return false;

            }

            --freqMap[2 * n];

            if (freqMap[2 * n] == 0)

            {

                freqMap.Remove(2 * n);

            }

        }

        return freqMap.Count == 0;

    }


Complexity: O(nlogn)

Tuesday, August 10, 2021

[LeetCode] Flip String to Monotone Increasing

Problem: A binary string is monotone increasing if it consists of some number of 0's (possibly none), followed by some number of 1's (also possibly none).

You are given a binary string s. You can flip s[i] changing it from 0 to 1 or from 1 to 0.

Return the minimum number of flips to make s monotone increasing.

Example:

Input: s = "00110"
Output: 1
Explanation: We flip the last digit to get 00111.
Input: s = "010110"
Output: 2
Explanation: We flip to get 011111, or alternatively 000111.
Input: s = "00011000"
Output: 2
Explanation: We flip to get 00000000.


Approach: A brute force approach is to generate all the binary strings of length n and see the number of flips required for each binary string and return the minimum flips among them but it will be very expensive solution.

Let's look at what we are trying to do here; we are trying to form a binary string which looks like 000...111 that mean x0sy1s so we can say there are x 0s in the left half and y 1s in the right half so ultimately it is the problem of knowing how many 1s are in left half and how many 0s are in the right half and we take the minimum of it.


Implementation in C#:

    public int MinFlipsMonoIncr(string s) 

    {

        int ones = 0;

        int minFlips = 0;

        for (int i = 0; i < s.Length; ++i)

        {

            if (s[i] == '1')

            {

                ++ones;

            }

            else

            {

                minFlips = Math.Min(minFlips + 1, ones);

            }

        }    

        return minFlips;

    }


Complexity: O(n)

Friday, August 6, 2021

[LeetCode] N-ary Tree Level Order Traversal

Problem: Given an n-ary tree, return the level order traversal of its nodes' values.

Nary-Tree input serialization is represented in their level order traversal, each group of children is separated by the null value (See examples).

Example:

Input: root = [1,null,3,2,4,null,5,6]
Output: [[1],[3,2,4],[5,6]]

Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
Output: [[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]


Approach: Like in case of binary tree's level order traversal, we will use queue here. The approach is same.


Implementation in C#:

    public IList<IList<int>> LevelOrder(Node root) 

    {

        List<IList<int>> result = new List<IList<int>>();

        if (root == null)

        {

            return result;

        }

        Queue<Node> queue = new Queue<Node>();

        queue.Enqueue(root);

        while(queue.Count > 0)

        {

            List<int> currRow = new List<int>();

            int size = queue.Count;

            for (int i = 0; i < size; ++i)

            {

                var node = queue.Dequeue();

                currRow.Add(node.val);

                foreach(var child in node.children)

                {

                    queue.Enqueue(child);

                }

            }

            result.Add(currRow);

        }

        return result;

    }


Complexity: O(n)

Wednesday, August 4, 2021

[LeetCode] PathSum II

Problem: Given the root of a binary tree and an integer targetSum, return all root-to-leaf paths where each path's sum equals targetSum.

A leaf is a node with no children.

Example:

Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
Output: [[5,4,11,2],[5,8,4,5]]

Input: root = [1,2,3], targetSum = 5
Output: []
Input: root = [1,2], targetSum = 0
Output: []


Approach: We can use PreOrder Traversal to solve this problem easily.


Implementation in C#:

    public IList<IList<int>> PathSum(TreeNode root, int targetSum) 

    {

        List<IList<int>> result = new List<IList<int>>();

        if (root == null)

        {

            return result;

        }

        int currSum = 0;

        List<int> currNums = new List<int>();

        this.AllPathSums(root, targetSum, currSum, currNums, result);

        return result;

    }

    

    private void AllPathSums(

        TreeNode node, 

        int targetSum, 

        int currSum, 

        List<int> currNums,

        List<IList<int>> result

    )

    {

        if (node == null)

        {

            return;

        }

        currSum += node.val;

        currNums.Add(node.val);

        if (node.left == null && node.right == null)

        {

            if (currSum == targetSum)

            {

                result.Add(new List<int>(currNums));

            }

        }

        this.AllPathSums(node.left, targetSum, currSum, currNums, result);

        this.AllPathSums(node.right, targetSum, currSum, currNums, result);

        currSum -= node.val;

        currNums.RemoveAt(currNums.Count - 1);

    }


Complexity: O(n)