Wednesday, April 10, 2024

[LeetCode] Merge Two Sorted Lists

Problem: Merge the two lists into one sorted list. The list should be made by splicing together the nodes of the first two lists.

Return the head of the merged linked list.

Example:

Input: list1 = [1,2,4], list2 = [1,3,4]
Output: [1,1,2,3,4,4]
Input: list1 = [], list2 = []
Output: []
Input: list1 = [], list2 = [0]
Output: [0]


Approach: We have already done it recursively before. Just adding the iterative approach here. You can just understand it by looking at the implementation.


Implementation in C#:

    public ListNode MergeTwoLists(ListNode list1, ListNode list2)
    {
        var itr1 = list1;
        var itr2 = list2;
        var head = new ListNode();
        var itr = head;

        while (itr1 != null && itr2 != null)
        {
            if (itr1.val <= itr2.val)
            {
                itr.next = itr1;
                itr1 = itr1.next;
            }
            else
            {
                itr.next = itr2;
                itr2 = itr2.next;
            }
            itr = itr.next;
        }
        if (itr1 != null)
        {
            itr.next = itr1;
        }
        else if (itr2 != null)
        {
            itr.next = itr2;
        }
        return head.next;
    }


Complexity: O(Max(m, n))

Middle of the Linked List

Problem: Given the head of a singly linked list, return the middle node of the linked list.

If there are two middle nodes, return the second middle node.

Example:

Input: head = [1,2,3,4,5]
Output: [3,4,5]
Explanation: The middle node of the list is node 3.

Input: head = [1,2,3,4,5,6]
Output: [4,5,6]
Explanation: Since the list has two middle nodes with values 3 and 4, we return the second one.


Approach: We can do it in two passes where in first iteration we can get the number of nodes and in the second iteration we will get the size/2th node. 

Let's try to reduce the number of iterations. What we can do, we can use two pointers; one is slow which goes 1 step at one time and one is fast which goes 2 steps at a time:

  • slow = slow.next
  • fast = fast.next.next

Obviously when fast is null or it's next is null, slow will be pointing at the middle node. That's all!


