【leetcode-Python】-二分搜索-34 Find First and Last Position of Element in Sorted Array

这篇博客详细介绍了如何使用二分搜索算法在非降序整数数组中寻找指定目标元素的起始和结束索引。文章通过三种不同的解决思路,包括基本的二分搜索变体,以及优化后的搜索方法,展示了如何有效地找到目标元素的边界。在每种方法中,作者都提供了Python实现,并特别强调了边界条件的处理和特殊情况的检查,以确保算法的正确性和效率。
摘要由CSDN通过智能技术生成

目录

 

题目链接

题目描述

示例

解决思路一

解决思路一Python实现

解决思路二

解决思路二Python实现

解决思路三

解决思路三Python实现


题目链接

34. Find First and Last Position of Element in Sorted Array

题目描述

给定非降序整数数组nums,找出元素值等于target的索引区间。如果数组中不存在target,返回[-1,-1]。

约束条件:

  • 0 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
  • nums is a non-decreasing array.
  • -10^9 <= target <= 10^9

示例

输入:nums = [5,7,7,8,8,10],target = 8

输出:[3,4]

输入:nums = [5,7,7,8,8,10],target = 6

输出:[-1,-1]

输入:nums = [],target = 0

输出:[-1,-1]

解决思路一

1、先处理特例,当nums为空时,任何target都无法在nums中被找到,因此直接返回[-1,-1]。

2、利用二分搜索算法找出目标索引区间的左端点,即数组中第一个等于target的元素索引值start,如果找不到设置start为-1。

3、找出目标索引区间的右端点,即数组中最后一个等于target的元素索引值end,如果找不到设置end为-1。具体策略为找出数组中第一个大于target的元素nums[left],如果nums[left]是数组的第一个元素,设置end = -1,如果nums[left-1]存在,检验nums[left-1]是否等于target,如果不等于说明target在数组中不存在,则设置end = -1。

解决思路一Python实现

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        #首先找出starting position
        if(len(nums) == 0):
            return [-1,-1]
        left = 0
        right = len(nums)
       
        while(left < right):
            mid = (left + right) // 2
            if(nums[mid] >= target):
                right = mid
            else:
                left = mid + 1
        if(left != len(nums) and nums[left] == target):
            start = left
        else:
            start = -1
        
        #find the ending position 
        left = 0
        right = len(nums)
        while(left < right):
            mid = (left + right) // 2
            if(nums[mid] > target):
                right = mid
            else:
                left = mid + 1
        end = left - 1 #left为num中第一个大于target的索引,该索引减去1的结果可能是元素值等于target的最大索引。
        if(left != 0 and nums[end] != target): #对left进行检验,如果end索引能够访问并且nums[end]不等于target,说明数组中不存在target
            end = -1
        return [start,end]

在寻找目标序列的右端点时,如果设置right初始化为len(nums)-1,当left == right 条件满足跳出循环时,需要判断nums[left]是否等于target(因为有可能right没有移动,left一直移动到和right重合等于len(nums)-1,nums[left]有可能等于target),如果等于,那么end应该设为left。 代码如下,但是因为后续要考虑的东西更多,不推荐这种初始化方式(如果搜索条件不涉及到对nums[right]的访问,right变量初始化尽量都设置为len(nums),按照标准配置来写不容易出现边界错误)。

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        #首先找出starting position
        if(len(nums) == 0):
            return [-1,-1]
        left = 0
        right = len(nums)  
        while(left < right):
            mid = (left + right) // 2
            if(nums[mid] >= target):
                right = mid
            else:
                left = mid + 1
        if(left != len(nums) and nums[left] == target):
            start = left
        else:
            start = -1
        
        #find the ending position 
        left = 0
