LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置

34. 在排序数组中查找元素的第一个和最后一个位置

题目:给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题

链接 https://leetcode.cn/problems/search-insert-position/

个人思路
  1. 思路1:想着先转化为,然后使用字符串的find,和rfind就解决了,结果会出现以下错误,看来想的还是太简单了
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        b = [str(i) for i in nums]
        c = ''.join(b)
        return [c.find(str(target)),c.rfind(str(target))]

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置_第1张图片

  1. 思路2:先进行一次二分法得到target所在的一个位置,然后对[0,target]和[target,len(nums)-1]分别进行一次二分就能得到左右边界位置,代码如下:
class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if target not in nums:
            return [-1,-1]
        length = len(nums)
        if length == 1:
            return [0,0]
        # 储存答案
        ans = []
        # 查找左边边界
        def lFind(l,rm):
            while rm >= l:
                mid = (l + rm) // 2
                if nums[mid] == target and nums[mid-1] < target:
                    ans.append(mid)
                    break
                elif nums[mid] < target:
                    l = mid + 1
                else:
                    rm =  mid
        # 查找右边边界
        def rFind(lm,r):
            while r >= lm:
                mid = (lm + r) // 2
                print(lm,mid,r)
                if nums[mid] == target and nums[mid+1] > target:
                    ans.append(mid)
                    break
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    lm =  mid + 1
        # 找一个三分位点
        # 双指针left,right
        left = 0
        right = length
        mid = (left + right) // 2
        while right > left:
            mid = (left + right) // 2
            if nums[mid] == target:
                break
            elif nums[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        lFind(0,mid)
        rFind(mid,length)
        return ans

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置_第2张图片
当数字都为target时,陷入死循环超时了
修改:出现左右大边界都为target时候,直接返回

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if target not in nums:
            return [-1,-1]
        length = len(nums)
        if length == 1:
            return [0,0]
        # 储存答案
        ans = []
        # 查找左边边界
        def lFind(l,rm):
            while rm >= l:
                mid = (l + rm) // 2
                if nums[mid] == target and nums[mid-1] < target:
                    ans.append(mid)
                    break
                elif nums[mid] < target:
                    l = mid + 1
                else:
                    rm =  mid - 1
        # 查找右边边界
        def rFind(lm,r):
            while r >= lm:
                mid = (lm + r) // 2
                print(lm,mid,r)
                if nums[mid] == target and nums[mid+1] > target:
                    ans.append(mid)
                    break
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    lm =  mid + 1
        # 找一个三分位点
        # 双指针left,right
        left = 0
        right = length
        mid = (left + right) // 2
        # 不加等于容易陷入循环或者得不到准确答案
        while right >= left:
            mid = (left + right) // 2
            if nums[mid] == target:
                break
            elif nums[mid] > target:
                right = mid - 1
            else:
                left = mid + 1
        if nums[0] == target:
            ans.append(0)
        else:
            lFind(0,mid)
        if nums[length-1] == target:
            ans.append(length-1)
        else:
            rFind(mid,length)
        return ans

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置_第3张图片
3. 还可以对思路2进行优化,我们在第一次进行二分时,也缩小了左右边界,如果能记录下来后面就可以减少后面二分的次数

class Solution:
    def searchRange(self, nums: List[int], target: int) -> List[int]:
        if target not in nums:
            return [-1,-1]
        length = len(nums)
        if length == 1:
            return [0,0]
        # 储存答案
        ans = []
        # 查找左边边界
        def lFind(l,rm):
            while rm >= l:
                mid = (l + rm) // 2
                if nums[mid] == target and nums[mid-1] < target:
                    ans.append(mid)
                    break
                elif nums[mid] < target:
                    l = mid + 1
                else:
                    rm =  mid
        # 查找右边边界
        def rFind(lm,r):
            while r >= lm:
                mid = (lm + r) // 2
                print(lm,mid,r)
                if nums[mid] == target and nums[mid+1] > target:
                    ans.append(mid)
                    break
                elif nums[mid] > target:
                    r = mid - 1
                else:
                    lm =  mid + 1
        # 找一个三分位点
        # 双指针left,right
        left = 0
        right = length
        mid = (left + right) // 2
        # 记录第一次二分时候初步的左右边界
        l = 0
        r = length-1
        while right >= left:
            mid = (left + right) // 2
            if nums[mid] == target:
                break
            elif nums[mid] > target:
                right = mid - 1
                r = right
            else:
                left = mid + 1
                l = left
        if nums[l] == target:
            ans.append(l)
        else:
            lFind(l,mid)
        if nums[r] == target:
            ans.append(r)
        else:
            rFind(mid,r)
        return ans

LeetCode 34. 在排序数组中查找元素的第一个和最后一个位置_第4张图片
从结果来看,优化结果还不错

官方思路
  1. 也是二分法
    (1)考虑target 开始和结束位置,其实我们要找的就是数组中「第一个等于target 的位置」(记为leftIdx)和「第一个大于 target 的位置减一」(记为rightIdx)。
    (2)二分查找中,寻找leftIdx 即为在数组中寻找第一个大于等于target 的下标,寻找rightIdx 即为在数组中寻找第一个大于 target 的下标,然后将下标减一。两者的判断条件不同,为了代码的复用,我们定义 binarySearch(nums, target, lower) 表示在nums 数组中二分查找target 的位置,如果 lower 为true,则查找第一个大于等于target 的下标,否则查找第一个大于 target 的下标。
    (3)最后,因为 target 可能不存在数组中,因此我们需要重新校验我们得到的两个下标leftIdx 和rightIdx,看是否符合条件,如果符合条件就返回 [leftIdx,rightIdx],不符合就返回 [-1,-1]。

作者:LeetCode-Solution
链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/solution/zai-pai-xu-shu-zu-zhong-cha-zhao-yuan-su-de-di-3-4/
来源:力扣(LeetCode)

你可能感兴趣的:(LeetCode,leetcode,算法,数据结构)