前言
今天主要就是刷几道和二分法相关的几道题,加深理解
35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5 输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2 输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7 输出: 4
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
为 无重复元素 的 升序 排列数组-104 <= target <= 104
思路:
- 这道题用暴力解法O(n)也可过,遍历每个元素看是不是比它大
-
class Solution { public: int searchInsert(vector<int>& nums, int target) { for(int i = 0; i < nums.size(); i++){ // 目标值在数组所有元素之前 // 目标值等于数组中某一个元素 // 目标值插入数组中的位置 if(target <= nums[i]) return i; } // 一旦发现大于或者等于target的num[i],那么i就是我们要的结果 // 目标值在数组所有元素之后的情况 return nums.size(); // 如果target是最大的,或者 nums为空,则返回nums的长度 } };
- 有序就用二分,依然注意边界和返回值的意义
-
class Solution { public: int searchInsert(vector<int>& nums, int target) { int left = 0; int right = nums.size()-1; while(left <= right){ //左闭右闭 int mid = (left + right) / 2; if(nums[mid] < target) left = mid + 1; else if(nums[mid] > target) right = mid - 1; else return mid; } // 分别处理如下四种情况 // 目标值在数组所有元素之前 [0, -1] // 目标值等于数组中某一个元素 return middle; // 目标值插入数组中的位置 [left, right],return right + 1 // 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1 return right + 1; // 大了 } };
34. 在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 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
思路:
- 用二分法分别寻找不包含target的左边界和右边界
- 左边界就是找到第一个小于它的数,通过right-1找,所以>=target的都不要(右类似)
- 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
- 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
- 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
-
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { int leftBorder = getLeftBorder(nums, target); int rightBorder = getRightBorder(nums, target); // 情况一 if (leftBorder == -2 || rightBorder == -2) return {-1, -1}; // 情况三 if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1}; // 情况二 return {-1, -1}; } private: int getRightBorder(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况 while (left <= right) { int middle = left + ((right - left) / 2); if (nums[middle] <= target) { // 寻找右边界,nums[middle] == target的时候更新left left = middle + 1; rightBorder = left; } else { right = middle - 1; } } return rightBorder; } int getLeftBorder(vector<int>& nums, int target) { int left = 0; int right = nums.size() - 1; int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况 while (left <= right) { int middle = left + ((right - left) / 2); if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right right = middle - 1; leftBorder = right; } else { left = middle + 1; } } return leftBorder; } };
总结
感觉没太搞懂最后这个找边界,脑阔疼,再花点时间自己调试写写,另外我对于C++类的应用太陌生了,也花点时间熟悉一下,这样之后才可以本地调试
2023/08/23更新
左边界<指定数<右边界(不含等)
左边界用right约束(因为最后跳出循环是right更新),用>=
右边界用left约束(因为最后跳出循环是left更新),用<=