C++:用二分法查找目标值在排序排列数组中的左右边界

笔者最近用LeeCode刷算法题时遇到查找目标值在升序排列数组中的左右边界的问题,直觉上肯定这就是个二分法问题,但在如何查找左右边界的问题上钻进了牛角尖,最终经过不懈努力解决了这个问题,故在此分享理清之后的思路,也希望更同在此问题上纠结的小伙伴提供一点帮助。具体的问题描述如下所示:

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

题意
给定一个按照升序排列的整数数组 nums 和一个目标值 target,找出给定目标值在数组中的开始位置和结束位置。

如果 target 不在数组中,返回 [-1,-1]。

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

输出:[3,4]

提示
0 <= len(nums) <= 10^5
-10^9 <= nums[i] <= 10^9
nums 是一个非递减数组
-10^9 <= target <= 10^9

解题思路

由于nums 数组存在重复的元素,这就意味着在 nums 数组中利用二分查找 target 返回的下标不具有唯一性,而题目要求查找元素 target 的第一个位置和最后一个位置,即目标值在排序数组nums 中的左右边界。因此这个问题最直观的思路就变成了,利用两次二分法分别查找数组nums中最左侧元素和最右侧元素,这就意味着需要对标准的二分法做以调整。

为了便于理解,我们首先对标准的二分法作以回顾,然后再与调整过的二分法做个比对,梳理一下调整的思路,值得注意的是,不管利用哪种形式的二分法进行查找,要求数组必须按照顺序排列,这是使用二分查找的前提。

1、标准的二分查找

以nums = [1,3,5,7,8],target=3为例,首先初始化left和right指向的位置,对于本示例而言:

left = 0; right = nums.zise() - 1;

由此可计算得到 mid = left + (right - left) / 2;

此时判断nums[mid]与target的关系:

(1) 如果nums[mid] == target,则返回mid,即为target在数组nums中的位置;

(2) 如果nums[mid] > target,则说明mid处存储的数字过大,调整右边界right=mid-1继续查找;

(3) 如果nums[mid] < target,则说明mid处存储的数字过小,调整左边界left=mid-1继续查找;

因此,本实例属于第二种情况,需要调整右界right继续查找。

重复以上步骤,当nums[mid] == target,即找到target的位置,跳出循环;或者当left>right时结束循环,即未查找到数组中存在target元素。

基于以上分析,可通过C++将上述标准的二分查找算法表述如下:

int searchTarget(vector<int>& nums, int target) {
    
    // 定义二分法的区间
    left = 0, right = nums.size() - 1;
    
    while (left <= right) {
        
        int mid = left + (right - left) / 2;  // 计算要查找的位置

        if (nums[mid] == target)              // 查找到目标值结束循环
            return mid;
        else if (nums[mid] < target)          // 查找的值小于目标值,改变左界向右继续查找
            left = mid + 1;
        else                                  // 查找的值大于目标值,改变右界向左继续查找
            right = mid - 1;
    }

    return -1;                                // 未在数组中查找到目标值
}

2、二分查找元素边界

 标准的二分法与查找元素边界最大的区别是:前者只要找到目标值就结束循环,不再继续查找了;而后者找到目标值还需要继续查找,判断是否存在其他的目标值。

基于这个区别,通过二分查找元素边界的思想为:通过不断的二分循环查找目标值,找到目标值之后,先记住目标值的位置,然后继续向左(查找元素左边界)或向右(查找元素右边界)查找目标值,直到不满足循环条件left<=right时退出循环,则最后一次记录的目标值位置即为查找的元素边界。

以nums = [1,3,3,3,8],target=3,查找元素左边界为例对上述思想加以说明:

首先初始化left和right指向的位置:left = 0; right = nums.zise() - 1;

由此可计算得到 mid = left + (right - left) / 2;

此时判断nums[mid]与target的关系:

(1) 如果nums[mid] == target,则记录mid,这是目标值在nums数组中的一个位置,然后改变二分区间,调整右边界right=mid-1继续查找该元素左侧的目标值;

(2) 如果nums[mid] > target,则说明mid处存储的数字过大,调整右边界right=mid-1继续查找;

(3) 如果nums[mid] < target,则说明mid处存储的数字过小,调整左边界left=mid-1继续查找;

重复以上步骤可以发现,对于查找左边界而言,查找到目标元素记录此位置之后,仍继续向左查找是否存在目标元素,直到不满足循环条件left<=right的时候退出循环,那么最后一次记录的mid即为目标元素的左边界。

