Find First and Last Position of Element in Sorted Array


🤖This article was translated by AI (LLM). There may be errors or inaccuracies. For the original content, please refer to the original version.

Problem URL: https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/

Essentially a binary search problem, searching for the start and end positions

Solution Approaches

Recursive Method

Code

def check_nums_middle(target: int, nums: list, start_position: int, stop_position: int):
    if start_position == stop_position:
        if target == nums[start_position]:
            return start_position, stop_position
        else:
            return [-1, -1]
    elif start_position + 1 == stop_position:
        r_start_position = -1
        r_stop_position = -1
        if nums[start_position] == target:
            r_start_position = start_position
            r_stop_position = start_position
        if nums[stop_position] == target:
            if r_start_position == -1:
                r_start_position = stop_position
            r_stop_position = stop_position
        return r_start_position, r_stop_position
    else:
        middle_position = int((start_position + stop_position) / 2)
        middle_position_num = nums[middle_position]
        if target > middle_position_num:
            return check_nums_middle(target, nums, middle_position, stop_position)
        elif target < middle_position_num:
            return check_nums_middle(target, nums, start_position, middle_position)
        else:  # Middle value equals target
            left_position_range, left_r = check_nums_middle(
                target, nums, start_position, middle_position)
            right_l, right_position_range = check_nums_middle(
                target, nums, middle_position + 1, stop_position)
            if left_r == -1:
                return right_l, right_position_range
            elif right_l == -1:
                return left_position_range, left_r
            else:
                return left_position_range, right_position_range


def main(target: int, nums: list) -> list:
    if len(nums) == 0:
        return [-1, -1]
    left_position_range, right_position_range = check_nums_middle(
        target, nums, 0, len(nums) - 1)
    return [left_position_range, right_position_range]

LeetCode Test

LeetCode test for recursive approach

Iterative Approach

When the list is sufficiently long, recursion may cause tail call issues, while iteration avoids this

Code

def for_num_check(target: int, nums: list) -> list:
    start_position = 0
    stop_position = len(nums) - 1
    # Find middle value equal to target
    while True:
        middle_position = int((start_position + stop_position) / 2)
        #print(f"find target: {start_position}, {stop_position}")
        middle_position_num = nums[middle_position]
        if target == middle_position_num:
            break
        # Number doesn't exist
        elif stop_position - start_position <= 1:
            if target == nums[stop_position]:
                middle_position = stop_position
                break
            else:
                return [-1, -1]
        elif target > middle_position_num:
            start_position = middle_position + 1
        else:
            stop_position = middle_position - 1

    # Determine left and right ranges
    left_start_position = start_position
    left_stop_position = middle_position - 1
    #print(f"target {middle_position}")
    #print(f"left: {left_start_position}, {left_stop_position}")
    if left_start_position <= left_stop_position:
        left_position = left_num_check(target, nums, left_start_position,
                                        left_stop_position)
        if left_position == -1:
            left_position = middle_position
    else:
        left_position = middle_position
    #print(f"left: {left_position}")

    right_start_position = middle_position + 1
    right_stop_position = stop_position
    #print(f"right: {right_start_position}, {right_stop_position}")
    if right_start_position <= right_stop_position:
        right_position = right_num_check(target, nums, right_start_position,
                                          right_stop_position)
        if right_position == -1:
            right_position = middle_position
    else:
        right_position = middle_position
    #print(f"right: {right_position}")

    return [left_position, right_position]


# Find leftmost target value
def left_num_check(target: int, nums: list, start_position: int,
                   stop_position: int) -> int:
    if nums[stop_position] != target:
        return -1
    while True:
        middle_position = int((start_position + stop_position) / 2)
        middle_position_num = nums[middle_position]
        if middle_position == start_position:
            if target == middle_position_num:
                return middle_position
            else:
                return stop_position
        elif target == middle_position_num and\
                target > nums[middle_position - 1]:
            return middle_position
        elif target == middle_position_num:
            stop_position = middle_position - 1
        else:
            start_position = middle_position + 1


# Find rightmost target value
def right_num_check(target: int, nums: list, start_position: int,
                    stop_position: int) -> int:
    if nums[start_position] != target:
        return -1
    while True:
        middle_position = int((start_position + stop_position) / 2)
        middle_position_num = nums[middle_position]
        if middle_position == stop_position:
            if target == middle_position_num:
                return middle_position
            else:
                return start_position
        elif target == middle_position_num and\
                target < nums[middle_position + 1]:
            return middle_position
        elif target == middle_position_num:
            start_position = middle_position + 1
        else:
            stop_position = middle_position - 1


def main(target: int, nums: list) -> list:
    if len(nums) == 0:
        return [-1, -1]
    return for_num_check(target, nums)

LeetCode Test

LeetCode test results for iterative approach

Testing Method

print(main(0, [0, 0, 0, 0, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 8, 9, 9, 10, 10, 11, 11]))

Summary

Iteration has some advantages (like avoiding tail calls) over recursion, but it’s more complex to write and prone to errors. In practical applications (when not writing general-purpose libraries), it’s not recommended 🙁