Showing posts with label BinarySearch. Show all posts
Showing posts with label BinarySearch. Show all posts

1/23/2022

[LeetCode] 2064. Minimized Maximum of Products Distributed to Any Store

 Problem : https://leetcode.com/problems/minimized-maximum-of-products-distributed-to-any-store/

If X is valid answer, all numbers larger than X is still valid answer. If X is invalid answer, all numbers less than X is still invalid answer. We may use binary search to 'guess' the minimum valid answer.


class Solution {
    
    /**
     Use binary search to find the lower bound of the valid maximum number of products
     Time complexity = O ( N * Log(M) ).  
     N = number of product types. M = maximum value of the quantities
    */
    public int minimizedMaximum(int n, int[] quantities) {
        int right = Arrays.stream(quantities).max().getAsInt();
        int left = 1;
        
        while (left < right) {
            int mid = left + (right - left) / 2;
            
            if (isValid(n, quantities, mid)) {
                // 'mid' is a valid answer. any number larger than X is still valid answer.
                //  move the right pointer to left side.
                right = mid;
            } else {
                // 'mid' is a invalid answer. any number less than X is still invalid anwser.
                // move the left pointer to right side.
                left = mid + 1;
            }
        }
        
        return right;
    }
    
    /**
     'mx' is valid if all products can be distrbuted to 'n' stores.
    */
    boolean isValid(int n, int[] quantities, int mx) {
        int neededStore = 0;
        
        for (int i = 0; i < quantities.length; i++) {
            // accumulate the number of store needed for product type 'i'
            neededStore += quantities[i] / mx;
            neededStore += (quantities[i] % mx == 0) ? 0 : 1;
        }
       
        return neededStore <= n;
    }
}

11/11/2021

[LeetCode] 1413. Minimum Value to Get Positive Step by Step Sum

 Problem : https://leetcode.com/problems/minimum-value-to-get-positive-step-by-step-sum/

Assume x is a valid start value, then x + 1 is also a start value.

Assume x is an invalid start value, then x - 1 is also a invalid start value.

So there is a contiguous valid start value section. We can use binary search to find the lower bound of the section.


class Solution {
    fun minStartValue(nums: IntArray): Int {
    
        fun isValid(x: Int): Boolean {
            var prefixSum = x
            
            nums.forEach {
                prefixSum += it
                if (prefixSum < 1) {
                    return false
                }
            }
            
            return true
        }
        
        // valid start value section [1, nums.size * 100 + 1]
        // 1 -> when all numbers in the array equal to 0
        // nums.size * 100 + 1 -> when all numbers in the array equal to -100
        
        var left = 1
        var right = nums.size * 100 + 1
        
        while (left < right) {
            val mid = left + (right - left) / 2
            
            if (isValid(mid)) {
                right = mid
            } else {
                left = mid + 1
            }
            
        }
        
        return right
    }
}

11/05/2021

[LeetCode] 441. Arranging Coins

 Problem : https://leetcode.com/problems/arranging-coins/

Because 1 + 2 + 3 + ... + n = n * (n+1) / 2,  we can use binary search to find the answer.


class Solution:
    def arrangeCoins(self, n: int) -> int:
        
        # the possible result range = [left, right)
        left, right = 1, n + 1
        
        while left < right:
            mid = left + (right - left) // 2
            
            # total coions = mid * (mid + 1) / 2
            if mid *(mid+1)//2 <= n:
                left = mid + 1
            else:
                right = mid 
        
        return left - 1

7/17/2021

[LeetCode] 611. Valid Triangle Number

 Problem : https://leetcode.com/problems/valid-triangle-number/

To form a valid triangle, the 3 side lengths must compel with  A + B > C.

Use binary search to find number of C which less than (A + B).


class Solution:
    def triangleNumber(self, nums: List[int]) -> int:
        nums.sort()
        result = 0
        
        for i in range(len(nums)):
            for j in range(i+1, len(nums)-1):
                k = bisect.bisect_left(nums, nums[i] + nums[j], j+1, len(nums))
                result +=  k - (j+1)
        
        return result

Java solution:


class Solution {
    public int triangleNumber(int[] nums) {
        Arrays.sort(nums);
        int result = 0;
        
        for (int i = 0; i < nums.length -1; i++) {
            for (int j = i+1; j < nums.length - 1; j++) {
                int k = lowerBound(nums, nums[i] + nums[j], j+1, nums.length);
                result += k - (j+1);
            }
        }
        
        return result;
    }
    