同理,查找元素右边界仅需要在nums[mid] == target时,调整二分区间往右便可,即调整左边界left=mid-1继续查找,在此不再赘述。

基于以上分析,可通过C++将上述查找元素左边界的算法表述如下:

int searchRange(vector<int>& nums, int target) {
    
    int leftRange = -1;
    
    // 定义二分法的区间
    left = 0, right = nums.size() - 1;
    
    while (left <= right) {
        
        int mid = left + (right - left) / 2;  // 计算要查找的位置

        if (nums[mid] == target) {            // 查找到目标值,记录mid,然后向左继续查找
            leftRange = mid;
            right = mid - 1;
        }
        else if (nums[mid] < target)          // 查找的值小于目标值,改变左界向右继续查找
            left = mid + 1;
        else                                  // 查找的值大于目标值,改变右界向左继续查找
            right = mid - 1;
    }

    return leftRange;                         // 返回元素左界的位置,若元素不存在则返回-1
}

3、LeeCode题解

通过上述分析,该题目利用两个二分法获取元素的左右边界,若元素不存在返回{-1,-1}即可,C++程序如下:

vector<int> searchRange(vector<int>& nums, int target) {
        
    // 定义target的存在的位置为-1,-1
    int leftRange = -1, rightRange = -1;

    // 定义二分法的区间
    int left = 0, right = nums.size() - 1;
    
    // 求target的左边界
    while (left <= right) {
        int mid = left + (right - left) / 2;  // 计算要查找的位置

        if (nums[mid] == target) {            // 搜索到目标值之后暂存该结果,并且继续向左搜索
            leftRange = mid;
            right = mid - 1;
        }
        else if (nums[mid] > target)          // 查找的值小于目标值,改变左界向右继续查找
            right = mid - 1;
        else                                  // 查找的值大于目标值,改变右界向左继续查找
            left = mid + 1;
    }

    // 定义二分法的区间
    left = 0, right = nums.size() - 1;
    
    // 求target的右边界
    while (left <= right) {
        int mid = left + (right - left) / 2;  // 计算要查找的位置

        if (nums[mid] == target) {            // 搜索到目标值之后暂存该结果,并且继续向右搜索
            rightRange = mid;
            left = mid + 1;
        }
        else if (nums[mid] < target)          // 查找的值小于目标值,改变左界向右继续查找
            left = mid + 1;
        else                                  // 查找的值大于目标值,改变右界向左继续查找
            right = mid - 1;
    }

    return vector<int> {leftRange, rightRange};
}

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C++中使用二分法查找数据,并在查找前对数据进行排序可以通过以下步骤实现: 1. 定义一个`binarySearch`函数,该函数接受三个参数,分别为待查找的数组、数组的长度和要查找的数据。 2. 在`binarySearch`函数中,先对数组进行排序,可以使用C++标准库中的`sort`函数进行排序,也可以使用其他排序算法进行排序。 3. 对排序后的数组进行二分查找,具体实现方法为:定义两个指针`left`和`right`,分别指向数组的第一个元素和最后一个元素,然后计算它们的中间位置`mid`,如果`arr[mid]`等于要查找的数据,则返回`mid`;如果`arr[mid]`大于要查找的数据,则将`right`指针移动到`mid-1`的位置;如果`arr[mid]`小于要查找的数据,则将`left`指针移动到`mid+1`的位置。重复这个过程直到查找到要查找的数据或者`left`指针大于`right`指针为止。 4. 如果没找到要查找的数据,则返回-1或者其他特定的值表示未找到。 以下是使用二分法查找数据,并在查找前对数据进行排序C++代码示例: ```c++ #include <iostream> #include <algorithm> using namespace std; int binarySearch(int arr[], int n, int target) { sort(arr, arr + n); // 对数组进行排序 int left = 0, right = n - 1; while (left <= right) { int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; } else if (arr[mid] > target) { right = mid - 1; } else { left = mid + 1; } } return -1; } int main() { int arr[] = {5, 4, 3, 2, 1}; int n = sizeof(arr) / sizeof(arr[0]); int target = 3; int index = binarySearch(arr, n, target); if (index != -1) { cout << "找到了,下标为:" << index << endl; } else { cout << "没找到!" << endl; } return 0; } ``` 在上述代码中,`binarySearch`函数用于实现二分查找,`arr`为输入的数组,`n`为数组的长度,`target`为要查找的数据。在`binarySearch`函数中,先对数组进行排序,然后使用二分查找的方式查找要查找的数据。如果找到了要查找的数据,则返回它的下标;如果没找到,则返回-1。 输出结果为:`找到了,下标为:2`。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值