Friday, September 17, 2021

[LeetCode] Intersection of Two Arrays II

Problem: Given two integer arrays nums1 and nums2, return an array of their intersection. Each element in the result must appear as many times as it shows in both arrays and you may return the result in any order.

Example:

Input: nums1 = [1,2,2,1], nums2 = [2,2]
Output: [2,2]
Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
Output: [4,9]
Explanation: [9,4] is also accepted.


Approach: We can sort both the arrays and then we can use two pointers approach. The algorithm looks like follows:

  • Sort(num1)
  • Sort(num2)
  • result = []
  • i = 0, j = 0, k = 0
  • WHILE i < COUNT(num1) AND j < COUNT(num2)
    • IF num1[i] < num2[j]
      • i = i + 1
    • ELSE IF num1[i] > num2[j]
      • j = j + 1
    • ELSE
      • result[k] = num1[i]
      • i = i + 1
      • j = j + 1
      • k = k + 1
  • RETURN result

This approach will take O(nlogn) time but is very useful and fast if arrays are sorted. 

Another approach would be to simply use the hashing here to solve this question. You can look at the implementation to understand the approach.


Implementation in C#:

    public int[] Intersect(int[] nums1, int[] nums2) 

    {

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

        int[] arrayToBuildHash = nums1.Length < nums2.Length ? nums1 : nums2;

        int[] arrayToIterate = nums1.Length < nums2.Length ? nums2 : nums1;

        this.FillNumsWithCount(arrayToBuildHash, frequencies);

        return this.GetIntersection(arrayToIterate, frequencies);

    }

    

    private int[] GetIntersection(int[] nums, Dictionary<int, int> frequencies)

    {

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

        foreach(int num  in nums)

        {

            if (frequencies.ContainsKey(num))

            {

                commonNums.Add(num);

                --frequencies[num];

                if (frequencies[num] == 0)

                {

                    frequencies.Remove(num);

                }

            }

        }

        return commonNums.ToArray();

    }

    

    private void FillNumsWithCount(int[] nums, Dictionary<int, int> frequencies)

    {

        foreach(int num in nums)

        {

            if (!frequencies.ContainsKey(num))

            {

                frequencies[num] = 0;

            }

            ++frequencies[num];

        }

    }


Complexity: O(m + n)

Thursday, September 9, 2021

[Google Question][LeetCode] Sum of Distances in Tree

Problem: There is an undirected connected tree with n nodes labeled from 0 to n - 1 and n - 1 edges.

You are given the integer n and the array edges where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the tree.

Return an array answer of length n where answer[i] is the sum of the distances between the ith node in the tree and all other nodes.

Example:

Input: n = 6, edges = [[0,1],[0,2],[2,3],[2,4],[2,5]]
Output: [8,12,6,10,10,10]
Explanation: The tree is shown above.
We can see that dist(0,1) + dist(0,2) + dist(0,3) + dist(0,4) + dist(0,5)
equals 1 + 1 + 2 + 2 + 2 = 8.
Hence, answer[0] = 8, and so on.

Input: n = 1, edges = []
Output: [0]

Input: n = 2, edges = [[1,0]]
Output: [1,1]


Approach: We can apply BFS taking every node as root and we can get our answer but this will be expensive solution as it will take O(n^2) time. Let's try to optimize it.

Let's say our tree is:

        0

      /.   \

    1.       2

  /         /    \ 

5.        3      4

Now let's say we calculated the distance for 0 which will be 8. Now let's try to calculate the distance of 1 -

     1

  /.      \

5.          0 [distance is 8]

                \

                   2

                 /.    \

               3        4

Here we already know that the sum of distances from 0 to every every node is 8. We can divide the this value into two part:

  1. distances of 0 to 1 and its children 
  2. distances of 0 to 2 and its children

Now when we calculate the same distances 1, we can say the sum of distances from 1 is going to be:

distances[0] - Number of Nodes in subtree with root as 1 + Number of nodes in subtree with root as 2 + 1 (for 0) = 

distance[0] - Number of Nodes in subtree with root as 1 + TotalNodes in tree - Number of Nodes in subtree with root as 1

Why? If you see for each node in the subtree(1), we are reducing the distance by 1 because instead of parent of 1 that is 0, we are now starting from 1. Similarly for each node in the subtree(2), we are adding 1 in the distance because instead of starting from 0, we are starting from the other 1, something like:

