二分查找-6.3 元素出现的最左边位置

题目:

对于一个有序数组arr,再给定一个整数num,请在arr中找到num这个数出现的最左边的位置。

给定一个数组arr及它的大小n,同时给定num。请返回所求位置。若该元素在数组中未出现,请返回-1。

测试用例:

[1,2,3,3,4],5,3
返回:2

思路: 

二分查找的变种。标准的二分查找有3个分支:arr[mid] == num; arr[mid] < num; arr[i] > num,最终目的是找到一个数。就这题来说,我们希望找到找到有序数组中num出现的第一个位置,那么当arr[mid] >= num 时, mid和mid右边的元素都大于或等于num,应该从mid左边的元素中取寻找;如果arr[mid] < num,那么所有小于等于mid的元素一定不是num,应该从mid+1开始的右边元素中寻找。所以这题需要两个分支。

二分查找最大的难点还是在于代码实现。在二分查找中,最重要的就是维护查找区间的开闭性一致,以及什么时候退出循环

假设在进入循环前,取left = 0, right = len(arr)-1,说明我们的查找区间是一个闭区间[0 : len(arr)-1]。那么应该保持循环条件left <= right。在循环的过程中,如果arr[left] < arr[mid],让left = mid+1,如果arr[right] >= arr[mid], right = mid-1查找区间的变化等价于[mid+1 : right] 或 [left : mid-1],当left > right时,查找区间长度为0,此时跳出循环。无论是因为 left == right 时 arr[left] < num导致了 left+1 还是 arr[right] >= num 导致了 right-1,都可以对arr[righ+1] 或是 arr[left]进行判断。当然,还需要判断特殊情况:即num大于有序数组中的所有数,left = len(arr)。

假设在进入循环前,取left = 0, right = len(arr),说明我们的查找区间是一个闭区间[0 : len(arr)-1)。那么应该保持循环条件left < right。在循环的过程中,如果arr[left] < arr[mid],让left = mid+1,如果arr[right] >= arr[mid], right = mid查找区间的变化等价于[mid+1 : right) 或 [left : mid),当left == right时,查找区间长度为0,此时跳出循环。因为 arr[mid] >= num 时,右边界维持不动,所以循环结束时right及right右边的数是大于等于num的,而left左边的数是小于num的,且left == right,所以应该判断arr[left] 或 arr[right]。 同样需要特殊判断

代码: 

# -*- coding:utf-8 -*-

class LeftMostAppearance:
    def findPos(self, arr, n, num):
        # 保持闭区间
        left, right = 0, n-1
        while left <= right:
            mid = left + (right-left)//2
            if arr[mid] < num:
                left = mid+1
            else:
                right = mid-1
        if left >= n:
            return -1
        if arr[left] == num:
            return left
        else:
            return -1

class LeftMostAppearance:
    def findPos(self, arr, n, num):
        # 保持左闭右开区间
        left, right = 0, n
        while left < right:
            mid = left + (right-left)//2
            if arr[mid] < num:
                left = mid+1
            else:
                right = mid
        if left >= n:
            return -1
        if arr[left] == num:
            return left
        else:
            return -1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值