一、寻找一个数(基本的二分搜索)
int binary_search(int[] nums, int target) { int left = 0, right = nums.length - 1; while(left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if(nums[mid] == target) { // 直接返回 return mid; } } // 直接返回 return -1; }
1、为什么 while 循环的条件中是 <=,而不是 <?
答:因为初始化right的赋值是nums.length - 1,即最后一个元素的索引,而不是nums.length。
这二者可能出现在不同功能的二分查找中,区别是:前者相当于两端都闭区间[left, right],后者相当于左闭右开区间[left, right),因为索引大小为nums.length是越界的。
2、为什么left = mid + 1,right = mid - 1?我看有的代码是right = mid或者left = mid,没有这些加加减减,到底怎么回事,怎么判断?
答:这也是二分查找的一个难点,不过只要你能理解前面的内容,就能够很容易判断。
刚才明确了「搜索区间」这个概念,而且本算法的搜索区间是两端都闭的,即[left, right]。那么当我们发现索引mid不是要找的target时,下一步应该去搜索哪里呢?
当然是去搜索[left, mid-1]或者[mid+1, right]对不对?因为mid已经搜索过,应该从搜索区间中去除。
3、为什么返回left而不是right?
答:都是一样的,因为 while 终止的条件是left == right。
二、寻找左侧边界的二分搜索
写法一
因为我们初始化 right = nums.length-1
所以决定了我们的「搜索区间」是 [left, right]
所以决定了 while (left <=right)
决定了下一步的二分区间为[left,mid-1] [mid+1,right]
同时也决定了 left = mid +1 和 right = mid-1
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要立即返回
而要令 right = mid - 1锁定左侧边界
int left_bound(int[] nums, int target) { int left = 0, right = nums.length - 1; while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid - 1; } else if (nums[mid] == target) { // 别返回,锁定左侧边界 right = mid - 1; } } // 最后要检查 left 越界的情况 if (left >= nums.length || nums[left] != target) return -1; return left; }
写法二
因为我们初始化 right = nums.length
所以决定了我们的「搜索区间」是 [left, right)
所以决定了 while (left < right)
决定了下一步的二分区间为[left,mid) [mid+1,right)
同时也决定了right = mid 和 left = mid +1
因为我们需找到 target 的最左侧索引
所以当 nums[mid] == target 时不要立即返回
而要令 right = mid 锁定左侧边界
int left_bound(int[] nums, int target) { if (nums.length == 0) return -1; int left = 0; int right = nums.length; // 注意 while (left < right) { // 注意 int mid = (left + right) / 2; if (nums[mid] == target) { right = mid; } else if (nums[mid] < target) { left = mid + 1; } else if (nums[mid] > target) { right = mid; // 注意 } } return left; }