常规有序数组二分查找和旋转有序数组二分查找—Java实现

常规有序数组二分查找

二分查找又称折半查找(Binary Search),是一种效率较高的查找方法。二分查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。

查找过程:首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。重复上述过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。 

举例说明:现在我有1到50的数字(这一组数字不一定是连续整数,但一定是有序排列),随机选取其中一数(假设为35),你来猜到我所选择的数字,每次猜测后,我会告诉你大了,小了,或者对了。常规思路是从1一直猜到35,这种简单查找最大的问题是假设我随机选中的数字为50,那么你可能需要从1猜到50了!假设随机选取的数为n,则简单查找需要n步,显然时间计算成本太高,那么有没有更好的查找方式呢?

答案当然是有的,接下来我们就来聊一聊二分查找,还是刚刚的1到50的数,同样需要你查找35,首先将表{1,2,3,⋯⋯50}分成前后两个子表{1,2,3,⋯⋯25}和{26,27,28,⋯⋯50},25即为关键字,你猜25,我告诉你“小了”,这样就排除了一半,你再将{26,27,28,⋯⋯50}分成两个字表{26,27,28,⋯⋯37}和{38,39,40⋯⋯50},此时37及为关键字,你猜37,我告诉你“大了”,这样就又排除了一半,再将{26,27,28,⋯⋯37}分成两个字表{26,27,28,⋯⋯31}和{32,33,34⋯⋯37},,此时31及为关键字,你猜31,我告诉你“小了”,如此重复,直至查找到35为止。用二分查找最多需要logn步。

Java实现(使用递归):

public static int BinarySearch(int[] arr,int target,int start,int end) {
	if(target<arr[start]||target>arr[end]||start>end) {
		return -1;
	}
	//middle=(start+end)/2有可能溢出
	int middle=start+(end-start)/2;
	if(arr[middle]>target) {
		return BinarySearch(arr,target,start,middle-1);
	}
	else if(arr[middle]<target) {
		return BinarySearch(arr,target,middle+1,end);
	}
	else {
		return middle;
	}
}

测试代码:

public static void main(String[] args) {
	int[] arr= {2,4,6,8,12,16,19,22,25,26,29,30,36,39,47,58,66,71,77,88};
	int target=39;
	System.out.print(BinarySearch(arr,target,0,arr.length-1));
}

Java实现(不使用递归):

public static int BinarySearch(int[] arr,int target) {
	int start=0;
	int end=arr.length-1;
	int middle=0;
	if(target<arr[start]||target>arr[end]||start>end) {
		return -1;
	}
	while(start<=end) {
		//middle=(start+end)/2有可能溢出
		middle=start+(end-start)/2;
		if(target<arr[middle]) {
			end=middle-1;
		}
		else if(target>arr[middle]) {
			start=middle+1;
		}
		else {
			return middle;	
		}
	}
	return -1;
}

测试代码:

public static void main(String[] args) {
	int[] arr= {2,4,6,8,12,16,19,22,25,26,29,30,36,39,47,58,66,71,77,88};
	int target=39;
	System.out.print(BinarySearch(arr,target));
}

到这里是不是感觉问题迎刃而解了,但可别高兴的太早,二分查找还有许多变化可以挖掘,例如我们刚刚研究的只是常规有序数组,但如果变成了旋转有序数组呢?所谓旋转有序数组,举个栗子你就明白了,原数组:{2,4,6,8,12,16,19,22,25,26},旋转数组:{12,16,19,22,25,26,2,4,6,8},这样的旋转数组还有很多,下面我们就来聊聊如何对旋转有序数组进行二分查找吧!

旋转有序数组二分查找

数组:int[] arr={12,16,19,22,25,26,2,4,6,8}

查找:int target=22

查找过程:每次根据middle=start+(end-start)/2求出middle后,middle的左边数组为[start, middle], middle的右边数组为[middle+1, end],这两个数组中至少有一个是有序数组。将arr[middle]与target进行比较:

(1) arr[middle] ==target,此时要查找的数正好是arr[middle],因此返回middle;
(2) arr[middle] < arr[end],此时至少可说明右边数组为有序数组,当arr[middle] < target 时,则start = middle + 1,否则 end= middle - 1;
(3) arr[middle] > arr[start],此时至少可说明左边数组为有序数组,当arr[middle] > target 时,则end= middle - 1,否则 start = mid + 1;

Java实现:

public static int BinarySearch(int[] arr,int target) {
	int start=0;
	int end=arr.length-1;
	while(start<end) {
		//middle=(start+end)/2有可能溢出
		int middle=start+(end-start)/2;
		if(target==arr[middle]) {
			return middle;
		}
		//如果左边数组为有序数组
		if(arr[middle]>arr[start]) {
			//判断target是否在左边数组,如果在,则继续遍历左边数组,如果不在,则遍历右边数组;
			if(target<arr[middle]) {
				end=middle-1;
			}else {
				start=middle+1;
			}
		}
		//如果右边数组为有序数组
		if(arr[middle]<arr[end]) {
			//判断target是否在右边数组,如果在,则继续遍历右边数组,如果不在,则遍历左边数组;
			if(target>arr[middle]) {
				start=middle+1;
			}else {
				end=middle-1;
			}
		}
	}
	return -1;
}

测试代码:

public static void main(String[] args) {
	int[] arr= {12,16,19,22,25,26,2,4,6,8};
	int target=26;
	System.out.print(BinarySearch(arr,target));
}
原创文章 12 获赞 6 访问量 1456

猜你喜欢

转载自blog.csdn.net/YUBANGSHUANGYUER/article/details/105704664