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

  1. 34.在排序数组中查找元素的第一个和最后一个位置
    1. 34.1 题目
    2. 34.2 题解
      1. 二分查找两次
    3. 34.3 代码
    4. 34.4 运行结果

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


34.1 题目

给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。

如果数组中不存在目标值 target,返回 [-1, -1]

你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [], target = 0
输出:[-1,-1]

提示:

  • 0 <= nums.length <= 10^5
  • -10^9 <= nums[i] <= 10^9
  • nums 是一个非递减数组
  • -10^9 <= target <= 10^9

34.2 题解

二分查找两次

// 方法一:二分查找两次,分别查找开始位置和结束位置
static int[] SearchRange1(int[] nums, int target)
{
    // 查找目标值的开始位置
    int start = FindBound(nums, target, true);
    // 查找目标值的结束位置
    int end = FindBound(nums, target, false);
    // 返回结果
    return new int[] { start, end };
}

// 查找目标值的边界位置
static int FindBound(int[] nums, int target, bool isFirst)
{
    int left = 0; // 左边界初始化为0
    int right = nums.Length - 1; // 右边界初始化为数组最后一个元素的索引
    int bound = -1; // 初始化边界值为-1

    while (left <= right) // 当左边界小于等于右边界时进行循环
    {
        int mid = left + (right - left) / 2; // 计算中间位置
        if (nums[mid] == target) // 如果中间位置的元素等于目标值
        {
            bound = mid; // 更新边界值为中间位置
            if (isFirst) // 如果查找的是开始位置
            {
                right = mid - 1; // 更新右边界为中间位置减1
            }
            else // 如果查找的是结束位置
            {
                left = mid + 1; // 更新左边界为中间位置加1
            }
        }
        else if (nums[mid] < target) // 如果中间位置的元素小于目标值
        {
            left = mid + 1; // 更新左边界为中间位置加1
        }
        else // 如果中间位置的元素大于目标值
        {
            right = mid - 1; // 更新右边界为中间位置减1
        }
    }

    return bound; // 返回边界值
}

34.3 代码

using System;

class Program
{
    static void Main()
    {
        #region 题目

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

        // 示例 1:
        // 输入:nums = [5,7,7,8,8,10], target = 8
        // 输出:[3,4]

        // 示例 2:
        // 输入:nums = [5,7,7,8,8,10], target = 6
        // 输出:[-1,-1]

        // 示例 3:
        // 输入:nums = [], target = 0
        // 输出:[-1,-1]

        // 提示:
        // 0 <= nums.length <= 105
        // -109 <= nums[i] <= 109
        // nums 是一个非递减数组
        // -109 <= target <= 109

        #endregion

        #region 测试

        // 示例 1
        int[] nums1 = { 5, 7, 7, 8, 8, 10 };
        int target1 = 8;
        int[] result1 = SearchRange1(nums1, target1);
        Console.WriteLine($"示例1 方法1 输出:[{result1[0]}, {result1[1]}]");

        // 示例 2
        int[] nums2 = { 5, 7, 7, 8, 8, 10 };
        int target2 = 6;
        int[] result2 = SearchRange1(nums2, target2);
        Console.WriteLine($"示例2 方法1 输出:[{result2[0]}, {result2[1]}]");


        // 示例 3
        int[] nums3 = { };
        int target3 = 0;
        int[] result3 = SearchRange1(nums3, target3);
        Console.WriteLine($"示例3 方法1 输出:[{result3[0]}, {result3[1]}]");

        #endregion
    }

    #region 答案

    // 方法一:二分查找两次,分别查找开始位置和结束位置
    static int[] SearchRange1(int[] nums, int target)
    {
        // 查找目标值的开始位置
        int start = FindBound(nums, target, true);
        // 查找目标值的结束位置
        int end = FindBound(nums, target, false);
        // 返回结果
        return new int[] { start, end };
    }

    // 查找目标值的边界位置
    static int FindBound(int[] nums, int target, bool isFirst)
    {
        int left = 0; // 左边界初始化为0
        int right = nums.Length - 1; // 右边界初始化为数组最后一个元素的索引
        int bound = -1; // 初始化边界值为-1

        while (left <= right) // 当左边界小于等于右边界时进行循环
        {
            int mid = left + (right - left) / 2; // 计算中间位置
            if (nums[mid] == target) // 如果中间位置的元素等于目标值
            {
                bound = mid; // 更新边界值为中间位置
                if (isFirst) // 如果查找的是开始位置
                {
                    right = mid - 1; // 更新右边界为中间位置减1
                }
                else // 如果查找的是结束位置
                {
                    left = mid + 1; // 更新左边界为中间位置加1
                }
            }
            else if (nums[mid] < target) // 如果中间位置的元素小于目标值
            {
                left = mid + 1; // 更新左边界为中间位置加1
            }
            else // 如果中间位置的元素大于目标值
            {
                right = mid - 1; // 更新右边界为中间位置减1
            }
        }

        return bound; // 返回边界值
    }

    #endregion
}

34.4 运行结果

示例1 方法1 输出:[3, 4]
示例2 方法1 输出:[-1, -1]
示例3 方法1 输出:[-1, -1]


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