Binary Search 二分查找

Binary Search 二分查找

有序数组中(允许重复数字),查找第一个最后一个目标数字。时间复杂度为O(logN)。如果需要优化O(n)的时间复杂度,那么只能是O(logn)的二分法。

基本模板:

0. 查找一个(原始方法)

private static int bs(int[] nums, int target) {
	int start = 0;
	int end = nums.length - 1;
	int mid;
	while (start <= end) {
		mid = start + ((end - start) >> 1);
		if (target < nums[mid]) {
			end = mid - 1;
		} else if (target > nums[mid]) {
			start = mid + 1;
		} else {
			return mid;
		}
	}
	return -1;
}

1. 查找第一个

循环方法:

public static int firstBS(int[] nums, int target) {
	if (nums == null || nums.length == 0) {
		return -1;
	}
	int start = 0;
	int end = nums.length - 1;
	int mid;
	while (start + 1 < end) {
		mid = (start + ((end - start) >> 1));
		if (target < nums[mid]) {
			end = mid;
		} else if (target > nums[mid]) {
			start = mid;
		} else {
			end = mid;
		}
	}
	if (nums[start] == target) {
		return start;
	} else if (nums[end] == target) {
		return end;
	} else {
		return -1;
	}
}

递归方法:

public static int firstBS(int[] nums, int target, int start, int end) {
	if (nums == null || nums.length == 0) {
		return -1;
	}
	if (start + 1 < end) {
		int mid = start + ((end - start) >> 1);
		if (target < nums[mid]) {
			return firstBS(nums, target, start, mid);
		} else if (target > nums[mid]) {
			return firstBS(nums, target, mid, end);
		} else {
			return firstBS(nums, target, start, mid);
		}
	} else {
		if (nums[start] == target) {
			return start;
		} else if (nums[end] == target) {
			return end;
		} else {
			return -1;
		}
	}
}

2. 查找最后一个

循环方法:

public static int lastBS(int[] nums, int target) {
	if (nums == null || nums.length == 0) {
		return -1 ;
	}
	int start = 0;
	int end = nums.length - 1;
	int mid;
	while (start + 1 < end) {
		mid = (start + ((end - start) >> 1));
	<span style="white-space:pre">	</span>if (target < nums[mid]) {
			end = mid;
		} else if (target > nums[mid]) {
			start = mid;
		} else {
				start = mid;
			}
	}
	if (nums[end] == target) {
		return end;
	} else if (nums[start] == target) {
		return start;
	} else {
		return -1;
<span style="white-space:pre">	</span>}
}


递归方法:

public static int lastBS(int[] nums, int target, int start, int end) {
	if (nums == null || nums.length == 0) {
		return -1;
	}
	if (start + 1 < end) {
		int mid = start + ((end - start) >> 1);
		if (target < nums[mid]) {
			return lastBS(nums, target, start, mid);
		} else if (target > nums[mid]) {
			return lastBS(nums, target, mid, end);
		} else {
			return lastBS(nums, target, mid, end);
		}
	} else {
		if (nums[end] == target) {
			return end;
		} else if (nums[start] == target) {
			return start;
		} else {
			return -1;
		}
	}
}


总结:

1. 循环方法和递归方法结构上类似:

  1. 判断边界条件
  2. 判断 start + 1 < end
  3. 如果 target < nums[mid],则 end = mid。 慎重使用 end = mid - 1
  4. 如果 target > nums[mid],则 start = mid。慎重使用 start = mid + 1
  5. 如果 target == nums[mid],找第一个 end = mid ;找最后一个 start = mid。这一步可以和上面合并在一起。
  6. 判断 nums[start] nums[end] 是否是要找的目标数字。找第一个先判断 nums[start],找最后一个先判断 nums[end] 
2. 终止条件是 start 与 end 相邻。使用加减1时,start 和 mid 会重合,不好。
3. 计算 mid 时,使用  mid = (start + ((end - start) >> 1)) 可以防止 start + end 溢出。
4. 循环方法 mid 声明在 while循环外。如果只要找mid,则用break跳出循环。循环外要有return。
5. index 在加减1的时候考虑溢出。
6. 二分法的核心在于排除另一半。
7. 注意数组是否能保持有序,是否允许重复。无序数组只能遍历查找。
8. 旋转数组有特别的性质,两段都单调递增,第一段比第二段高,转点是最小值。用二分查找时关键在于排除另一半。

题目汇总:

1. Binary Search   查找第一个目标数字

2. Search Insert Position   查找第一个目标数字,最后根据位置判断。

3. Search for a Range   查找第一个和最后一个目标数字。

4. Search in Rotated Sorted Array 在旋转数组中找到目标数字,画图分析,二分排除。

5. Search in Rotated Sorted Array II 在含重复数字的旋转数组中找目标数字,无法基于二分查找进行优化,数组可看为无序,{2,2,3,2,2}, {3,2,2,2,3}。遍历查找即可。

6. Find Minimum in Rotated Sorted Array 在旋转数组中找到最小值,画图分析,二分排除,将转折点缩小范围到start 和 end。或者暴力求解。

7. Find Minimum in Rotated Sorted Array II 在含重复数字的旋转数组中找目标数字,无法基于二分查找进行优化,数组可看为无序。遍历查找即可。

8. First Bad Version 在boolean有序数组中找到第一个true,直接二分查找即可。

9. Find Peak Element 在多旋转数组中找一个波峰,只找mid,不用加减1。循环外要return。

10. Search a 2D Matrix 在矩阵中搜索目标数字。可以用多行二分搜索。或者右上角/左下角搜索。

11.  Search a 2D Matrix II 在矩阵中搜索目标数字的出现次数。可以用多行二分搜索。或者右上角/左下角搜索。

12. Sqrt(X) 求平方根。用二分搜索。注意乘法会溢出,改用除法。

13. Median of two Sorted Arrays 根据两个数组,求中位数。二分排除法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值