1 - 0.- 2 <

so the distance we need to add is the distance between 1 - 0 that is 1 to reach 2 and its children as there is no other way to reach subtree(2) from 1. If it is understood than we can have the generic formula:

distance[node] = distance[parentOfNode] - NumOfChildren[node] + (NumNodes - NumOfChildren[node])

We can use post order traversal to calculate the number of nodes in each subtree and initial distance calculation then we can use pre order traversal to calculate the final distances. Have a look at the implementation for more details.

That's all!


Implementation in C#:

public class Graph

{

    public int[] SumOfDistancesInTree(int n, int[][] edges) 

    {

        if (n == 1)

        {

            return new int[n];    

        }

        this.InitializeMembers(n);

        this.CreateGraph(edges);

        this.GetChildrenCountAndInitialResult(0, -1);   

        this.GetDistances(0, -1);

        return this.dist;

    }

    

    private void GetDistances(int node, int parent)

    {

        foreach (int child in this.graph[node])

        {

            // Bidirectional graph instead of visit set we can use this condition here.

            if (child == parent)

            {

                continue;

            }

            dist[child] = dist[node] - this.numOfChildren[child] + (this.numOfNodes - this.numOfChildren[child]);         

            this.GetDistances(child, node);

        }

    }

    

    private void GetChildrenCountAndInitialResult(int node, int parent)

    {

        foreach (int child in this.graph[node])

        {

            // Bidirectional

            if (child == parent)

            {

                continue;

            }

            this.GetChildrenCountAndInitialResult(child, node);

            this.numOfChildren[node] += this.numOfChildren[child];

            this.dist[node] += (this.dist[child] + this.numOfChildren[child]);

        }

        ++this.numOfChildren[node];

    }

    

    private void CreateGraph(int[][] edges)

    {

        foreach (int[] edge in edges)

        {

            this.graph[edge[0]].Add(edge[1]);

            this.graph[edge[1]].Add(edge[0]);

        }

    }

    

    private void InitializeMembers(int n)

    {

        this.numOfNodes = n;

        this.numOfChildren = new int[n];

        this.dist = new int[n];

        this.graph = new HashSet<int>[n];

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

        {

            this.graph[i] = new HashSet<int>();

        }

    }

    

    private HashSet<int>[] graph;

    private int[] numOfChildren;

    private int[] dist;

    private int numOfNodes;

}


Complexity: O(n)

[LeetCode] Largest Plus Sign

Problem: You are given an integer n. You have an n x n binary grid grid with all values initially 1's except for some indices given in the array mines. The ith element of the array mines is defined as mines[i] = [xi, yi] where grid[xi][yi] == 0.

Return the order of the largest axis-aligned plus sign of 1's contained in grid. If there is none, return 0.

An axis-aligned plus sign of 1's of order k has some center grid[r][c] == 1 along with four arms of length k - 1 going up, down, left, and right, and made of 1's. Note that there could be 0's or 1's beyond the arms of the plus sign, only the relevant area of the plus sign is checked for 1's.

Example:

Input: n = 5, mines = [[4,2]]
Output: 2
Explanation: In the above grid, the largest plus sign can only be of order 2. One of them is shown.

Input: n = 1, mines = [[0,0]]
Output: 0
Explanation: There is no plus sign, so return 0.


Approach: The brute force approach would be to take every cell as center and calculate the length of biggest plus it can produce. The maximum of these lengths will be our answer but this will take O(n^3) time. Let's try to optimize it.

We can use DP here. Let's see at given cell (i, j) what could be the longest plus size:

Min(Size of contiguous 1's on the left, Size of contiguous 1's on the right, Size of contiguous 1's down side, Size of contiguous 1's up)

Now we just need to calculate the max of above for calculation for every cell. Now we need to see how we can calculate the left/right/up/down contiguous 1s efficiently. We can use DP here to calculate it in O(n^2). We can maintain 4 tables each for left, right, up and down and we can fill it in following way:

  • Left[i][j] = if matrix[i][j] == 0 then 0 else Left[i][j-1]  + 1
  • Right[i][j] = if matrix[i][j] == 0 then 0 else Right[i][j+1] + 1
  • Down[i][j] = if matrix[i][j] == 0 then 0 else Down[i-1][j] + 1
  • Up[i][j] = if matrix[i][j] == 0 then 0 else Up[i+1][j] + 1