    /**
    * Return position of the first number >= target
    */
    int lowerBound(int[] nums, int target, int start, int end) {
        int left = start;
        int right = end;
        
        while (left < right) {
            int mid = left + (right - left) / 2;
            
            if (nums[mid] > target)
                right = mid;
            else if (nums[mid] < target)
                left = mid + 1;
            else if (nums[mid] == target)
                right = mid;
        }
        
        return right;
    }
}

12/29/2020

[LeetCode] 374. Guess Number Higher or Lower

 Problem: https://leetcode.com/problems/guess-number-higher-or-lower/

Use binary search to locate the target number.

We can optimize the process by narrowing the window down as small as possible before start the binary search.

Time Complexity = O ( Log N )


# The guess API is already defined for you.
# @param num, your guess
# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
# def guess(num: int) -> int:

class Solution:
    def guessNumber(self, n: int) -> int:
        left, right = n, 1
        
        while guess(left) == -1:
            # left = left // 2
            left >>= 1 
        
        while guess(right) == 1:
            # right = right * 2
            right <<= 1 
        
        while left <= right:
            # mid = left + (right - left) // 2
            mid = left + ((right - left) >> 1)
            
            tmp = guess(mid)
            if tmp == 0: return mid
            if tmp == 1: left = mid + 1
            if tmp == -1: right = mid - 1
        
        return left

11/25/2020

[LeetCode] 367. Valid Perfect Square

 Problem : https://leetcode.com/problems/valid-perfect-square/

Generally, for number N, it's square root is in the range of [1, N)

Use binary search to find the square root.


class Solution:
    def isPerfectSquare(self, num: int) -> bool:
        
        left, right = 1, num
        
        while left <= right:
            
            mid = left + (right - left) // 2
            
            if mid * mid == num:
                return True
            
            if mid * mid > num:
                right = mid - 1
            else:
                left = mid + 1
        
        
        return False

11/23/2020

[LeetCode] 363. Max Sum of Rectangle No Larger Than K

 Problem : https://leetcode.com/problems/max-sum-of-rectangle-no-larger-than-k/


class Solution:
    def maxSumSubmatrix(self, matrix: List[List[int]], k: int) -> int:
        if not matrix or not matrix[0]: return 0
        
        ROW = len(matrix)
        COLUMN = len(matrix[0])
        result = -2 ** 31
        
        for left in range(COLUMN):
            rowSums = [0] * ROW
            
            for right in range(left, COLUMN):
                
                for i in range(ROW):
                    rowSums[i] += matrix[i][right]
                    
                # find the max subarry no more than k
                dp = [0]
                total = 0

                for rsum in rowSums:
                    total += rsum

                    i = bisect.bisect_left(dp, total - k)
                    if i < len(dp): 
                        result = max(result, total - dp[i])

                    bisect.insort(dp, total)
            
        return result

11/15/2020

[LeetCode] 354. Russian Doll Envelopes

 Problem : https://leetcode.com/problems/russian-doll-envelopes/

This problem equals to find the length of longest increasing subsequence on 2 dimensions.

1. Sort the array. Ascending on width and descend on height for same width.

Because [3,3] cannot be contained by [3, 4], we need to put [3,4] in front of [3,3]. Otherwise,  "[3,3], [3,4]" will be considered as a valid increasing subsequence.


2.  Find the longest increasing subsequence base on height.

Since width of envelop has been sorted in increasing order, we only need to consider height.

Time Complexity = O ( N Log N )


from functools import cmp_to_key

class Solution:
    def maxEnvelopes(self, envelopes: List[List[int]]) -> int:
        
        def lowerBound(dp, n):
            left, right = 0, len(dp)
            
            while left < right:
                mid = left + (right - left) // 2
                
                if dp[mid] == n:
                    right = mid
                elif dp[mid] > n:
                    right = mid
                elif dp[mid] < n:
                    left = mid + 1
            
            return left
        
        def compare(a, b):
            return a[0] - b[0] if a[0] != b[0] else b[1] - a[1]
        
        envelopes.sort(key=cmp_to_key(compare))
        
        dp = []
        
        for _, h  in envelopes:
            i = lowerBound(dp, h)
            
            if i == len(dp):
                dp.append(h)
            else:
                dp[i] = h
        
        return len(dp)

11/13/2020

[LeetCode] 352. Data Stream as Disjoint Intervals

 Problem : https://leetcode.com/problems/data-stream-as-disjoint-intervals/

Use binary search to find the insertion point. Then merge the previous and next intervals.


