写在前面: 本人是某大学软件工程专业大三的学生,写这个专栏,是为了总结自己刷的LeetCode题,温故而知新,同时分享给这条路上的朋友们。由于本人水平有限,博客中难免有些错误,欢迎朋友们来指正,也很乐意与各位朋友交流学习经验。虽然现在还很菜,但是,我相信只要不断的学习、积累,总会有成为大佬的那一天。
1. 面试题53 - I. 在排序数组中查找数字 I(简单)
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: 2
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: 0
解题思路:
利用二分查找,先找到,最右边等于target的数组元素索引为r,再利用二分查找,找到左边第一个等于target的元素索引为l,最后数字在排序数组中出现的次数就是r-l+1。
代码
class Solution {
public int search(int[] nums, int target) {
if(nums == null || nums.length == 0) return 0;
int left = 0;
int right = nums.length - 1;
//找到右边最后一个target
while(left <= right){
int mid = left + (right - left) / 2;
//注意等于的时候是让mid+1,缩小左边界范围
if(nums[mid] <= target){
//下一次搜索的区间是[mid+1,right];
left = mid + 1;
}else if(nums[mid] > target){
right = mid - 1;
}
}
//循环结束的时候,如果数组中有target,right指向最右边那个
//如果没有右边界,说明数组中没有等于target的元素,直接返回0,就不用去找左边界了
if(right >= 0 && nums[right] != target) return 0;
int r = right;
//找到左边第一个target
left = 0;
right= nums.length - 1;
while(left <= right){
int mid = left + (right - left) / 2;
if(nums[mid] < target){
left = mid + 1;
}else{
//当nums[mid]=target的时候,让right=mid-1,缩小左边界范围
//下一次搜索的区间是[left,right-1]
right = mid - 1;
}
}
return r - left + 1;
}
}
时间复杂度:O(log n) 空间复杂度:O(1)
总结
注意查找右边最后一个等于target的元素和左边第一个等于target的元素的条件,很类似,唯一的不同点就在于,前者nums[mid]=target时,让left= mid+1,缩小左边界,后者是让right=mid-1,缩小有边界。还有while中的条件,这道题用的是<=,如果不带等于号的话,数组只有一个元素或者只有两个元素的情况就没有办法计算。
改进
以上代码显得比较臃肿(两轮二分查找代码冗余)。为简化代码,可将二分查找右边界 rightright 的代码 封装至函数 helper() 。
如上图所示,由于数组 numsnums 中元素都为整数,因此可以分别二分查找 target 和 target - 1 的右边界,将两结果相减并返回即可。
代码:
class Solution {
public int search(int[] nums, int target) {
return helper(nums,target) - helper(nums,target-1);
}
//找最右边等于target的数组元素
public int helper(int[] nums, int target){
int left = 0;
int right = nums.length - 1;
while(right >= left){
int mid = left + (right - left) / 2;
if(nums[mid] <= target){
left = mid + 1;
}else{
right = mid - 1;
}
}
return right;
}
}
参考
题解
2. 面试题53 - II. 0~n-1中缺失的数字(简单)
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
示例 1:
输入: [0,1,3]
输出: 2
示例 2:
输入: [0,1,2,3,4,5,6,7,9]
输出: 8
解题思路
利用二分查找,数组可以分成两部分,左部分是从第一个元素开始到缺失元素的前一个元素,右部分是从缺失元素到最后一个元素,左部分中,元素的值和索引值相等,右部分中,元素值大于索引值。所以,利用这个特点,使用二分查找,当数组值和索引相等的时候,说明缺失值在当前元素的右边,如果数组值大于索引,说明缺失值在当前元素的左边。
代码:
class Solution {
public int missingNumber(int[] nums) {
int left = 0;
int right = nums.length - 1;
while(right >= left){
int mid = left + (right - left) / 2;
//相等时,往右查找
if(nums[mid] == mid){
left = mid + 1;
}else{
//不相等,往左查找
right = mid - 1;
}
}
//结束循环的时候,left 在右半部分的第一个位置,索引等于缺失的值
return left;
}
}
时间复杂度:O(logN) 空间复杂度:O(1)
参考
题解