整数数组 nums
按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums
在预先未知的某个下标 k
(0 <= k < nums.length
)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7]
在下标 3
处经旋转后可能变为 [4,5,6,7,0,1,2]
。
给你 旋转后 的数组 nums
和一个整数 target
,如果 nums
中存在这个目标值 target
,则返回它的下标,否则返回 -1
。
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0 输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3 输出:-1
示例 3:
输入:nums = [1], target = 0 输出:-1
提示:
-
1 <= nums.length <= 5000
-
-104 <= nums[i] <= 104
-
nums
中的每个值都 独一无二 -
题目数据保证
nums
在预先未知的某个下标上进行了旋转 -
-104 <= target <= 104
思路:
本题很明显是一个查询问题,题目要求要设计一个时间复杂度为O(logn)的一个算法,在查询算法中二分查找符合这个要求。二分查找有一个要求就是查询的表要是一个顺序表,根据题目可以看出数组经过了一个旋转,将一个有序数组分成了两个有序数组,那么找到这个旋转位置就变的至关重要。因此我们可以通过改进二分查找先找到旋转的位置,在使用二分查找对目标元素进行查找。
我们可以明显的看到旋转位置为蓝线所在的位置,它将数组分为了两个有序数组s1,s2,我们现在就是要找到这个位置,这个位置的特点就是后面的元素要比前面的小,通过二分的方法。我们可以通过比较mid位置的元素是否比low大,如果大的话就证明现在mid指针在s1串中,令low=mid,继续二分。
现在再次比较话就出现了mid<low的情况了,就证明mid现在到了S2串中因此需要移动high的位置,令high = mid,继续二分。
一直找到low=high,我们就找到了两个位置的分割线了。
然后记下这两个数据的位置,比较题中的target与最后一个元素的大小如果大,则在S1串中进行二分查找,否则在S2中进行二分。
//IDEA测试代码
public static void main(String[] args) {
int[] h = {1,3};
int target = 1;
System.out.println(search(h,target));
}
public static int search(int[] nums, int target) {
//二分查找
int low, mid, high;
low = 0;
high = 0;//为了保证特殊情况的一致性,查找旋转位置在进行赋值
//通过二分查找数组旋转的位置
//这个题有点特殊情况就是在0处旋转数组顺序保持不变在进行查找旋转的位置时要进行一个判断即最后一个元素是否大于第一个元素
if(nums[low] > nums[nums.length - 1]){
high = nums.length - 1;
while ((low + 1) != high && high != 0) {
mid = (low + high) / 2;
if (nums[low] < nums[mid]) low = mid;
else high = mid;
}
}
//low中储存了S1中的最大值,high中存储了S2中的最小值
//在S1串中进行二分查找
if (target > nums[nums.length - 1]) {
int begin, end;
begin = 0;
end = low;
//因为要保证每一个元素都遍历到
while (begin <= end && end >= 0) {
mid = (begin + end) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] > target) end = mid - 1 ;
else begin = mid + 1;
}
}
else{
int begin, end;
begin = high;
end = nums.length - 1;
//因为要保证每一个元素都遍历到
while (begin <= end && end >= 0) {
mid = (begin + end) / 2;
if (nums[mid] == target) return mid;
else if (nums[mid] > target) end = mid - 1;
else begin = mid + 1;
}
}
return -1;
}
总结:
这道题使用一般遍历的方法是很好解决的,但是题目要求时间复杂度,简单的遍历就会时间复杂度过高为O(n),不能满足题目中的要求,使用二分查找的方法能够满足题目的要求并达到好的效果,速度还是很快的。