##############right初始化值被修改###################
        right = len(nums)-1
        while(left < right):
            mid = (left + right) // 2
            if(nums[mid] > target):
                right = mid
            else:
                left = mid + 1
        #当left == right时跳出循环,如果right被初始化为len(nums)-1,那么跳出循环后需要对nums[left]进行检验
        if(nums[left] == target):
            left += 1
        end = left - 1 #left为num中第一个大于target的索引,该索引减去1的结果可能是元素值等于target的最大索引。
        if(left != 0 and nums[end] != target): #对end进行检验,如果end索引能够访问并且nums[end]不等于target,说明数组中不存在target
            end = -1
        return [start,end]

解决思路二

1、寻找目标区间的左边界,并根据左边界的寻找情况判断数组中是否存在target(如果没有找到左边界,说明数组中不存在值等于target的元素)。

2、在左边界存在的前提下,寻找右边界(找出数组中第一个大于target的元素,该元素的索引减去1即为右边界)。

设置一个findRange函数,传入nums数组和is_left变量。如果is_left为True,说明findRange函数找的是左边界,当nums[mid]==target时,由right变量暂存mid同时在[left,mid)区间继续搜索(因为寻找的是左边界,左边界要么是mid要么在mid的左边);如果is_left为False,说明findRange函数寻找的是第一个比target大的元素(右边界+1),因此当nums[mid]==target时,更新left = mid + 1,在mid右边进行搜索。

解决思路二Python实现

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        def findRange(nums,is_left):
            left = 0
            right = len(nums)
            while(left<right):
                mid = (left + right) // 2
                if(nums[mid] > target or (nums[mid] == target and is_left)):
                    right = mid
                else:
                    left = mid + 1
            return left
        
        left_index = findRange(nums,is_left = True)
        if left_index == len(nums) or nums[left_index] != target:
            return [-1,-1]
        right_index = findRange(nums,is_left = False)
        return [left_index,right_index-1]

抽取出寻找右边界的代码:

def findRange(nums):
    left = 0
    right = len(nums)
    while(left<right):
        mid = (left + right) // 2
        if(nums[mid] > target):
            right = mid
        else:
            left = mid + 1
    if(left == 0):
        return -1
    return nums[left-1] if nums[left-1] == target else -1

解决思路三

直接利用二分搜索来寻找右边界,不需要先找第一个大于target的元素再令其索引值减1。

“用二分搜索法寻找右边界”和“用二分搜索法寻找左边界”属于镜像问题。用二分搜索法寻找左边界的思路为:循环跳出条件为left == right,计算mid = (left + right) //2,当nums[mid] >= target时,更新right = mid,由right暂存可能的左边界;否则更新left = mid + 1。最后对nums[left]进行判断。代码如下:

def findLeftRange(nums):
    left = 0
    right = len(nums)
    while(left<right):
        mid = (left + right) // 2
        if(nums[mid] >= target):
            right = mid
        else:
            left = mid + 1
    if(left != len(nums) and nums[left] == target):   
        return left
    return -1

用二分搜索算法寻找右边界(类似找nums[i]<=target的右边界)的思路为:循环跳出条件为left == right,计算mid = (left + right) // 2 + 1,当nums[mid] > target时,right = mid - 1,当nums[mid] <= target时,left = mid,由left暂存可能的右边界。mid的计算方式设置为(left + right) // 2 + 1的原因为,当left + 1 == right时,按照mid = (left + right) // 2来计算,mid和left重合,如果满足nums[mid] <= target,仍然赋值left = mid,这样left、right都没有移动,会陷入死循环。如果按照 mid = (left + right) // 2 + 1来计算,当left + 1 == right时,mid和right重合,无论nums[mid]和target的大小关系是怎样的,循环都能终止(但需要将right初始化为len(nums)-1,否则当mid和right重合且right = len(nums)时对nums[mid]的访问会报错)。

代码如下:

def findRightRange(nums):
    left = 0
    right = len(nums)-1 
    while(left<right):
        mid = ((left + right) // 2) + 1
        if(nums[mid] <= target):
            left = mid
        else:
            right = mid - 1
    if(right != -1 and nums[right] == target):   
        return right
    return -1

解决思路三Python实现

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


        
            
        

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值