Showing posts with label Binary Search. Show all posts
Showing posts with label Binary Search. Show all posts

Friday, October 4, 2024

[LeetCode] Snapshot Array

Problem: Implement a SnapshotArray that supports the following interface:

  • SnapshotArray(int length) initializes an array-like data structure with the given length. Initially, each element equals 0.
  • void set(index, val) sets the element at the given index to be equal to val.
  • int snap() takes a snapshot of the array and returns the snap_id: the total number of times we called snap() minus 1.
  • int get(index, snap_id) returns the value at the given index, at the time we took the snapshot with the given snap_id

Example:

Input: ["SnapshotArray","set","snap","set","get"]
[[3],[0,5],[],[0,6],[0,0]]
Output: [null,null,0,null,5]
Explanation: 
SnapshotArray snapshotArr = new SnapshotArray(3); // set the length to be 3
snapshotArr.set(0,5);  // Set array[0] = 5
snapshotArr.snap();  // Take a snapshot, return snap_id = 0
snapshotArr.set(0,6);
snapshotArr.get(0,0);  // Get the value of array[0] with snap_id = 0, return 5

Constraints:

  • 1 <= length <= 5 * 10^4
  • 0 <= index < length
  • 0 <= val <= 10^9
  • 0 <= snap_id < (the total number of times we call snap())
  • At most 5 * 10^4 calls will be made to set, snap, and get.


Approach: First thing in the problem we can notice that we can't save full array, every time we take the snapshot as the length of the array could be 5 * 10 ^ 4 and snap can be called 5 * 10 ^ 4 times.

What we can do is we only store the updates which happened in the current snapshot. In this way for every index we will have a list of snapshot id and the value updates so now when we try to get the value given the snapshot we can do following:

  • If no modification i.e. list of updates at the input index will be  null or if no snapshot is taken i.e. current snapshot id is still 0 then we can simply return 0.
  • Else we find the upper bound of snap_id using binary search and return the stored value.

That's all!


Implementation in C#:

public class SnapshotArray
{
    public SnapshotArray(int length)
    {
        this.snapshots = new List<Tuple<int, int>>[length];
        this.currSnapId = 0;
    }
   
    public void Set(int index, int val)
    {
        if (this.snapshots[index] == null)
        {
            this.snapshots[index] = new List<Tuple<int, int>>();
        }
        int length = this.snapshots[index].Count;
        if (length > 0 &&
            this.snapshots[index][length - 1].Item1 == this.currSnapId)
        {
            this.snapshots[index].RemoveAt(length - 1);
        }
        this.snapshots[index].Add(new Tuple<int, int>(this.currSnapId,
                                                      val));
    }
   
    public int Snap()
    {
        ++this.currSnapId;
        return this.currSnapId - 1;
    }
   
    public int Get(int index, int snap_id)
    {
        // No modification or no snapshot taken
        if (this.snapshots[index] == null || this.currSnapId == 0)
        {
            return 0;
        }
        int snapIndex = this.FindSnapIndex(this.snapshots[index], snap_id);
        return snapIndex == -1 ?
               0 :
               this.snapshots[index][snapIndex].Item2;
    }

