版权声明:此文章为许诗宇所写,如需转载,请写下转载文章的地址 https://blog.csdn.net/xushiyu1996818/article/details/86147800
题目及测试
package pid033;
/*搜索旋转排序数组
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -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
*/
public class main {
public static void main(String[] args) {
int[][] testTable1=new int[][]{{4,5,6,7,0,1,2},{5,1,3}};
int[] testTable2=new int[]{0,4};
for(int i=0;i<testTable1.length;i++){
test(testTable1[i],testTable2[i]);
}
}
private static void test(int[] ito1, int ito2) {
Solution solution = new Solution();
int rtn;
long begin = System.currentTimeMillis();
rtn = solution.search(ito1,ito2);//执行程序
long end = System.currentTimeMillis();
System.out.print(rtn);
System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法1(成功,26ms,超慢)
同样使用二分查找法,只是每次的判断的结果start还是end变成mid+-1的条件不同
如果startvalue<endvalue说明处于上升序列,类似于二分查找即可
如果>= ,则中间有一个下降的悬崖,然后根据各个value的大小,确定target在左边还是右边,
注意如果target<startvalue但是大于endvalue说明target不存在
package pid033;
import java.util.HashMap;
public class Solution {
public int search(int[] nums, int target) {
int length=nums.length;
if(length<=0){
return -1;
}
int start=0;
int end=length-1;
while(start<=end){
int mid=(start+end)/2;
int startValue=nums[start];
int endValue=nums[end];
int midValue=nums[mid];
if(midValue==target){
return mid;
}
//头尾处于一条上升的线,按普通的二分查找
if(startValue<=endValue){
if(midValue>target){
end=mid-1;
continue;
}
else{
start=mid+1;
continue;
}
}
//头尾中间包含下降的那部分
else{
//前半部分长
if(midValue>=startValue){
//target 处于mid 和中间的max之间,或者target处于max和end之间
if(midValue<target||endValue>=target){
start=mid+1;
continue;
}
//target处于start和mid之间
if(startValue<=target&&target<midValue){
end=mid-1;
continue;
}
return -1;
}
//后半部分长
else{
if(midValue>target||startValue<=target){
end=mid-1;
continue;
}
if(midValue<target&&endValue>=target){
start=mid+1;
continue;
}
return -1;
}
}
}
return -1;
}
}
解法2
别人的方法
这里的那一边有序,就是指那一边有序的长度大
对于[0,1,2,3,4,5,6,7]而言,如红色标出所示,如果nums[mid]<nums[right],即中间的数小于最右边的数,那么mid(包括mid)右边的数是有序的,如果中间的数大于最右边的数,那么mid(包括mid)左边的数是有序的。那我们每次都可以折半查找。
平时使用二分法的时候,直接判断中点和目标的关系,就可以知道目标在左半边还是右半边,这需要一个条件,数组有序的。在这题中,因为发生了旋转,原来的数组不在有序。但升序数组旋转后如果左边的点比右边的点小,说明这两个点之间是有序的,不存在旋转点。如果左边的点比右边的大,说明这两个点之间有一个旋转点,导致了不再有序。因为只有一个旋转点,从中间一分为二后,肯定有一半是有序的。所以,我们还是可以用二分法,不过要先判断左半边有序还是右半边有序。如果左半边有序,则直接将目标和左半边的边界比较,就知道目标在不在左半边了,如果不在左半边肯定在右半边。同理,如果右半边有序,则直接将目标和右半边的边界比较,就知道目标在不在右半边了,如果不在右半边肯定在左半边。这样就完成了二分搜索。
实现(python)
def search(li,start,end,target):
mid = start + (end-start)//2
if start > end:
return -1
if li[mid] == target:
return mid
# 如果左半部分是有序的
if li[start] < li[mid]:
## 如果在左半部分的边界内
if (li[start] <= target) and (target < li[mid]):
return search(li,start,mid-1,target)
else:
return search(li,mid+1,end,target)
# 如果右半部分是有序的
else:
if (li[mid] < target) and (target <= li[end]):
return search(li, mid+1, end, target)
else:
return search(li, start, mid-1, target)
li = [4,5,6,7,0,1,2]
target = 0
search(li,0,len(li)-1,target)