1. 概述
二分查找相信大家都不陌生,表面上看这是一个非常简单的算法,但是却很少有人能将这个算法写对,正如大佬Knuth(发明KMP算法的那位)所说:
"Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky..."
思路很简单,细节是魔鬼
最传统的二分写法是这样的:
int binarySearch(int[] nums, int target) {
int left = 0, right = ...;
while(...) {
int mid = (right + left) / 2;
if (nums[mid] == target) {
...
} else if (nums[mid] < target) {
left = ...
} else if (nums[mid] > target) {
right = ...
}
}
return ...;
}
上面这种写法对于基本的二分场景是够用了,但是涉及到一些模糊查询,比如:寻找第一个大于等于target的数、最后一个小于等于target的数、第一个大于target的数、最后一个小于target的数。这种场景下,传统二分算法的边界条件啊,mid是否应该+1啊,left和right是否应该相等啊,等等这些条件简直让人眼花缭乱。所以接下来我们开辟一种新的思路,不需要去考虑这些冗杂的条件也能将上面的几个问题解决掉。
2. 基本思路
将排列好的数组划分为红色和蓝色两个区域,left和right想象为两个指针,保证循环完成后两个指针的位置就是红色和蓝色区域的边界。下面来举例说明:比如nums = {1,2,3,5,5,5,8,9}, 需要寻找nums数组中第一个大于等于5的,那么我下标,们可以这样做:
- 将nums数组分为两部分:第一部分为{1,2,3},第二部分为{5,5,5,8,9}。
- 循环之后,使得left和right指针的位置为下图所示:
然后可以惊喜的发现,这个right不就是我们要找的第一个大于等于5的值嘛。 所以问题的核心就转化为了:
- 如何划分红色和蓝色区域?通常以我们需要寻找的那个数的下标i来进行区域划分。
- 如何移动指针的位置?
import java.util.*;
public class binary_search {
public static void main(String[] args) {
int[] nums = {1,2,3,5,5,5,8,9};
int ans1 = findDydy(nums,10);
int ans2 = findXyDyL(nums,0);
int ans3 = findDy(nums,8);
int ans4 = findXu(nums,0);
//System.out.println(ans4);
String str = "sdafdsd";
if (str.contains("")) {
System.out.println("11111");
}
System.out.println((4 & 0x04) != 0);
}
/**
* 二分的整体思路:将有序数组划分为蓝色和红色两个区域
*
*
* @param nums
* @param tar
* @return
*/
//第一个查找大于等于tar的数
public static int findDydy(int[] nums, int tar){
int left = -1;
int right = nums.length;
int mid;
while (left +1 != right) {
mid = left + right >> 1;
if (nums[mid] < tar) {
left = mid;
} else {
right = mid;
}
}
if (right == nums.length) {
return -1000;
}
return nums[right];
}
//查找第一个小于等于tar的数
public static int findXyDyL(int[] nums, int tar){
int left = -1;
int right = nums.length;
int mid;
while (left +1 != right) {
mid = left + right >> 1;
if (nums[mid]<= tar) {
left = mid;
} else {
right = mid;
}
}
if (left == -1) {
return -1000;
}
return nums[left];
}
//查找第一个大于tar的数
public static int findDy(int[] nums, int tar){
int left = -1;
int right = nums.length;
int mid;
while(left+1 != right){
mid = left + right >> 1;
if (nums[mid] <= tar) {
left = mid;
} else {
right = mid;
}
}
if (right == nums.length) {
return -1000;
}
return nums[right];
}
//查找第一个小于tar的数
public static int findXu(int[] nums, int tar){
int left = -1;
int right = nums.length;
int mid;
while(left +1 != right){
mid = left + (right-left >> 1);
if (nums[mid] < tar) {
left = mid;
} else {
right = mid;
}
}
if (left == -1) {
return -1000;
}
return nums[left];
}
}