class SummaryRanges:

    def __init__(self):
        """
        Initialize your data structure here.
        """
        
        self.intervals = []

    def addNum(self, val: int) -> None:
        
        left, right = 0, len(self.intervals)
        
        while left < right:
            mid = left + (right -left) // 2
            
            tmp = self.intervals[mid]
            
            if tmp[0] <= val <= tmp[1]:
                return
            
            if tmp[0] > val:
                right = mid
            elif tmp[1] < val:
                left = mid + 1
        
        self.intervals.insert(left, [val, val])
        
        cur = self.intervals[left]
        pre = self.intervals[left-1] if left -1 >= 0 else None
        nxt = self.intervals[left+1] if left + 1 < len(self.intervals) else None
        
        if pre and pre[1] + 1 == cur[0]:
            pre[1] = cur[0]
            cur = pre
            self.intervals.pop(left)
            left -= 1
        
        if nxt and cur[1] + 1 == nxt[0]:
            cur[1] = nxt[1]
            self.intervals.pop(left + 1)
            

    def getIntervals(self) -> List[List[int]]:
            return self.intervals

[LeetCode] 349. Intersection of Two Arrays

 Problem : https://leetcode.com/problems/intersection-of-two-arrays/

Binary search solution: 


class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        
        def bsearch(nums, target):
            left, right = 0, len(nums)
            
            while left < right:
                mid = left + (right - left) // 2
                if nums[mid] == target:
                    return True
                if nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid
            
            return False
        
        result = []
        
        if len(nums1) > len(nums2) : nums1, nums2 = nums2, nums1
        
        nums1.sort()
        nums2.sort()
        
        for i, n in enumerate(nums1):
            if i - 1 >= 0 and nums1[i] == nums1[i-1]:
                continue
            
            if bsearch(nums2, n):
                result.append(n)
            
        
        return result

2 pointers solution:


class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        nums1.sort()
        nums2.sort()
        
        result = []
        
        i = j = 0
        
        while i < len(nums1) and j < len(nums2):
            if nums1[i] < nums2[j]:
                i += 1
            elif nums1[i] > nums2[j]:
                j += 1
            else:
                if not result or result[-1] != nums1[i]:
                    result.append(nums1[i])
                i += 1
                j += 1
        
        return result

Hash table based solution:


class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        seen = defaultdict(lambda : [0, 0])
        
        for n in nums1:
            seen[n][0] += 1
        
        for n in nums2:
            seen[n][1] += 1
            
        
        return filter(lambda n : seen[n][0] >= 1 and seen[n][1] >= 1, seen.keys())

10/17/2020

[LeetCode] 315. Count of Smaller Numbers After Self

 Problem : https://leetcode.com/problems/count-of-smaller-numbers-after-self/

The original array is unsorted. Iterate the input array from the rear and use insert sort to create a sorted sequence.  The insert position indicates the number of smaller elements.

Time Complexity = O ( N * Log N  + N * N ).   Inserting number to array is O(N) time complexity operation.


class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]: 
        def lowerBound(n, dp):
            """
            the first poistion on the left where dp[i] <= n
            """
            left, right = 0, len(dp)
            
            while left < right:
                mid = left + (right - left) // 2
                
                if dp[mid] == n:
                    right = mid
                elif dp[mid] < n:
                    left = mid + 1
                elif dp[mid] > n:
                    right = mid
            
            return right
        
        result = [0] * len(nums)
        dp = []
        
        for i in reversed(range(len(nums))):
            n = nums[i]
            # insert sort
            j = lowerBound(n, dp)
            dp.insert(j, n)
            
            result[i] = j
            
        return result

Use Python build-in lib:


class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        dp = []
        result = [0] * len(nums)
        
        for i in reversed(range(len(nums))):
            
            lowerBound = bisect.bisect_left(dp, nums[i])
            
            result[i] = lowerBound
            
            bisect.insort_left(dp, nums[i])
        
        return result

Use segment tree:


