leetcode-33-搜索旋转排序数组(search in rotated sorted array)-java

版权声明:此文章为许诗宇所写,如需转载,请写下转载文章的地址 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)


 

猜你喜欢

转载自blog.csdn.net/xushiyu1996818/article/details/86147800