二分法:力扣34题【在排序数组中查找元素的第一个和最后一个位置】(C++)

题目描述:

题目:

给你一个按照非递减顺序排列的整数数组 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]

解题思路:

为什么要用二分法?

题目中有几个关键词:非递减的数组、查找元素在数组的位置、时间复杂度为o(log n)。这些都是使用二分法的暗示。

这道题难点在哪?

既然已经知道要用二分法解题,那么就可以开始思考难点啦。我觉得二分法不难,难的是如何把二分法灵活运用起来。这道题的难点就在于如何通过二分法找到给定元素首尾位置,由于本人水平有限,这里只给出一种解题思路,读者有其他想法欢迎讨论、赐教~

破题思路是什么?

这道题可以分成三个部分:寻找元素第一次出现的位置、寻找最后一次出现的位置、元素没有出现时的算法调整。

寻找位置的关键:根据二分法,当中间值大于目标值,右边界(right)应该被赋与mid - 1;当中间值小于目标值,左边界(left)应该被赋予mid + 1;当中间值等于目标值就可以退出循环了。这道题巧妙之处在于找到和目标值相同的元素时不退出循环,而是将其根据此次循环中右边界(right)和左边界(left)的变化情况记录下来,如果是right变化了,得到的就是首位置,反之就是尾位置。

1.寻找元素第一次出现的位置

注意:第14、15行代码不可以颠倒顺序,因为是先寻找变化的边界,待其变化完成之后再记录其值,下同。

 2.寻找最后一次出现的位置

 3.元素没有出现时的算法调整

元素没有出现的情况有以下两种:

不在范围内。如 {5,7,7,8,8,10} 中寻找数字 2 ,或寻找数字 11

 在范围内。如 {5,7,7,8,8,10} 中寻找数字 6

对于第一种情况,在调用功能函数时一直没有执行“更新首尾位置变量”的代码,返回值仍为初始值 -2,所以直接判断返回值是否为 -2 即可。

除第二种情况之外,其余情况判断完之后,如果程序还没返回,就说明元素在范围内但没有出现,此时返回 {-1,-1} 即可。

代码:

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = getLeftBorder(nums, target);   //获得首位置
        int right = getrightBorder(nums, target); //获得尾位置
        if (left == -2 || right == -2) return { -1, -1 };  //元素未出现的第一种情况
        if (right - left > 1) return { left + 1, right - 1 }; //元素出现
        return { -1, -1 }; //元素未出现的第二种情况
    }
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;                     //左边界
        int right = nums.size() - 1;      //右边界
        int leftborder = -2;              //记录首位置值
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] >= target) {    //如果中间值 >= target,更新并记录right值
                right = mid - 1;
                leftborder = right;
            }
            else left = mid + 1;
        }
        return leftborder; //返回首位置
    }
    int getrightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightborder = -2;
        while (left <= right) {
            int mid = (left + right) / 2;
            if (nums[mid] <= target) { //如果中间值 <= target,更新并记录left值
                left = mid + 1;
                rightborder = left;
            }
            else right = mid - 1;
        }
        return rightborder;
    }
};

我是左岸花花 感谢阅读我的博客~  因为有你,这个冬天不再寒冷^_^

好的,我可以为您提供一个递归的二分查找算法来解决这个问。 假设我们要在已排序的数组 `arr` 中查找元素 `target` 出现的第一个位置,我们可以编写以下递归函数: ```python def binary_search(arr, target, left, right): if left > right: return -1 mid = (left + right) // 2 if arr[mid] == target: if mid == 0 or arr[mid-1] < target: return mid else: return binary_search(arr, target, left, mid-1) elif arr[mid] > target: return binary_search(arr, target, left, mid-1) else: return binary_search(arr, target, mid+1, right) ``` 该函数接受四个参数:已排序的数组 `arr`、要查找元素 `target`、当前查找区间的左右边界 `left` 和 `right`。函数首先判断查找区间是否有元素,如果没有则返回 `-1` 表示查找失败。然后计算出中间位置 `mid`,如果 `arr[mid] == target`,则表示找到了目标元素,接下来判断它是否是第一个出现的位置,如果是则返回 `mid`,否则继续在左半边查找。如果 `arr[mid] > target`,则说明目标元素在左半边,继续在左半边查找;如果 `arr[mid] < target`,则说明目标元素在右半边,继续在右半边查找最后,我们可以调用该函数来查找元素第一次出现的位置: ```python arr = [1, 2, 3, 3, 3, 4, 5, 6] target = 3 left = 0 right = len(arr) - 1 print(binary_search(arr, target, left, right)) # 输出 2 ``` 在这个示例中,元素 `3` 第一次出现的位置是索引 `2`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值