Implementation in C#:

    public ListNode MiddleNode(ListNode head)
    {
        if (head == null || head.next == null)
        {
            return head;
        }
        ListNode slow = head, fast = head;
        while (fast != null && fast.next != null)
        {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }

Complexity: O(n)

[InterviewBit] Subarray with given XOR

Problem: Given an array of integers A and an integer B.

Find the total number of subarrays having bitwise XOR of all elements equals to B.

Example:

Input 1:

 A = [4, 2, 2, 6, 4]
 B = 6

Input 2:

 A = [5, 6, 7, 8, 9]
 B = 5


Example Output

Output 1:

 4

Output 2:

 2


Example Explanation

Explanation 1:

 The subarrays having XOR of their elements as 6 are:
 [4, 2], [4, 2, 2, 6, 4], [2, 2, 6], [6]

Explanation 2:

 The subarrays having XOR of their elements as 5 are [5] and [5, 6, 7, 8, 9]


Approach: We will use hash map to solve this problem. Approach is simple, we will keep doing the xor of every element and store each xor and its frequency in the hashmap. Now at every step we just need to check if curr_xor ^ B exist in the map or not, if yes we will just add count of curr_xor ^ B to the result. That's all!


Implementation in C#:

    public int solve(List<int> A, int B) 

    {

        int length = A?.Count ?? 0;

        if (length == 0)

        {

            return 0;

        }

        var xorMap = new Dictionary<int, int>();

        int xor = 0;

        int count = 0;

        xorMap[xor] = 1;

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

        {

            xor ^= A[i];

            if (xorMap.ContainsKey(B ^ xor))

            {

                count += xorMap[B ^ xor];

            }

            if (!xorMap.ContainsKey(xor))

            {

                xorMap[xor] = 0;

            }

            ++xorMap[xor];

        }

        return count;

    }


Complexity: O(n)

Sunday, March 31, 2024

[Amazon][LeetCode] Best Time to Buy and Sell Stock

Problem: You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

Example:

Input: prices = [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.
Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transactions are done and the max profit = 0.


Approach: One way to do it is we can maintain an array leftMin where leftMin[i] will give the minimum value in the prices[0...i]. Similarly we can maintain an array rightMax where rightMax[i] will give the maximum of prices[i...n-1]. It is obvious that maximum of all the rightMax[i] - leftMin[i] where i = 0...n-1 will be our answer.

The above approach is a big optimiztion over brute force approach but it's still doing three iterations and moreover the space is 2*n. Let's try to do better.

Instead of maintaining rightMax array we can have a variable maxElement which gives at any ith index the maximum of prices[i...n-1] and then we can just return the max of maxElement - leftMin[i] for i = 0...n-1. See now we have just save n space and also we are saving one iteration.

If we see like maxElement, we can maintain a minElement which gives at ith index the min of pices[0...i], so now do we need to actually need to create any addition array? No right. Actually we don't need maxElement from right. At any i (i = 0...n-1), if we have Min(prices[0...i]) which is our minElement, we can say the max profit using prices[i] will be prices[i] - minElement so if we take max of all prices[i] - minElement for every i = 0...n-1 then we will have our answer.

That's all!


Implementation in C#:

Using left min array:

    public int MaxProfit(int[] prices)
    {
        int length = prices?.Length ?? 0;
        if (length <= 1)
        {
            return 0;
        }
        int[] leftMinArr = new int[length];
        leftMinArr[0] = prices[0];
        for (int i = 1; i < length; ++i)
        {
            leftMinArr[i] = Math.Min(leftMinArr[i - 1], prices[i]);
        }
        int maxElement = prices[length - 1];
        int maxProfit = Math.Max(0, maxElement - leftMinArr[length - 1]);
        for (int i = length - 2; i >= 0; --i)
        {
            maxElement = Math.Max(maxElement, prices[i]);
            maxProfit = Math.Max(maxProfit, maxElement - leftMinArr[i]);
        }
        return maxProfit;
    }

Without using extra space:

    public int MaxProfit(int[] prices)
    {
        int length = prices?.Length ?? 0;
        if (length <= 1)
        {
            return 0;
        }
        int minElement = prices[0];
        int maxProfit = 0;
        for (int i = 0; i < length; ++i)
        {
            minElement = Math.Min(minElement, prices[i]);
            maxProfit = Math.Max(maxProfit, prices[i] - minElement);
        }
        return maxProfit;
    }

Complexity: O(n)    

[LeetCode] Pascal's Triangle

Problem: Given an integer numRows, return the first numRows of Pascal's triangle.

In Pascal's triangle, each number is the sum of the two numbers directly above it as shown:

Example:

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


Approach: It's just simple implementation problem. Please have a look at implementation directly.


Implementation in C#:

    public IList<IList<int>> Generate(int numRows)
    {
        var result = new List<IList<int>>();
        if (numRows == 0)
        {
            return result;
        }
        result.Add(new List<int> {1});
        if (numRows == 1)
        {
            return result;
        }
        result.Add(new List<int> {1, 1});
        for (int i = 2; i < numRows; ++i)
        {
            var currRow = new List<int>();
            var lastRow = result[i - 1];
            currRow.Add(1);
            for (int j = 1; j < i; ++j)
            {
                currRow.Add(lastRow[j - 1] + lastRow[j]);
            }
            currRow.Add(1);
            result.Add(currRow);
        }
        return result;
    }


Complexity: O(n^2)

[LeetCode] Online Stock Span

Problem: Design an algorithm that collects daily price quotes for some stock and returns the span of that stock's price for the current day.

The span of the stock's price in one day is the maximum number of consecutive days (starting from that day and going backward) for which the stock price was less than or equal to the price of that day.

  • For example, if the prices of the stock in the last four days is [7,2,1,2] and the price of the stock today is 2, then the span of today is 4 because starting from today, the price of the stock was less than or equal 2 for 4 consecutive days.
  • Also, if the prices of the stock in the last four days is [7,34,1,2] and the price of the stock today is 8, then the span of today is 3 because starting from today, the price of the stock was less than or equal 8 for 3 consecutive days.

Implement the StockSpanner class:

  • StockSpanner() Initializes the object of the class.
  • int next(int price) Returns the span of the stock's price given that today's price is price.

Example:

Input
["StockSpanner", "next", "next", "next", "next", "next", "next", "next"]
[[], [100], [80], [60], [70], [60], [75], [85]]
Output
[null, 1, 1, 1, 2, 1, 4, 6]

Explanation
StockSpanner stockSpanner = new StockSpanner();
stockSpanner.next(100); // return 1
stockSpanner.next(80);  // return 1
stockSpanner.next(60);  // return 1
stockSpanner.next(70);  // return 2
stockSpanner.next(60);  // return 1
stockSpanner.next(75);  // return 4, because the last 4 prices (including today's price of 75) were less than or equal to today's price.
stockSpanner.next(85);  // return 6


Approach: This is a monotonic stack problem. We keep popping the elements from stack till the current price is greater than or equal to the top of stack and return the difference of the sequences.

The problem here if we just record the the price and not the sequence in which the values are coming we will never be able to get the number of elements less than or equal to current price until we store every price. But instead of storing every price, we  can store the pair of price and the sequence number in the stack.

We just need to be careful while storing the price, we need to store the sequence number of the last popped price from stack. What it is indicating is that the current price is the maximum price till this particular sequence number (reversed).

That's all!


Implementation  in C#:

public class StackElement
{
    public int Value {get; set;}
    public int Sequence {get; set;}
    public StackElement(int val, int seq)
    {
        this.Value = val;
        this.Sequence = seq;
    }
}

public class StockSpanner
{
    public StockSpanner()
    {
        this.stack = new Stack<StackElement>();    
    }
   
    public int Next(int price)
    {
        int retVal = 1;
        ++this.seqNo;
        int seq = this.seqNo;
        StackElement se = null;
        while(stack.Count > 0 &&
              price >= stack.Peek().Value)
        {
            se = stack.Pop();
        }
        if (se != null)
        {
            retVal = seqNo - se.Sequence + 1;
            seq = se.Sequence;
        }
        stack.Push(new StackElement(price, seq));
        return retVal;
    }

    private Stack<StackElement> stack;
    private int seqNo;
}

Complexity: O(n)

Saturday, March 30, 2024

[LeetCode] Search Suggestions System

Problem: You are given an array of strings products and a string searchWord.

Design a system that suggests at most three product names from products after each character of searchWord is typed. Suggested products should have common prefix with searchWord. If there are more than three products with a common prefix return the three lexicographically minimums products.

Return a list of lists of the suggested products after each character of searchWord is typed.

Example:

Input: products = ["mobile","mouse","moneypot","monitor","mousepad"], searchWord = "mouse"
Output: [["mobile","moneypot","monitor"],["mobile","moneypot","monitor"],["mouse","mousepad"],["mouse","mousepad"],["mouse","mousepad"]]
Explanation: products sorted lexicographically = ["mobile","moneypot","monitor","mouse","mousepad"].
After typing m and mo all products match and we show user ["mobile","moneypot","monitor"].
After typing mou, mous and mouse the system suggests ["mouse","mousepad"].
Input: products = ["havana"], searchWord = "havana"
Output: [["havana"],["havana"],["havana"],["havana"],["havana"],["havana"]]
Explanation: The only word "havana" will be always suggested while typing the search word.
Input: products = ["havana"], searchWord = "tatiana"
Output: [[],[],[],[],[],[],[]]
Explanation: No match at all


Approach: Given this search is kind of prefix search so it's intuitive that we will use Trie. However the TrieNode will have one more field which is going to be list of sorted suggestions. We will have these suggestions at every node and we will maintain it at every insertion so that we can quickly return these suggestions. That's all!


Implementation in C#:

public class TrieNode
{
    public TrieNode(char ch)
    {
        this.Value = ch;
        this.Children = new TrieNode[26];
        this.Suggestions = new List<string>();
    }

    public char Value {get; set;}
    public TrieNode[] Children {get; set;}
    public List<string> Suggestions {get; set;}
}

public class Trie
{
    private const char ENDOFWORD = '#';
   
    public Trie()
    {
        this.root = new TrieNode('X');
    }

    public void InsertWord(string word)
    {
        var node = this.root;
        foreach (char ch in word)
        {
            int index = ch - 'a';
            if (node.Children[index] == null)
            {
                node.Children[index] = new TrieNode('*');
            }
            node = node.Children[index];
            this.AdjustSuggestions(node, word);
        }
        node.Value = ENDOFWORD;
    }

    public IList<IList<string>> GetSuggestions(string searchWord)
    {
        var result = new List<IList<string>>();
        var node = this.root;
        foreach (char ch in searchWord)
        {
            int index = ch - 'a';
            if (node.Children[index] == null)
            {
                break;
            }
            node = node.Children[index];
            result.Add(node.Suggestions);
        }
        int remainingElements = searchWord.Length - result.Count;
        for (int i = 0; i < remainingElements; ++i)
        {
            result.Add(new List<string>());
        }

        return result;
    }

    private void AdjustSuggestions(TrieNode node, string word)
    {
        // We can do better here, instead of assuming max number of
        // suggestions is 3
        int count = node.Suggestions.Count;
        if (count == 0)
        {
            node.Suggestions.Add(word);
        }
        else if (count == 1)
        {
            if (String.Compare(word, node.Suggestions[0]) < 0)
            {
                node.Suggestions.Insert(0, word);
            }
            else
            {
                node.Suggestions.Add(word);
            }
        }
        else if (count == 2)
        {
            this.HandleTwoSuggestions(node, word);
        }
        else
        {
            if (String.Compare(word, node.Suggestions[2]) < 0)
            {
                node.Suggestions.RemoveAt(node.Suggestions.Count - 1);
                this.HandleTwoSuggestions(node, word);
            }
        }
    }

    private void HandleTwoSuggestions(TrieNode node, string word)
    {
        if (String.Compare(word, node.Suggestions[0]) < 0)
        {
            node.Suggestions.Insert(0, word);
        }
        else if (String.Compare(word, node.Suggestions[1]) < 0)
        {
            node.Suggestions.Insert(1, word);
        }
        else
        {
            node.Suggestions.Add(word);
        }
    }

    private TrieNode root;
}

public class Solution
{
    public IList<IList<string>> SuggestedProducts(string[] products,
                                                  string searchWord)
    {
        var trie = new Trie();
        foreach (var product in products)
        {
            trie.InsertWord(product);
        }
        return trie.GetSuggestions(searchWord);
    }
}

Complexity: O(m * n + l) where m is number of products, n is length of largest string in products and l is length of search word.