zoukankan      html  css  js  c++  java
  • 34在排序数组中查找元素的第一个和最后一个位置

    题目:给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
    你的算法时间复杂度必须是 O(log n) 级别。如果数组中不存在目标值,返回 [-1, -1]。
    链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array

    法一:自己的代码

    思路:虽然right=len(nums)-1,即左闭右闭区间,但没有用left<right终止循环,当left=mid时,此时就做出判断终止循环,即可以灵活应用,不是必须当left==right的时候终止循环,

    from typing import List
    class Solution:
        def searchRange(self, nums: List[int], target: int) -> List[int]:
            right = len(nums) - 1
            if right == -1:
                return [-1, -1]
            elif right == 0:
                if nums[0] == target:
                    return [0,0]
                else:
                    return [-1, -1]
            # 先查找左端点
            def find_left(left, right):
                while left < right:
                    mid = (left+right) >> 1
                    # 等于的时候,目标值如果存在一定在等于的这两个值里
                    if left == mid:
                        if nums[right] == target:
                            if nums[left] == target:
                                return left
                            else:
                                return right
                        elif nums[left] == target:
                            return left
                        else:
                            return -1
                    if nums[mid] >= target:
                        right = mid
                    else:
                        left = mid + 1
                return left if nums[left] == target else -1
            # return find_left(0, right)
            # 再查找右端点
            def find_right(left, right):
                while left < right:
                    mid = (left+right) >> 1
                    if left == mid:
                        if nums[left] == target:
                            if nums[right] == target:
                                return right
                            else:
                                return left
                        elif nums[right] == target:
                            return right
                        else:
                            return -1
                    # 只有大于才让右端点左移,
                    if nums[mid] > target:
                        right = mid
                    else:
                        left = mid
            k = find_left(0, right)
            if k == -1:
                return [-1, -1]
            else:
                return [k, find_right(0, right)]
    View Code

    改进后的代码,right=len(nums),左闭右开区间

    from typing import List
    class Solution:
        def searchRange(self, nums: List[int], target: int) -> List[int]:
            right = len(nums)
            l = right
            # 找左侧边界的二分查找
            def find_left(left, right):
                # 搜索区间最短的时候有一个元素,为左边的left
                while left < right:
                    mid = (left + right) >> 1
                    if nums[mid] == target:
                        right = mid
                    # 右边是开区间,而mid一定不满足条件,所以right直接取mid
                    elif nums[mid] > target:
                        right = mid
                    # 左边是闭区间,mid一定不满足条件,直接将其排除,left取mid+1
                    # 综上可见,left和right本质上都一样,都是对已经作过判断的位置mid
                    # 不再进行判断,但是由于left闭right开,导致了排序mid位置的方法不一样
                    elif nums[mid] < target:
                        left = mid + 1
                if left == l:
                    return -1
                return left if nums[left] == target else -1
            # 找右侧边界的二分查找
            def find_right(left, right):
                while left < right:
                    mid = (left + right) >> 1
                    # 注意这里要加1,因为left是闭区间
                    # 如果不加1会出现死循环,如left等于2,right等于3时,这时mid每次都是left,陷入死循环
                    if nums[mid] == target:
                        left = mid + 1
                    # 如果大于目标值,则该值一定不满足条件,
                    elif nums[mid] > target:
                        right = mid
                    # 如果小于,右移一个,因为左边是闭区间,
                    elif nums[mid] < target:
                        left = mid + 1
                # 注意当left == right的时候,循环结束,但由于nums[mid] > target时,right=mid,即nums[right]一定不等于target
                # 也就是说nums[left-1]才可能等于target,故需要判断nums[left-1]
                if nums[max(left-1, 0)] == target:
                    return max(left-1, 0)
                else:
                    return -1
            k = find_left(0, right)
            if k == -1:
                return [-1,-1]
            else:
                return [k, find_right(0, right)]
    if __name__ == '__main__':
        solution = Solution()
        # result = solution.searchRange(nums = [5,7,7,8,8,8,8,9,10], target = 8)
        # result = solution.searchRange(nums = [7,7,7,7], target = 7)
        result = solution.searchRange(nums = [-4, -2, -1], target = -5)
        # result = solution.searchRange(nums = [5,7,7,8,8,10], target = 8)
        # result = solution.searchRange(nums = [1,2,3,4,5,8,8,8,8,8,8,8], target = 8)
        # result = solution.searchRange(nums = [1,1,1,1,1,1,1,1,1,2,3], target = -22)
        # result = solution.searchRange(nums = [0], target = 0)
        # result = solution.searchRange(nums = [8,8,8,8,8,8,8], target = 8)
        print(result)
    View Code

    注意上面求右侧边界时,是让左端点向中间靠拢,即左端点小于等于目标值是向右靠,事实上也可以让右端点在大于目标值时让右端点左移一位即mid-1,左端点不变等于mid,如下

    def rightloc(self, nums,target):
        lo, hi = 0, len(nums) - 1
        while lo < hi:
            mid = (lo + hi + 1) >> 1
            if nums[mid] <= target:
                lo = mid
            else:
                hi = mid - 1
        return lo if not lo == len(nums) and nums[lo] == target else -1
    View Code

    总结:对于求右侧边界,事实上移动左右端点都行,只要把目标值锁定在搜索区间肯定是对的,只不过后处理的方法可能不同,

    ttt

  • 相关阅读:
    .NET使用ICSharpCode.SharpZipLib压缩/解压文件
    在vs2005中使用AnkhSvn服务端IP改变无法连接
    repeater标签双重循环的使用
    在vs2005中使用SVN进行版本管理
    jmeter--响应断言
    jmeter--十三种断言方式介绍
    jmeter--参数化的四种方法
    jmeter--元件的作用域与执行顺序
    jmeter--使用badboy录制脚本
    jmeter--基本组件介绍
  • 原文地址:https://www.cnblogs.com/xxswkl/p/12354096.html
Copyright © 2011-2022 走看看