    private int FindSnapIndex(List<Tuple<int, int>> snapIds, int snapId)
    {
        int start = 0, end = snapIds.Count - 1;
        if (snapId >= snapIds[end].Item1)
        {
            return end;
        }
        while (start <= end)
        {
            int mid = start + (end - start) / 2;
            int currSnap = snapIds[mid].Item1;
            if (currSnap == snapId)
            {
                return mid;
            }
            else if (currSnap < snapId)
            {
                start = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        return end;
    }

    private List<Tuple<int, int>>[] snapshots;
    private int currSnapId;
}

Complexity: SnapshotArray: O(1), Set: O(1), Snap: O(1), Get: O(logn) 

[LeetCode] Time Based Key-Value Store

Problem: Design a time-based key-value data structure that can store multiple values for the same key at different time stamps and retrieve the key's value at a certain timestamp.

Implement the TimeMap class:

  • TimeMap() Initializes the object of the data structure.
  • void set(String key, String value, int timestamp) Stores the key key with the value value at the given time timestamp.
  • String get(String key, int timestamp) Returns a value such that set was called previously, with timestamp_prev <= timestamp. If there are multiple such values, it returns the value associated with the largest timestamp_prev. If there are no values, it returns "".

Example:

Input
["TimeMap", "set", "get", "get", "set", "get", "get"]
[[], ["foo", "bar", 1], ["foo", 1], ["foo", 3], ["foo", "bar2", 4], ["foo", 4], ["foo", 5]]
Output
[null, null, "bar", "bar", null, "bar2", "bar2"]

Explanation
TimeMap timeMap = new TimeMap();
timeMap.set("foo", "bar", 1);  // store the key "foo" and value "bar" along with timestamp = 1.
timeMap.get("foo", 1);         // return "bar"
timeMap.get("foo", 3);         // return "bar", since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 is "bar".
timeMap.set("foo", "bar2", 4); // store the key "foo" and value "bar2" along with timestamp = 4.
timeMap.get("foo", 4);         // return "bar2"
timeMap.get("foo", 5);         // return "bar2"

Constraints:

  • 1 <= key.length, value.length <= 100
  • key and value consist of lowercase English letters and digits.
  • 1 <= timestamp <= 10^7
  • All the timestamps timestamp of set are strictly increasing.
  • At most 2 * 10^5 calls will be made to set and get.


Approach: This is a binary search problem where we need to find the timestamp which is equal to or just smaller than the given timestamp. 

Given the constraint that timestamps are coming in strictly increasing order, we don't have to sort or maintain a sorted data structure. We can simply use a list.


Implementation in C#:

public class TimeMap
{
    public TimeMap()
    {
        this.kvMap = new Dictionary<string, List<Tuple<int, string>>>();
    }
   
    public void Set(string key, string value, int timestamp) {
        if (!this.kvMap.ContainsKey(key))
        {
            this.kvMap[key] = new List<Tuple<int, string>>();
        }
        this.kvMap[key].Add(new Tuple<int, string>(timestamp, value));
    }
   
    public string Get(string key, int timestamp)
    {
        if (!kvMap.ContainsKey(key))
        {
            return "";
        }
        int index = this.GetIndex(kvMap[key], timestamp);
        return index == -1 ?
               "" :
               kvMap[key][index].Item2;
    }

    private int GetIndex(List<Tuple<int, string>> sortedTimestamps,
                         int timestamp)
    {
        int start = 0, end = sortedTimestamps.Count - 1;
        if (start > end || timestamp < sortedTimestamps[0].Item1)
        {
            return -1;
        }
        while (start <= end)
        {
            int mid = start + (end - start) / 2;
            int currTimestamp = sortedTimestamps[mid].Item1;
            if (currTimestamp == timestamp)
            {
                return mid;
            }
            if (currTimestamp < timestamp)
            {
                start = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        return end;
    }

    private Dictionary<string, List<Tuple<int, string>>> kvMap;
}

Complexity: Set - O(1), Get - O(logn)

Thursday, October 3, 2024

[LeetCode] Search Insert Position

Problem: Given a sorted array of distinct integers and a target value, return the index if the target is found. If not, return the index where it would be if it were inserted in order.

Example:

Input: nums = [1,3,5,6], target = 5
Output: 2
Input: nums = [1,3,5,6], target = 2
Output: 1
Input: nums = [1,3,5,6], target = 7
Output: 4

Constraints:

  • 1 <= nums.length <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums contains distinct values sorted in ascending order.
  • -10^4 <= target <= 10^4


Approach: The problem here is just to find the target itself or just greater element in the array so if you see this is a binary search problem with following changes:

  1. If nums[mid] < target then obviously we need to go to the right side i.e. start = mid + 1
  2. Else end = mid
  3. Return end.


Implementation in C#:

    public int SearchInsert(int[] nums, int target)
    {
        int start = 0, end = nums.Length - 1 ;
        if (target < nums[start])
        {
            return start;
        }
        if (target > nums[end])
        {
            return end + 1;
        }
        while (start < end)
        {
            int mid = start + (end - start) / 2;
            if (nums[mid] < target)
            {
                start = mid + 1;
            }
            else
            {
                end = mid;
            }
        }    
        return end;
    }


Complexity: O(log(n))

[LeetCode] Find Smallest Letter Greater Than Target

Problem: You are given an array of characters letters that is sorted in non-decreasing order, and a character target. There are at least two different characters in letters.

Return the smallest character in letters that is lexicographically greater than target. If such a character does not exist, return the first character in letters

Example:

Input: letters = ["c","f","j"], target = "a"
Output: "c"
Explanation: The smallest character that is lexicographically greater than 'a' in letters is 'c'
Input: letters = ["c","f","j"], target = "c"
Output: "f"
Explanation: The smallest character that is lexicographically greater than 'c' in letters is 'f'.
Input: letters = ["x","x","y","y"], target = "z"
Output: "x"
Explanation: There are no characters in letters that is lexicographically greater than 'z' so we return letters[0].

Constraints:

  • 2 <= letters.length <= 10^4
  • letters[i] is a lowercase English letter.
  • letters is sorted in non-decreasing order.
  • letters contains at least two different characters.
  • target is a lowercase English letter.


Approach: This is a simple binary search problem with following changes:

  1. If letters[mid] <= target then obviously we will search on the right side so start becomes mid + 1.
  2. Else we go to the left to see there is a smaller letter which is greater than target so end becomes mid.
  3. In the end we return letters[end]


Implementation in C#:

    public char NextGreatestLetter(char[] letters, char target)
    {
        int start = 0, end = letters.Length - 1;
        if (target < letters[start] || target >= letters[end])
        {
            return letters[start];
        }
        while (start < end)
        {
            int mid = start + (end - start) / 2;
            if (letters[mid] <= target)
            {
                start = mid + 1;
            }
            else
            {
                end = mid;
            }
        }
        return letters[end];
    }


Complexity: O(log(n))

Friday, March 22, 2024

[LeetCode] Koko Eating Bananas

Problem: Koko loves to eat bananas. There are n piles of bananas, the ith pile has piles[i] bananas. The guards have gone and will come back in h hours.

Koko can decide her bananas-per-hour eating speed of k. Each hour, she chooses some pile of bananas and eats k bananas from that pile. If the pile has less than k bananas, she eats all of them instead and will not eat any more bananas during this hour.

Koko likes to eat slowly but still wants to finish eating all the bananas before the guards return.

Return the minimum integer k such that she can eat all the bananas within h hours.

Example:

Input: piles = [3,6,7,11], h = 8
Output: 4
Input: piles = [30,11,23,4,20], h = 5
Output: 30
Input: piles = [30,11,23,4,20], h = 6
Output: 23

Constraints:

  • 1 <= piles.length <= 104
  • piles.length <= h <= 109
  • 1 <= piles[i] <= 109


Approach: This is again a binary search problem but how? Where is our sorted array? Sorted array is speed of eating banana which could be in range 1, 2, 3, ...., Max(piles). Right.

Now what to compare with mid? This is easy, we need to see if with mid speed, all the bananas can be eaten or not. If all the bananas can't be eaten then that means we need to increase the speed, right. This means start will become mid + 1;

If it can be eaten then with speed mid that means we just need to check if there is a speed which is less than mid with which all the bananas can still be eaten withn h hours. Remember we need to find the minimum speed with which all the bananas can be eaten within h hours so that means we need to go left side so end will become mid.

That's all!


Implementation in C#:

    public int MinEatingSpeed(int[] piles, int h)
    {
        int start = 1;
        int end = Enumerable.Max(piles);
        while (start < end)
        {
            int mid = start + (end - start) / 2;
            if (this.BananaCanBeEaten(piles, mid, h))
            {
                end = mid;
            }
            else
            {
                start  = mid + 1;
            }
        }
        return start;
    }

    private bool BananaCanBeEaten(int[] piles, int speed, int h)
    {
        long hours = 0;
        foreach(int pile in piles)
        {
            hours += (pile / speed);
            if (pile % speed != 0)
            {
                ++hours;
            }
        }
        return hours <= h;
    }

Complexity:  O(n + nlog(max(pile)))

Thursday, March 21, 2024

[LeetCode] Successful Pairs of Spells and Potions

Problem: You are given two positive integer arrays spells and potions, of length n and m respectively, where spells[i] represents the strength of the ith spell and potions[j] represents the strength of the jth potion.

You are also given an integer success. A spell and potion pair is considered successful if the product of their strengths is at least success.

Return an integer array pairs of length n where pairs[i] is the number of potions that will form a successful pair with the ith spell.

Example:

Input: spells = [5,1,3], potions = [1,2,3,4,5], success = 7
Output: [4,0,3]
Explanation:
- 0th spell: 5 * [1,2,3,4,5] = [5,10,15,20,25]. 4 pairs are successful.
- 1st spell: 1 * [1,2,3,4,5] = [1,2,3,4,5]. 0 pairs are successful.
- 2nd spell: 3 * [1,2,3,4,5] = [3,6,9,12,15]. 3 pairs are successful.
Thus, [4,0,3] is returned.
Input: spells = [3,1,2], potions = [8,5,8], success = 16
Output: [2,0,2]
Explanation:
- 0th spell: 3 * [8,5,8] = [24,15,24]. 2 pairs are successful.
- 1st spell: 1 * [8,5,8] = [8,5,8]. 0 pairs are successful. 
- 2nd spell: 2 * [8,5,8] = [16,10,16]. 2 pairs are successful. 
Thus, [2,0,2] is returned.

Constraints:

  • n == spells.length
  • m == potions.length
  • 1 <= n, m <= 10^5
  • 1 <= spells[i], potions[i] <= 105
  • 1 <= success <= 10^10


Approach: We can use binary search here. We can simply sort the potions array and then for every spell, we try to get the first index in the potions which satisfy following condition:

spell * potions[index] >= success

Once we get this index, we can simply say that for a particular spell the number of successful pairs is m - index.

That's all!


Implementation in C#:

    public int[] SuccessfulPairs(int[] spells, int[] potions, long success)
    {
        Array.Sort(potions);
        int[] result = new int[spells.Length];
        for (int i = 0; i < spells.Length; ++i)
        {
            result[i] = this.GetNumOfPotions(spells[i], potions, success);
        }
        return result;
    }

    private int GetNumOfPotions(int spell, int[] potions, long success)
    {
        int start = 0, end = potions.Length - 1;
        while (start <= end)
        {
            int mid = start + (end - start) / 2;
            long currValue = (long)spell * potions[mid];
            if (currValue < success)
            {
                start = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        return potions.Length - start;
    }


Complexity: O(mlogm + nlogm)

Wednesday, November 16, 2022

[Uber][LeetCode] Find First and Last Position of Element in Sorted Array

Problem: Given an array of integers nums sorted in non-decreasing order, find the starting and ending position of a given target value.

If target is not found in the array, return [-1, -1].

You must write an algorithm with O(log n) runtime complexity.

Example:

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]


Approach: We can use binary search to find the first position and last position of the target given the array is sorted.


Implementation in C#:

    public int[] SearchRange(int[] nums, int target)
    {
        int first = this.FindFirstOccrence(nums, target);
        if (first == -1)
        {
            return new int[] {first, first};
        }
        return new int[] {first,
                          this.FindLastOccrence(nums, target)};
    }

    private int FindFirstOccrence(int[] nums, int target)
    {
        int start = 0, end = nums.Length - 1;
        while (start <= end)
        {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target &&
                (mid == 0 || nums[mid] > nums[mid - 1]))
            {
                return mid;
            }
            else if (nums[mid] < target)
            {
                start = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        return -1;
    }

    private int FindLastOccrence(int[] nums, int target)
    {
        int start = 0, end = nums.Length - 1;
        while (start <= end)
        {
            int mid = start + (end - start) / 2;
            if (nums[mid] == target &&
                (mid == nums.Length - 1
                 || nums[mid] < nums[mid + 1]))
            {
                return mid;
            }
            else if (nums[mid] <= target)
            {
                start = mid + 1;
            }
            else
            {
                end = mid - 1;
            }
        }
        return -1;
    }


Complexity: O(log n)