认=识=二=分=法


经常见到的二分类型是在一个有序数组上,开展二分搜索。但是真的所有问题求解时使用二分的必要条件吗?不。主要能正确的构建左右两侧的淘汰逻辑,你就可以二分。

常见的写法

mid = L + ((R - L) >> 1)

因为 mid = (L+R)/2 的话,可能会在计算 L + R 的时候溢出。所以转变成 mid = L + (R - L)/2 。因为 R - L 的减法不会溢出,再除也不会溢出,而加上 L 后肯定比 R 小,R 都不溢出,肯定就不会溢出。然后 /2 可以转变成 >>1,因为位运算一定比除法快。

N * 2 + 1 = ((N << 1) | 1)

N * 2 + 1,可以写成 ((N << 1) | 1),因为左移一位相当于*2,此时最右边一位肯定是0,再与1进行或运算就相当于+1。

题目

例1:在一个有序数组中,找某个数是否存在?

pubilc static boolean exist(int[] sortedArr, int num) {
	if (sortedArr == null || sortedArr.length == 0) {
		return false;
	}
	int L = 0;
	int R = sortedArr.length()-1;
	int mid = 0;
	while (L < R) {
		mid = L + ((R - L) >> 1);
		if (sortedArr[mid] == num) {
			return true;
		} else if (sortedArr[mid] > num) {
			R = mid-1;
		} else {
			L = mid+1;
		}	
	return sortedArr[mid] == num;
}

例2:在一个有序数组中,找到 >= 某个数最左侧的位置

pubilc static int nearestIndex(int[] arr, int value) {
	int L = 0;
	int R = arr.length-1;
	int index = -1;
	while(L <= R) {
		int mid = L + ((R - L) >> 1);
		if (arr[mid] >= value) { // mid 比 value 大,说明在左边
			index = mid;
			R = mid - 1;
		} else {
			L = mid + 1;
		}
	}
	return index;
}

例3:在一个有序数组中,找到 <= 某个数最右侧的位置

pubilc static int nearestIndex(int[] arr, int value) {
	int L = 0;
	int R = arr.length-1;
	int index = -1;
	while(L <= R) {
		int mid = L + ((R - L) >> 1);
		if (arr[mid] <= value) {
			index = mid;
			L = mid + 1;
		} else {
			R = mid - 1;
		}
	}
	return index;
}

例4:无须数组中任意两个数不相等,求局部最小值

先看0位置是不是局部最小(和1位置比),如果是就返回,否则说明趋势是向下的;再看N-1位置是不是局部最小(和N-2位置比),如果是就返回,否则说明趋势是向上的。【注意局部最小不是全局最小】一个开始局势向下,最后趋势向上的曲线,中间一定存在局部最小。所以直接来到中点的位置mid:
1)如果mid位置既比mid-1位置小,又比mid+1位置小,那么直接返回mid位置的值。
2)如果mid不是局部最小,那么mid或者比左边大,或者比右边大,或者比两边都大,甭管哪种情况。我们不妨假设 mid 比 mid-1 位置大,那么一开始的确实就重现了,我 mid 右侧就不要了,直接在左侧二分。
注意不需要左右都找,只要确定有一边有局部最小就行了。

public static int getLessIndex(int[] arr) {
	if (arr == null || arr.length == 0) {
		return -1;
	}
	if (arr[0] < arr[1])
		return 0;
	if (arr[arr.length-1] < arr[arr.length-2]) 
		return arr.length-1;
	
	int L = 1;
	int R = arr.length-2;
	int mid = 0;
	while (L < R) {
		mid = L + ((R - L) >> 1);
		if (arr[mid] > arr[mid - 1]) {
			R = mid - 1;
		} else if (arr[mid] > arr[mid + 1]) {
			L = mid + 1;
		} else { // 表示既比左边小又比右边小
			return mid;
		}
	}
	return left;
}
发布了40 篇原创文章 · 获赞 0 · 访问量 384

猜你喜欢

转载自blog.csdn.net/weixin_43780400/article/details/105633327