题目:
假设一个按升序排序的数组在某个未知的主轴上旋转。
(即[0,1,2,4,5,6,7]可能变成[4,5,6,7,0,1,2])。
给定要搜索的目标值。如果在数组中找到,返回它的索引,否则返回-1。
可以假设数组中不存在,算法的运行时复杂度必须是O(log n)
Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.(i.e., [0,1,2,4,5,6,7]
might become [4,5,6,7,0,1,2]
).
You are given a target value to search. If found in the array return its index, otherwise return -1
. You may assume no duplicate exists in the array. Your algorithm's runtime complexity must be in the order of O(log n).
Input: nums = [4,5,6,7,0,1,2], target = 0 Output: 4
Input: nums = [4,5,6,7,0,1,2], target = 3 Output: -1
思路:
因为运行时复杂度需要为O(log n),因此使用二分法进行查找
设t = nums[mid] mid = (start+end)/2
如何分辨需要 target 是在 t 的左边还是右边,设数组左边界的数为left,右边界的数为right
对 t > target,想要找到比 t 更小的数,
1)如果 left > t,则说明 t 处于右边的递增数组中,想要找到比t更小的数,只能在其左边找;
2)如果 left < t,则说明 t 处于左边的递增数组中,想要找到比 t 更小的数,可以在两边查找,
如果 left < target,说明target在left~t中,只能在左边的递增数组中查找;
否则,target比左边数组中最小的数还要小,此时,如果 right > target,说明target可能在右边的递增数组中
如果target比左边数组中最小的数还要小,但又大于右边数组的最大数(right),则说明不在这两个数组中,返回-1
对 t < target,想要找到比 t 更大的数,
1)如果 left < t,则说明 t 处于左边的递增数组中,想要找到比 t 更大的数,需要在 t 的右边找
2)如果 left > t,则说明 t 处于右边的递增数组中,此时,
如果right > target,说明target在t~right之间,继续在右边的递增数组中查找;
否则,target大于右边递增数组的最大数,如果left < target,则说明target可能在左边的递增数组中,在左边继续查找
如果target大于右边的最大数,又小于左边的最小数(left),则说明不在这两个数组中,返回-1
代码:
(因为我自己边界处理的时候总是容易出错。。所以我单独处理的边界,大佬们可以写到判断里面~)
分循环实现和迭代实现(主要的函数是一样的_(:з」∠)_ 只是感觉把功能函数分开写可能会清晰一些)
// 循环实现
class Solution {
public int search(int[] nums, int target) {
if(nums.length<1) {
return -1;
}
int start = 0;
int end = nums.length-1;
while(start<=end) {
int mid = (start+end)/2;
int left = nums[start];
int right = nums[end];
if(left==target) {
return start;
}
if (right==target) {
return end;
}
// t = target
if(nums[mid]==target){
return mid;
}
// t > target 要找比t更小的数
if(nums[mid] > target) {
// 在右的递增数组中
if(left > nums[mid]) {
end = mid - 1;
// 在左边的递增数组中
}else{
if(left < target) {
end = mid - 1;
}else if(right > target){
start = mid + 1;;
}else {
return -1;
}
}
// t < target
}else{
// 在左边的递增数组中
if(left < nums[mid]) {
start = mid + 1;
}else{
// 在右边的递增数组中
if(right>target) {
start = mid + 1;
}else if(left<target){
end = mid - 1;
}else {
return -1;
}
}
}
}
return -1;
}
}
// 迭代实现
class Solution {
public int search(int[] nums, int target) {
int[] locArr = new int[] {0,nums.length-1,-2};
while(locArr[0]<=locArr[1] && locArr[2]!=-1) {
if(locArr[2]!=-2) {
return locArr[2];
}
subSearch(nums, locArr, target);
}
if(locArr[2]>=0) {
return locArr[2];
}else {
return -1;
}
}
// 返回下次需要比较的start——end 以及当前的比较结果
public void subSearch(int[] nums, int[] locArr, int target) {
int start = locArr[0];
int end = locArr[1];
int mid = (start+end)/2;
int left = nums[start];
int right = nums[end];
if(left==target) {
locArr[2] = start;
return ;
}
if (right==target) {
locArr[2] = end;
return ;
}
// t = target
if(nums[mid]==target){
locArr[2]= mid;
return ;
}
// t > target 要找比t更小的数
if(nums[mid] > target) {
// 在右的递增数组中
if(left > nums[mid]) {
locArr[1] = mid - 1;
// 在左边的递增数组中
}else{
if(left < target) {
locArr[1] = mid - 1;
}else if(right > target){
locArr[0] = mid + 1;;
}else {
locArr[2]=-1;
return ;
}
}
// t < target
}else{
// 在左边的递增数组中
if(left < nums[mid]) {
locArr[0] = mid + 1;
}else{
// 在右边的递增数组中
if(right>target) {
locArr[0] = mid + 1;
}else if(left<target){
locArr[1] = mid - 1;
}else {
locArr[2] = -1;
return ;
}
}
}
}
}