Now once we calculate it we can go at every cell (i, j) calculate the min of Left[i][j], Right[i][j], Down[i][j] and Up[i][j] say minVal[i][j]. In the end we can return max of all the minVal[i][j]s.

The above approach will solve the problem in O(n^2) and will work well but as you can see we are taking four 2D tables. We can apply the same algorithm using one 2D table only. The steps remains almost same and you can understand it by just looking at the implementation of our approach 2.


Implementation in C#:

Approach 1: Using four tables:

    public int OrderOfLargestPlusSign(int n, int[][] mines) 

    {

        HashSet<int> minesSet = new HashSet<int>();

        foreach (int[] mine in mines)

        {

            minesSet.Add(mine[0] * n + mine[1]);

        }

        int[,] leftTable = new int[n,n];

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

        {

            for (int j = 0; j < n; ++j)

            {

                if (j == 0)

                {

                    leftTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    leftTable[i, j] = minesSet.Contains(i * n + j) ? 0 : leftTable[i, j - 1] + 1;

                }

            }

        }

        int[,] rightTable = new int[n,n];

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

        {

            for (int j = n - 1; j >= 0; --j)

            {

                if (j == n - 1)

                {

                    rightTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    rightTable[i, j] = minesSet.Contains(i * n + j) ? 0 : rightTable[i, j + 1] + 1;

                }

            }

        }

        int[,] downTable = new int[n,n];

        for (int j = 0; j < n; ++j)

        {

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

            {

                if (i == 0)

                {

                    downTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    downTable[i, j] = minesSet.Contains(i * n + j) ? 0 : downTable[i - 1, j] + 1;

                }

            }

        }

        int[,] upTable = new int[n,n];

        for (int j = 0; j < n; ++j)

        {

            for (int i = n - 1; i >= 0; --i)

            {

                if (i == n - 1)

                {

                    upTable[i, j] = minesSet.Contains(i * n + j) ? 0 : 1;

                }

                else

                {

                    upTable[i, j] = minesSet.Contains(i * n + j) ? 0 : upTable[i + 1, j] + 1;

                }

            }

        }

        int maxLength = 0;

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

        {

            for (int j = 0; j < n; ++j)

            {   

                int currLength = this.MinElement(leftTable[i, j], rightTable[i, j], downTable[i, j], upTable[i, j]);

                maxLength = Math.Max(maxLength, currLength);

            }

        }        

        return maxLength;

    }


    private int MinElement(params int[] nums)

    {

        return nums.Min();

    }


Approach 2: Using only one table:

    public int OrderOfLargestPlusSign(int n, int[][] mines) 

    {

        HashSet<int> minesSet = new HashSet<int>();

        foreach (int[] mine in mines)

        {

            // We can use it as matrix size is nxn

            minesSet.Add(mine[0] * n + mine[1]);

        }

        

        int[,] table = new int[n,n];

        int maxLength = 0, count;

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

        {

            count = 0;

            // left

            for (int j = 0; j < n; ++j)

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i,j] = count;

            }

            // right

            count = 0;

            for (int j = n - 1; j >= 0; --j)

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i, j] = Math.Min(table[i, j], count);

            }

        }

        for (int j = 0; j < n; ++j)

        {

            // down

            count = 0;

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

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i, j] = Math.Min(table[i, j], count);                

            }

            //up

            count = 0;

            for (int i = n - 1; i >= 0; --i)

            {

                count = minesSet.Contains(i * n + j) ? 0 : count + 1;

                table[i, j] = Math.Min(table[i, j], count);

                if (table[i, j] > maxLength)

                {

                    maxLength = table[i, j];

                }

            }

        }  

        return maxLength;

    }


Complexity: O(n^2)

Wednesday, September 8, 2021

[LeetCode] Shifting Letters

Problem: You are given a string s of lowercase English letters and an integer array shifts of the same length.

Call the shift() of a letter, the next letter in the alphabet, (wrapping around so that 'z' becomes 'a').

For example, shift('a') = 'b', shift('t') = 'u', and shift('z') = 'a'.

Now for each shifts[i] = x, we want to shift the first i + 1 letters of s, x times.

