前言
一般我们看到关键字有序数组,查找某个值都要下意思的往二分查找上面去想;
当然双指针也是经常用的办法;不过暴力遍历还是别了吧,暴力解法虽然很美滋滋,但是遍历遍历offer就遍历没了;
下面分享几道剑指offer上面的题目
第一题
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一个旋转,该数组的最小值为1。 链接: link.
思路分析:
代码
int minArray(int* numbers, int numbersSize){
int left=0;
int right=numbersSize-1;
while(left<right)
{
int mid=left+(right-left)/2;
if(numbers[mid]<numbers[right])
right=mid;//注意不能-1,小心越过所求值
else if(numbers[mid]>numbers[right])
left=mid+1;//始终在左半边,可以+1
else
right-=1;//有相等元素
}
return numbers[left];
}
第二题
统计一个数字在排序数组中出现的次数。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8;输出: 2 链接: link.
思路分析:
看到查找数组中的数字的次数,下意识就要反应二分查找;
假设数组中有我们查找的值target,并且有多个,这时候就不能用普通的二分查找来进行了,
这多个目标值肯定是连在一起的,因此我们需要找到这几个连在一起的起始坐标和终止坐标,他们的差值就是我们
需要目标值的个数(注意边界,不同的做法差值不一定正好)),我采用的做法是先找到目标值的右边边界即nums[right-1]=target,
再寻找
target-1的右边边界,即nums[right]=target,
因此两个相减就为目标值的个数;
当寻找第一次右边界的时候,我们可以通过第一次的边界判断是否存在目标值;
如果 第一次寻找的目标值 target<=0||target>=numsSize,那么数组之中不存在目标值,
此时直接返回0,不再进行第二次查找;
代码:
int Binearch(int *nums,int numsSize,int target)//封装一个右边界二分查找
{
int left=0;
int right=numsSize-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(target<nums[mid])
right=mid-1;
else if(target>nums[mid])
left=mid+1;
else//找到了
left=mid+1;
}
return left;//返回右边界点
}
int search(int* nums, int numsSize, int target){
int R_right=Binearch(nums,numsSize,target);//找到第一个右边界点
if(R_right<=0&&R_right>=numsSize)//数组中没有目标值
return 0;
int L_right=Binearch(nums,numsSize,(target-1));//找到target-1的边界
return (R_right-L_right);
}
第三题
题目:
一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。
链接: link.
思路分析:
代码:
int missingNumber(int* nums, int numsSize){
int left=0;
int right=numsSize-1;
while(left<=right)
{
int mid=left+(right-left)/2;
if(nums[mid]==mid)//左边是有序
left=mid+1;
else//左边是无序的
right=mid-1;
}
return left;
}