小米笔试题(查找旋转数组中的元素)

今天参加小米现场笔试,遇到一个关于查找旋转数组中的元素算法问题,在这里记录一下。题目大致如下:给定一个有序数组,如{1,2,3,4,5,6,7,8,9},我们将对这个数组进行选择,未知从什么位置旋转。下面给出一个可能的旋转结果。如{4,5,6,7,8,9,1,2,3},我们可以理解为它从元素4位置开始旋转。之后给定一个指定的数字n,让我们从{4,5,6,7,8,9,1,2,3}这个数组中找出它的位置,要求时间复杂度尽可能的低。

拿到这个题目,我们很容易的想到,可以直接遍历一次数组,在时间复杂度为O(n)的情况下可以找到这个元素。但是我们并没有利用到这个数组是有序的前提条件。事实上,如果我们仔细观察的话,可以发现这个数组集合其实可以理解为是2个非递减的有序集合。{4,5,6,7,8,9,1,2,3}这个集合可以划分为{4,5,6,7,8,9}和{1,2,3}。如果我们可以找到这个拐点,那么我们也可以很轻松的在O(lgn)的时间复杂度下,利用二分查找找出该元素。那么我们如何找出这个拐点呢,其实在O(lgn)的时间复杂度下可以找到。

在这里我们不妨在观察一下这个数组的特性。当我们选取一个拐点,将这个数组进行选择的时候,我们可以发现,从数组的中间元素开始,可以将这个数组划分为两个子数组。并且必然有一边的数组是有序的集合。{4,5,6,7,8,9,1,2,3},该例中,middle元素为8,我们发现了他的左边元素为{4,5,6,7}是一个有序的集合。如果我们要查找的元素恰好在这个有序的数组内,那么只需要利用二分查找,便可以很快地找出这个元素。当然,我们需要一个条件来判断该元素是否在这个集合中。例:我们现在要查找的数为n if(n<array[middle]&&n>array[low]) 如果我们当前查找的数,小于middle元素,并且大于左边最小的元素。其实就可以理解为左边的数组是有序的了。我们就可以在左边集合中进行查找;反之,我们需要在右侧数组中查找该元素。

下面贴上代码

public static int search(int n,int[] array){
		int low = 0;		
		int high = array.length-1;
		while(low<=high){
			int middle = (low+high)/2;
			if(array[middle]==n) return middle;
			if(array[middle]>array[low]){  //left is order
				if(n<=array[middle]&&n>=array[low]){
					high = middle-1;
				}else {
					low = middle+1;
				}
			}else {			
				if(n>=array[middle]&&n<=array[high]){
					low = middle+1;
				}else {
					high = middle-1;
				}
			}
		}
		return -1;	
	} 


点赞