class SegmentTree:
    def __init__(self, m):
        # M = number of leafs of the segement tree
        self.M = m
        
        # Because segement tree is a completed binary tree.
        # If number of leafs = M, then number of internal nodes = M - 1.
        # Because tree[0] will not be used, the total number of nodes = 2 * M
        self.tree = [0] * (2 * self.M)
        
    def update(self, n, delta):
        """
        update counter of number on index n
        """
        
        # number n leaf node index = n + M
        n += self.M
        self.tree[n] += delta
        
        n //= 2
        while n > 0:
            # left child node index = 2 * n
            # right child node index = 2 * n
            self.tree[n] = self.tree[2*n] + self.tree[2*n+1]
            n //= 2
    
    def query(self, left, right):
        """
        query accumulative count between range [left, right).
        the 'right' is excluded.
        """
        
        result = 0
        
        left += self.M
        right += self.M
        
        while left < right:
            if left & 1 == 1:
                # on left side, the left child node is not in the needed range.
                result += self.tree[left]
                left += 1
            
            left //= 2
            
            if right & 1 == 1:
                # on right side, the right child node is not in the needed range
                right -= 1
                result += self.tree[right]
            
            right //= 2
        
        return result

class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        if not nums: return []
        
        mi = min(nums)
        mx = max(nums) + 1
        m = mx - mi + 1
        segmentTree = SegmentTree(m)
        
        # query in segement tree for number of smaller elements to the right of nums[i]
        result = [0] * len(nums)
        for i in reversed(range(len(nums))):
            n = nums[i] - mi
            
            result[i] = segmentTree.query(0, n)
            segmentTree.update(n, 1)
        
        return result

Use fenwick tree:


class FenwickTree:
    def __init__(self, m):
        self.tree = [0] * m
    
    def update(self, n, delta):
        while n < len(self.tree):
            self.tree[n] += delta
            n += self.lowbit(n)
    
    def query(self, n):
        result = 0
        
        while n > 0 :
            result += self.tree[n]
            n -= self.lowbit(n)
        
        return result
            
    
    def lowbit(self, n):
        return n & (~(n-1))
        
class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        if not nums: return []
        
        mi = min(nums)
        mx = max(nums) + 1
        m = mx - mi + 1
        
        fenwickTree = FenwickTree(m)
        
        result = [0] * len(nums)
        
        for i in reversed(range(len(nums))):
            # fenwick tree node index starts with 1
            n = nums[i] - mi + 1
            
            result[i] = fenwickTree.query(n-1)
            fenwickTree.update(n, 1)
        
        return result

Use merge sort approach.

Split the nums array into left and right parts.

Sort the right part first, then for each number on the left part, use binary-search to find count of number less than it. (lower-bound)


class Solution:
    def countSmaller(self, nums: List[int]) -> List[int]:
        if not nums: return []
        
        result = [0] * len(nums)
        
        def mergeSort(left, right):
            if left + 1 == right: 
                return 

            mid = left + (right - left) // 2

            # sort right side
            mergeSort(mid, right)

            # for each number on left side, find
            # how many number is smaller than it on right side.
            for i in range(left, mid):
                lowerBound = bisect.bisect_left(nums, nums[i], mid, right)
                # count of smaller numbers = lowerBound - mid
                result[i] += lowerBound - mid
            
            # sort left side
            mergeSort(left, mid)
            
            # merge the sorted left and right side
            tmp = []
            i = left
            j = mid

            while i < mid and j < right:
                if nums[i] < nums[j]:
                    tmp.append(nums[i])
                    i += 1
                else:
                    tmp.append(nums[j])
                    j += 1

            while i < mid:
                tmp.append(nums[i])
                i += 1

            while j < right:
                tmp.append(nums[j])
                j += 1

            for i in range(right - left):
                nums[left+i] = tmp[i]
        
        # merge sort starts
        mergeSort(0, len(nums))
        
        return result

10/08/2020

[LeetCode] 300. Longest Increasing Subsequence

 Problem : https://leetcode.com/problems/longest-increasing-subsequence/

DP Solution

Let DP[i] = longest increasing sequence end at i, then DP[i] = DP[j] + 1 if Nums[i] > Nums[j]

Time Complexity = O ( N ** 2 )


class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        if not nums:
            return 0
        
        N = len(nums)
        dp = [1] * N
        
        for i in range(1, N):
            for j in reversed(range(i)):
                if nums[i] > nums[j]:
                    dp[i] = max(dp[i], 1 + dp[j])
        
        return max(dp)

DP + Binary Search Solution

DP is a cache to store the increasing sequence.

Iterate the input array and pick number from left to right to insert into DP.

lowerBound method returns the position of the newly added number should be inserted into DP to maintain an increasing sequence.  Replace existing number if the returned position does not expand DP. Otherwise, append the new number to DP.

The final length of DP is the length of the longest increasing subsequence.  However, DP is not the longest increasing subsequence.


Time Complexity = O ( N * LogN )


