剑指offer之在排序数组中查找数字

118 阅读2分钟

题目要求:在已排序数组中查找目标数字出现的次数

原题链接点这里

解题思路:

1.暴力解题:时间复杂度为O(n),弃用
2.二分查找:在排序数组中查找,一般都用二分法,时间复杂度为O(logN)

关于二分查找

关于二分查找都要清楚的两个个问题:查找什么?边界条件怎么处理(即等号怎么处理)?

对于本题

  • 查找什么?--->查找目标数字的左右边界

image.png

  • 边界条件怎么处理?--->代码中具体叙述

代码如下:

var search = function(nums, target) {
let left=0
let right=nums.length-1
//查找右边界
while(left<=right){
let mid=Math.floor((left+right)/2)
if(target<nums[mid]){
right=mid-1
}
else{
left=mid+1
}
//获取右边界  left就是我们要获取的右边界
let i=left

left=0
right=nums.length-1
//获取左边界
while(left<=right){
let mid=Math.floor((left+right)/2)
if(nums[mid]>=target){
right=mid-1
}
else{
left=mid+1
}
//获取左边界
let j=right
return i-j-1
}
}
}

根据上述代码来考虑二分查找的边界问题,当查找右边界时,如果nums[mid]==target,这时是left=mid+1还是right=mid-1呢?答案是left=mid+1。因为这时,右边界范围在[mid+1,right].

当查找左边界时,如果nums[mid]==target,这时是left=mid+1还是right=mid-1呢?答案是right=mid-1.因为这时,左边界范围在[left,mid-1]

对本题进行优化(lettcode题解区Krahets大佬的绝佳思路)

优化一:

当查找到右边界时,右边界-1如果不是target,则返回0.这种情况还要考虑一下右边界可能是数组的第一个元素 添加代码:

//右边界
let i=left
if(i==0||nums[i-1]!==target){
return 0
}
优化二:

本题用了两次二分查找,一次查找右边界,一次查找左边界。而查找左边界可以转化成查找target-1的右边界。这样就可以将查找右边界封装成一个函数.

function helper(left,right,target){
while(left<=right){
let mid=Math.floor((left+right)/2)
if(nums[mid]<=target){
left=mid+1
}
else{
right=mid-1
}
}
}

最后(经过两步优化的代码)

var search = function(nums, target) {
let len=nums.length-1
let right=helper(0,len,target)
//优化一
if(right-1<0||nums[right-1]!==target){
return 0
}
let left=helper(0,len,target-1)
//优化二
function helper(left,right,target){
while(left<=right){
let mid=Math.floor((left+right)/2)
if(nums[mid]<=target){
left=mid+1
}
else{
right=mid-1
}
}
return left
}
return right-left
};

总结(遇到这类题目该怎么做)

  • 通过关键词排序后的数组查找可以想到要用二分法
  • 使用二分法要思考以下问题:1.查找什么 2.查找几次 3.边界条件怎么处理 4.可不可以进行优化