Array, LeetCode

704. Binary Search

To calculate the middle index: use (left + right) // 2, where // denotes floor division. When the number of elements is even, the fomular returns the left-middle index.

Optimization: left + (right - left) // 2 - Avoid integer overflow caused by a too large right + left through interval offset

Why O(log(n))?

  • Each iteration eliminates half of elements
  • Search Space shrinks exponentially
    • 1st iteration: n elements
    • 2st iteration: n/2 elements
  • At most log(n) comparisons are needed
    • 2^k = n, log(n) = k
  • Stop in advance (nums[middle] == target) before checking all search space (left == right)
from typing import List
class Solution:
    def search(self, nums: List[int], target: int) -> int:
        left = 0
        right = len(nums) - 1
        while left <= right:
            middle = left + (right - left)//2
            if target < nums[middle]: 
                # target is smaller, search the left half and update the right boundary
                right = middle - 1
            elif target > nums[middle]: 
                # target is bigger, search the right half and update the left boundary
                left = middle + 1
            else:
                return middle
        return -1

27. Remove Element

Run test cases:

a = [1, 2, 3]
b = [2, 3, 4]
for i in range(len(a)):
    assert a[i] == b[i] # assertion error

The algorithm aims to remove elements. Using nums[j] == val finds elements to delete, but deletion is essentially “doing nothing”. The elements that actually need operations are those where nums[j] != val (preserve and move postions)

from typing import List
class Solution:
    def removeElement(self, nums: List[int], val: int) -> int:
        slow = 0
        for fast in range(len(nums)):
            if nums[fast] != val: # we need to do some operations 
                nums[slow] == nums[fast]
                slow += 1
        return slow

977. Squares of a Sorted Array

# left, right pointers:
left = 0
right = len(nums) - 1
while left <= right:
    # dynamic boundaries
    # last iteration: left == right (can terminate early)
# slow, fast pointers:
slow = 0
for fast in range(len(nums)):
    # complete traversal (counldn't terminate early)

In-place: algorithms that do not use additional data structures to stroe data during execution, but instead modify the original input data structure directly.

Space Complexity:

  • In-place: O(1)
  • Not in-place: O(n), additional array

Why not in-place in this question?

  • Position changes are very complex (lacking a fixed direction or fixed pattern). Cross movements are difficult to accomplish with simple (two) pointer opertaions
from typing import List
class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        left = 0
        right = len(nums) - 1
        result = [0] * len(nums)
        position = len(nums) - 1
        while left <= right:
            left_square = nums[left] ** 2
            right_square = nums[right] ** 2
            if left_square <= right_square: # write '<' first, then check whether '=' works 
                result[position] = right_square
                position -= 1
                right -= 1
            else:
                reslut[position] = left_square
                position -= 1
                left += 1
        return result

209. Minimum Size Subarray Sum

In a loop (e.g., while, for):

  • record the current valid result first
  • update the state
    while sum_of_subarray >= target:
     min_length = max(min_length, fast - slow + 1) 
     # record min_length
     sum_of_subarray -= nums[slow] 
     # update the sum_of_subarray
     # record nums[slow] based on slow first
     slow += 1 
     # update slow
     # use it, and then update it
    

right - left actually calculates the length of the interval [left, right)

from typing import List
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        min_length = float('inf')
        sub_of_subarray = 0
        slow = 0
        for fast in range(len(nums)):
            sub_of_subarray += nums[fast]

            while sub_of_subarray >= target:
                min_length = max(min_length, fast - slow + 1)
                sum_of_array -= nums[slow]
                slow += 1
        return min_length if min_length != float('inf') else 0

59. Spiral Matrix II

for _ in range(n): iterates n times

for _ in range(left, right): iterates right - left times -> [left, right)

for _ in range(left, right, -1): iterates abs(right - left) times -> (right, left]

n * n matrix: every row, every column has n elements

[0] * n: has n elements in total

from typing import List
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        matrix = [[0] * n for _ in range(n)]
        left = 0
        right = n - 1
        top = 0
        bottom = n - 1
        count = 1
        while left <= right and top <= bottom:
            for i in range(left, right + 1):
                matrix[top][i] = count
                count += 1
            top += 1 # update the boundary when all elemenets in a certain row/column is handled
            if top > bottom:
                break
                
            for i in range(top, bottom + 1):
                matrix[i][right] = count
                count += 1
            right -= 1
            if left > right:
                break

            for i in range(right, left - 1, -1):
                matrix[bottom][i] = count
                count += 1
            bottom -= 1
            if top > bottom:
                break
                
            for i in range(bottom, top - 1, -1):
                matrix[i][left] = count
                count += 1
            left += 1
            if left > right:
                break
        return matrix

327. Count of Range Sum

"""
prefix_sum
nums = [1, 2, 3]
prefix_sum = [0, 1, 3, 6]
prefix_sum[i] = nums[0] + ... nums[i - 1]
prefix_sum[j + 1] = nums[0] + ... nums[i - 1] + nums[i] + ... + nums[j]
prefix_sum[j + 1] - prefix_sum[i] = nums[i] + ... nums[j]
"""
prefix_sum =[0]
for num in nums:
    prefix_sum.append(prefix_sum[-1] + num)
# sum of interval [i, j] could be represented as prefix_sum[j + 1] - prefix_sum[i]



Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Binary Tree, LeetCode
  • NVIDIA, LeetCode
  • Stack and Queue, LeetCode
  • String, LeetCode
  • HashMap, LeetCode