对于寻找左右边界的二分搜索,常见的手法是使用左闭右开的搜索区间,但是为了方便记忆,全部统一为两端都闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| int binary_search(int[] nums, int target) { int left = 0, right = nums.length - 1; while(left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if(nums[mid] == target) { return mid; } } return -1; }
int left_bound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] == target) { right = mid - 1; } } if (left >= nums.length || nums[left] != target) { return -1; } return left; }
int right_bound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] == target) { left = mid + 1; } } if (right < 0 || nums[right] != target) { return -1; } return right; }
|
给定一个按照升序排列的整数数组 nums,和一个目标值
target。找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target,返回 [-1, -1]。
进阶:
你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
示例:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
思路:
二分查找的时间复杂度为O(logN)
,我们只需要调用两次,分别查找左边界和右边界。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { return {left_bound(nums, target), right_bound(nums, target)}; } int left_bound(vector<int>& nums, int target){ int left = 0, right = nums.size() - 1; while(left <= right){ int mid = left + (right - left) / 2; if(nums[mid] < target){ left = mid + 1; }else if(nums[mid] > target){ right = mid - 1; }else{ right = mid - 1; } } if(left >= nums.size() || nums[left] != target){ return -1; } return left; } int right_bound(vector<int>& nums, int target){ int left = 0, right = nums.size() - 1; while(left <= right){ int mid = left + (right - left) / 2; if(nums[mid] < target){ left = mid + 1; }else if(nums[mid] > target){ right = mid - 1; }else{ left = mid + 1; } } if(right < 0 || nums[right] != target){ return -1; } return right; } };
|
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
示例:
输入: nums = [1,3,5,6], target = 5
输出: 2
输入: nums = [1,3,5,6], target = 2
输出: 1
思路:
前面我们讲过了数组中寻在目标元素重复的情况,现在是目标元素不存在的情况。
当目标元素target
不存在数组nums
中时,搜索左侧边界的二分搜索的返回值可以做以下几种解读:
- 返回的这个值是 nums 中⼤于等于 target 的最⼩元素索引。
- 返回的这个值是 target 应该插⼊在 nums 中的索引位置。
- 返回的这个值是 nums 中⼩于 target 的元素个数。
⽐如在有序数组 nums = [2,3,5,7] 中搜索 target =
4,搜索左边界的⼆分算法会返回 2,你带⼊上⾯ 的说法,都是对的。
我们现在需要的正是第二种
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| class Solution { public: int searchInsert(vector<int>& nums, int target) { return left_bound(nums, target); } int left_bound(vector<int>& nums, int target){ int left = 0, right = nums.size(); while(left < right){ int mid = left + (right - left) / 2; if(nums[mid] < target){ left = mid + 1; }else if(nums[mid] > target){ right = mid; }else{ right = mid; } } return left; } };
|
以上几点需要注意,因为我们需要的是要插入的位置。