Return the final string after all such shifts to s are applied.

Example:

Input: s = "abc", shifts = [3,5,9]
Output: "rpl"
Explanation: We start with "abc".
After shifting the first 1 letters of s by 3, we have "dbc".
After shifting the first 2 letters of s by 5, we have "igc".
After shifting the first 3 letters of s by 9, we have "rpl", the answer.
Input: s = "aaa", shifts = [1,2,3]
Output: "gfd"
Constraints:
  • 1 <= s.length <= 105
  • s consists of lowercase English letters.
  • shifts.length == s.length
  • 0 <= shifts[i] <= 109

Approach: The approach is very much straight forward. The only problem is while shifting characters we need to take care of a case where s[i] + shifts[i] is more than the value of 'z'. We can take care of this problem by using mod 26.


Implementation in C#:

    public string ShiftingLetters(string s, int[] shifts) 

    {

        int length = shifts.Length;

        StringBuilder stringBuilder = new StringBuilder();

        shifts[length - 1] %= 26;

        for (int i = length - 2; i >= 0; --i)

        {

            shifts[i] = (shifts[i + 1] + shifts[i]) % 26;

        }

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

        {

            int pos = s[i] - 'a';

            stringBuilder.Append((char)(((pos + shifts[i]) % 26) + 'a'));

        }    

        return stringBuilder.ToString();

    }


Complexity: O(n)

Monday, September 6, 2021

[LeetCode] Slowest Key

Problem: A newly designed keypad was tested, where a tester pressed a sequence of n keys, one at a time.

You are given a string keysPressed of length n, where keysPressed[i] was the ith key pressed in the testing sequence, and a sorted list releaseTimes, where releaseTimes[i] was the time the ith key was released. Both arrays are 0-indexed. The 0th key was pressed at the time 0, and every subsequent key was pressed at the exact time the previous key was released.

The tester wants to know the key of the keypress that had the longest duration. The ith keypress had a duration of releaseTimes[i] - releaseTimes[i - 1], and the 0th keypress had a duration of releaseTimes[0].

Note that the same key could have been pressed multiple times during the test, and these multiple presses of the same key may not have had the same duration.

Return the key of the keypress that had the longest duration. If there are multiple such keypresses, return the lexicographically largest key of the keypresses.

Example:

Input: releaseTimes = [9,29,49,50], keysPressed = "cbcd"
Output: "c"
Explanation: The keypresses were as follows:
Keypress for 'c' had a duration of 9 (pressed at time 0 and released at time 9).
Keypress for 'b' had a duration of 29 - 9 = 20 (pressed at time 9 right after the release of the previous character and released at time 29).
Keypress for 'c' had a duration of 49 - 29 = 20 (pressed at time 29 right after the release of the previous character and released at time 49).
Keypress for 'd' had a duration of 50 - 49 = 1 (pressed at time 49 right after the release of the previous character and released at time 50).
The longest of these was the keypress for 'b' and the second keypress for 'c', both with duration 20.
'c' is lexicographically larger than 'b', so the answer is 'c'.
Input: releaseTimes = [12,23,36,46,62], keysPressed = "spuda"
Output: "a"
Explanation: The keypresses were as follows:
Keypress for 's' had a duration of 12.
Keypress for 'p' had a duration of 23 - 12 = 11.
Keypress for 'u' had a duration of 36 - 23 = 13.
Keypress for 'd' had a duration of 46 - 36 = 10.
Keypress for 'a' had a duration of 62 - 46 = 16.
The longest of these was the keypress for 'a' with duration 16.

Constraints:

  • releaseTimes.length == n
  • keysPressed.length == n
  • 2 <= n <= 1000
  • 1 <= releaseTimes[i] <= 109
  • releaseTimes[i] < releaseTimes[i+1]
  • keysPressed contains only lowercase English letters.


Approach: The approach is straight forward. We just need to calculate the maximum difference between two adjacent cells of the given releaseTimes array and we just need to return the character at that index in the given keyPressed string.

The only problem is if there is a tie in between the keypress duration, in that case we just need to return the maximum of those characters. We can easily do it by maintaining two variables say maxKeypressDuration and maxKey and then we can just return the maxKey in the end.

 