class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        
        def lowerBound(nums, n):
            """
            return the first position from left where nums[i] <= n
            return 0 if all items in nums > n
            return len(nums) if all items in nums < n
            """
            
            left, right = 0, len(nums)
            
            while left < right:
                mid = left + (right - left) // 2
                
                if nums[mid] == n:
                    right = mid
                elif nums[mid] < n:
                    left = mid + 1
                elif nums[mid] > n:
                    right = mid
                
            return left
        
        
        dp = []
        
        for n in nums:    
            i = lowerBound(dp, n)
            if i == len(dp):
                dp.append(n)
            else:
                dp[i] = n
        
        return len(dp)

Use built-in binary-search lib:


class Solution:
    def lengthOfLIS(self, nums: List[int]) -> int:
        dp = []
        
        for n in nums:
            i = bisect.bisect_left(dp, n)
            if i == len(dp):
                dp.append(n)
            else:
                dp[i] = n
        
        return len(dp)

Edited on 02/06/2021. Add implementation with built-in binary search lib.

[LeetCode] 295. Find Median from Data Stream

 Problem : https://leetcode.com/problems/find-median-from-data-stream/

Use insert sort to keep the data cache in ascending order.

Then median = ( nums[len(nums) // 2 ] + nums[(len(nums) - 1) // 2] ) / 2

Time Complexity = O ( Log N ) +  O ( N ) ≈ O ( N )


class MedianFinder:

    def __init__(self):
        """
        initialize your data structure here.
        """
        self.nums = []
        

    def addNum(self, num: int) -> None:
        
        def lowerBoundary(n):
            left, right = 0, len(self.nums)
            
            while left < right:
                mid = left + (right - left) // 2
                
                if self.nums[mid] == n:
                    right = mid
                elif self.nums[mid] < n:
                    left = mid + 1
                elif self.nums[mid] > n:
                    right = mid
            
            return left if left - 1 >= 0 else 0
            
        # insert sort   
        self.nums.insert(lowerBoundary(num), num)
        

    def findMedian(self) -> float:
        
        l = len(self.nums)
        
        return (self.nums[l//2] + self.nums[(l-1)//2]) / 2.0
        


# Your MedianFinder object will be instantiated and called as such:
# obj = MedianFinder()
# obj.addNum(num)
# param_2 = obj.findMedian()

9/20/2020

[LeetCode] 278. First Bad Version

Problem : https://leetcode.com/problems/first-bad-version/

Time complexity = O ( Log N )

# The isBadVersion API is already defined for you.
# @param version, an integer
# @return an integer
# def isBadVersion(version):

class Solution:
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        
        left, right = 0, n
        
        while left <= right:
            mid = left + (right - left) // 2
            
            if not isBadVersion(mid):
                left = mid + 1
            else:
                right = mid - 1

        return left

[LeetCode] 275. H-Index II

 Problem : https://leetcode.com/problems/h-index-ii/


class Solution:
    def hIndex(self, citations: List[int]) -> int:
        N = len(citations)
        left, right = 0, N - 1
        
        while left <= right:
            mid = left + (right - left) // 2    
            h = N - mid
                        
            if citations[mid] == h:
                return h
            
            if citations[mid] < h:
                left = mid + 1
            else:
                right = mid - 1
        
        return N - left

9/17/2020

[LeetCode] 240. Search a 2D Matrix II

Problem : https://leetcode.com/problems/search-a-2d-matrix-ii/

Because each row and column is sorted in ascending order.   One number is always larger than the number on its left and smaller than the number underneath. We can iterate from the top-right number. If the number is larger than the target, move the point to left side. If the number is smaller than the target value, move the point to down side.


class Solution:
    def searchMatrix(self, matrix, target):
        """
        :type matrix: List[List[int]]
        :type target: int
        :rtype: bool
        """
        
        ROW = len(matrix)
        if ROW == 0:
            return False
    
        COL = len(matrix[0])
        if COL == 0:
            return False
        
        if target < matrix[0][0]  or matrix[-1][-1] < target:
            return False
        
        y = 0 
        x = COL - 1
        
        while x >= 0 and y < ROW:
            if matrix[y][x] > target:
                # move pointer to left side
                x -= 1
            elif matrix[y][x] < target:
                # move pointer to down side
                y += 1
            else:
                return True
        
        return False

9/13/2020

[LeetCode] 220. Contains Duplicate III

Problem : https://leetcode.com/problems/contains-duplicate-iii/

Maintain a section which length is K. 
For every newly inserted number nums[i], we look for is there any number >= abs(nums[i] - t).
Because the larger the 'target', the smaller 't' is.

import bisect

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        j = 0
        section = []
        for i in range(len(nums)):
            
            if i - j > k:
                # shift the section window to right
                a = bisect.bisect_left(section, nums[j])
                section.pop(a)
                j += 1
                
            target = abs(nums[i] - t)
            
            # find is there any number larger or equal to 'target'
            # a larger 'target' leads a smaller 't'
            a = bisect.bisect_left(section, target)
            if a != len(section) and abs(section[a] - nums[i]) <= t:
                return True
            
            a = bisect.bisect_left(section, -target)
            if a != len(section) and abs(section[a] - nums[i]) <= t:
                return True
            
            bisect.insort(section, nums[i])
        
        return False

Treeset solution


class TreeSet:
    def __init__(self):
        self.sortedNums = []
        
    def add(self, n):
        bisect.insort(self.sortedNums, n)
        
    def remove(self, n):
        idx = bisect.bisect_left(self.sortedNums, n)
        if idx < len(self.sortedNums) and self.sortedNums[idx] == n:
            self.sortedNums.pop(idx)
    
    def ceiling(self, n):
        idx = bisect.bisect_right(self.sortedNums, n)
        
        if idx == len(self.sortedNums) or self.sortedNums[idx] < n: return None
                       
        return self.sortedNums[idx]     
     
    def floor(self, n):
        idx = bisect.bisect_left(self.sortedNums, n)
        
        if idx == len(self.sortedNums):
            if idx - 1 >= 0:
                return self.sortedNums[idx-1]
            else:
                return None
        
        if self.sortedNums[idx] == n: return n
        
        return self.sortedNums[idx-1] if idx -1 >= 0 else None

class Solution:
    def containsNearbyAlmostDuplicate(self, nums: List[int], k: int, t: int) -> bool:
        treeset = TreeSet()
        
        for i in range(len(nums)):
            if i - 1 - k >= 0:
                treeset.remove(nums[i - 1 -k])
                
            s = treeset.ceiling(nums[i])
            if s != None and s <= nums[i] + t: 
                return True
            
            g = treeset.floor(nums[i])
            if g != None and g + t >= nums[i]:
                return True
                      
            treeset.add(nums[i])
            if i - k >= 0:
                treeset.remove(nums[i - k])
            
        return False

Updated 05/02/2021. Add treeset solution.

7/26/2020

[LeetCode] 162. Find Peak Element

Problem :  https://leetcode.com/problems/find-peak-element/

Time Complexity : O ( Log N )
class Solution:
    def findPeakElement(self, nums: List[int]) -> int:
        left, right = 0, len(nums) - 1
        
        if len(nums) <= 1:
            return 0
        
        if nums[left] > nums[left+1]:
            return left
        
        if nums[right] > nums[right-1]:
            return right
        
        while left < right:
            mid = left + (right - left)
            
            if nums[mid-1] < nums[mid] > nums[mid+1]:
                return mid
            elif nums[mid-1] < nums[mid] < num[mid+1]:
                # peak exists on the end of the ascending sequence
                left = mid + 1
            else:
                # peak exists on the begining of descending sequence
                right = mid - 1
        
        return right

[LeetCode] 154. Find Minimum in Rotated Sorted Array II

Problem : https://leetcode.com/problems/find-minimum-in-rotated-sorted-array-ii/

Time Complexity = O( Log N )

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = 0, len(nums) - 1
        
        while left < right:
            mid = left + (right - left) // 2
            
            if nums[mid] > nums[right]:
                left = mid + 1
            elif nums[mid] < nums[right]:
                right = mid
            elif nums[mid] == nums[right]:
                while right > mid and nums[mid] == nums[right]:
                    right -= 1
        
        return nums[right]

[LeetCode] 153. Find Minimum in Rotated Sorted Array

Problem : https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/

Binary search solution:

class Solution:
    def findMin(self, nums: List[int]) -> int:
        left, right = 0, len(nums) - 1
        
        while left < right:
            mid = left + (right - left) // 2
            
            if nums[mid] > nums[right]:
                left = mid + 1
            else:
                right = mid
        
        return nums[right]

Divide and conquer solution:

class Solution:
    def findMin(self, nums: List[int]) -> int:
        
        def helper(left, right):
            if nums[left] <= nums[right]:
                return nums[left]
            
            mid = left + (right - left) // 2
            
            return min(helper(left, mid), helper(mid+1, right))
        
        return helper(0, len(nums) - 1)