假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7]
可能变为[4,5,6,7,0,1,2]
)。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。
你可以假设数组中不存在重复的元素。
你的算法时间复杂度必须是 O(log n) 级别。
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
主要有两大种情况,第一种从begin
到end
之间是没有出现旋转的,即单调递增,此时使用正常的二分法就可以搜索目标数;另一种就是出现了旋转,发生了断裂。后者中,mid
可能位于左半支(断裂位置前边),也可能位于右半支(断裂位置后边),所以分情况考虑。假设位于左半支,如果target
位于nums[begin]
到nums[mid]
之间,则搜索区间就在begin
到mid + 1
之间,反之在mid + 1
到end
之间;位于右半支同理。需要注意的是nums[mid]
可能与nums[begin]
和nums[end]
相等。
运行时间4ms,代码如下。
int search(int* nums, int numsSize, int target) {
int i = 0, j = numsSize - 1, m = 0;
while(i <= j) {
m = (i + j) / 2;
if(nums[m] == target)
return m;
if(nums[i] <= nums[m] && nums[i] > nums[j]) {
if(nums[i] <= target && target < nums[m])
j = m - 1;
else
i = m + 1;
}
else if(nums[m] < nums[j] && nums[i] > nums[j]) {
if(nums[m] < target && target <= nums[j])
i = m + 1;
else
j = m - 1;
}
else {
if(nums[m] < target)
i = m + 1;
else
j = m - 1;
}
}
return -1;
}
先通过二分法确定找数组的最大值或者最小值,来确定旋转的位置;然后根据旋转的位置,进行二分法确定目标数是否存在。其中m
表示原本的位置,nowMid
表示旋转后的位置。
运行时间4ms,代码如下。
int search(int* nums, int numsSize, int target) {
if(numsSize == 0)
return -1;
int i = 0, j = numsSize - 1, m = 0;
int rot = 0;
// 通过找最小值确定旋转位置
// while(i < j) {
// m = (i + j) / 2;
// if(nums[m] > nums[j])
// i = m + 1;
// else
// j = m;
// }
// rot = i;
// 通过找最大值确定旋转位置
while(i < j) {
m = (i + j) / 2 + 1;
if(nums[m] >= nums[i])
i = m;
else
j = m - 1;
}
rot = i + 1;
i = 0;
j = numsSize - 1;
while(i <= j) {
m = (i + j) / 2;
int nowMid = (m + rot) % numsSize;
if(nums[nowMid] == target)
return nowMid;
else if(nums[nowMid] < target)
i = m + 1;
else
j = m - 1;
}
return -1;
}