Implementation in C#:

    public char SlowestKey(int[] releaseTimes, string keysPressed) 

    {

        char maxKey = keysPressed[0];

        int maxDuration = releaseTimes[0];

        for (int i = 1; i < releaseTimes.Length; ++i)

        {

            int keypressDuration = releaseTimes[i] - releaseTimes[i - 1];

            if (maxDuration == keypressDuration && keysPressed[i] > maxKey)

            {

                maxKey = keysPressed[i];

            }

            else if (maxDuration < keypressDuration)

            {

                maxDuration = keypressDuration;

                maxKey = keysPressed[i];

            }

        }

        return maxKey;

    }


Complexity: O(n)

Friday, September 3, 2021

[Amazon Question] Maximum Average Subtree

Problem: Given the root of a binary tree, find the maximum average value of any subtree of that tree. 

A subtree of a tree is any node of that tree plus all its descendants. The average value of a tree is the sum of its values, divided by the number of nodes.

Example:

Input: Input = [5,6,1]
Output: 6
Explanation: 
For the subtree with root as 5, the average is (5 + 6 + 1) / 3 = 4
For the subtree with root as 6, the average is 6 / 1 = 6 and similarly for subtree with root as 1, the average is 1.
The answer is 6 as it is the maximum of all the above 3 averages.


Approach: We can use post-order traversal here as if we go by bottom-up approach, we will get the average at each and every node and then we can just store the maximum of all the averages. We need to write a function which will return the sum and the number of nodes (of the subtree) but we can have a ref parameter which can then be used for maintaining the maximum average.


Implementation in C#:

    public double MaximumAverageSubtree()

    {

        if (this.Root == null)

        {

            return 0;

        }

        double maxAverage = 0;        

        this.MaximumAverageSubtree(this.Root, ref maxAverage);

        return maxAverage;

    }

    

    private double[] MaximumAverageSubtree(BinaryTreeNode node, ref double maxAverage)

    {

        if (node == null)

        {

            return new double[] {0, 0};

        }

        double[] leftSumAndCount = this.MaximumAverageSubtree(node.LeftNode, ref maxAverage);

        double[] rightSumAndCount = this.MaximumAverageSubtree(node.RightNode, ref maxAverage);

        double sum = leftSumAndCount[0] + rightSumAndCount[0] + node.Value;

        double count = leftSumAndCount[1] + rightSumAndCount[1] + 1;

        double average = sum / count;

        if (average > maxAverage)

        {

            maxAverage = average;

        }        

        return new double[] {sum, count};

    }


Complexity: O(n)

Wednesday, September 1, 2021

[LeetCode] Array Nesting

Problem: You are given an integer array nums of length n where nums is a permutation of the numbers in the range [0, n - 1].

You should build a set s[k] = {nums[k], nums[nums[k]], nums[nums[nums[k]]], ... } subjected to the following rule:

  • The first element in s[k] starts with the selection of the element nums[k] of index = k.
  • The next element in s[k] should be nums[nums[k]], and then nums[nums[nums[k]]], and so on.
  • We stop adding right before a duplicate element occurs in s[k].

Return the longest length of a set s[k].

Example:

Input: nums = [5,4,0,3,1,6,2]
Output: 4
Explanation: 
nums[0] = 5, nums[1] = 4, nums[2] = 0, nums[3] = 3, nums[4] = 1, nums[5] = 6, nums[6] = 2.
One of the longest sets s[k]:
s[0] = {nums[0], nums[5], nums[6], nums[2]} = {5, 6, 2, 0}
Input: nums = [0,1,2]
Output: 1


Approach: This is very much a straight forward problem. Basically we will start with first element (index 0) of the array and we keep going till we get the duplicate element. That means we need to maintain a hash set to have a record of all the visited elements. We will calculate the length of every cycle and return the maximum of those lengths. That's all!

 

Implementation in C#:

    public int ArrayNesting(int[] nums) 

    {

        int length = nums?.Length ?? 0;       

        if (length == 0)

        {

            return 0;

        }

        int maxLength = 0;

        HashSet<int> visited = new HashSet<int>();

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

        {

            int count = 0;

            int j = i;

            while (visited.Add(j))

            {

                j = nums[j];

                ++count;

            }

            maxLength = Math.Max(count, maxLength);

        }

        return maxLength;

    }


Complexity: O(n)