1、题目描述
2、解题思路
输入的数组是一个升序排序数组的旋转,可以看成如下图所示的状态:
把上图两部分称为:旋转数组左侧和旋转数组右侧。可以分析出一个特点:旋转数组的左侧任意一个元素的值都大于等于右侧元素,使用二分法时,通过比较中间元素与最左侧元素的大小可以直接判断中间元素在旋转数组的左侧还是右侧。
如果对于每一次调整区间范围,都有以下两种情况:
1、mid 在左侧部分
此时,参照对象就是 nums[0],如果目标值大于等于它,且目标值小于 nums[mid],则 r = mid - 1,否则 l = mid + 1。
2、mid 在右侧部分
此时,参照对象是 nums[n-1] ,如果目标值小于等于它,且目标值大于 nums[mid],则 l = mid + 1,否则 r = mid + 1。
3、解题代码
// Java实现
class Solution {
public int search(int[] nums, int target) {
int n = nums.length; // 获取数组元素个数
// 空数组
if (n == 0) {
return -1;
}
// 单元素数组
if (n == 1) {
return nums[0] == target ? 0 : -1;
}
// 多元素数组
int left = 0, right = n - 1; // 初始化待搜索范围的左右边界
while (left <= right) { // 边界合理性判断
int mid = left + (right - left) / 2; // 待搜索范围的中间元素坐标
if (nums[mid] == target) { // 找到目标值,返回坐标
return mid;
}
if (nums[0] <= nums[mid]) { // mid在旋转数组的左侧
if (nums[0] <= target && target < nums[mid]) {
right = mid - 1; // 待搜索范围的右边界更新为 mid-1,不更新为 mid 的原因在于: mid 所在坐标的值在上面就确定不等于目标值
} else {
left = mid + 1; // 待搜索范围的左边界更新为 mid+1
}
} else { // mid在旋转数组的右侧
if (nums[mid] < target && target <= nums[n - 1]) {
left = mid + 1;
} else {
right = mid - 1;
}
}
}
return -1; // 遍历完毕,未找到目标
}
}
// C实现
#include<stdio.h>
#include<stdlib.h>
int search(int *nums, int numsSize, int target){
// 空数组的情况
if(numsSize == 0) return -1;
// 单元素数组的情况
if(numsSize == 1) return nums[0] == target ? 0 : -1;
// 多元素数组的情况
int left = 0, right = numsSize - 1;
int mid = 0;
while(left <= right)
{
mid = left + (right - left) / 2;
if(nums[mid] == target) return mid;
if(nums[0] < nums[mid]) // mid 在左侧数组
{
// 注意边界要小于等于
if(nums[0] <= target && target < nums[mid])
{
right = mid - 1;
}
else
{
left = mid + 1;
}
}
else // mid 在右侧数组
{
// 注意边界要小于等于
if(nums[mid] < target && target <= nums[numsSize-1])
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
}
// 找不到
return -1;
}