模板:Binary Search - 二分搜索
2017-01-17 08:16
337 查看
二分搜索是一种在有序数组中寻找目标值的经典方法,也就是说使用前提是『有序数组』。非常简单的题中『有序』特征非常明显,但更多时候可能需要我们自己去构造『有序数组』。下面我们从最基本的二分搜索开始逐步深入。
定义 lower bound 为在给定升序数组中大于等于目标值的最小索引,upper bound 则为小于等于目标值的最大索引,下面上代码和测试用例。
以
如果遇到有问插入索引的位置时,可以分三种典型情况:
目标值在数组范围之内,最后返回值一定是
目标值比数组最小值还小,此时
故最后返回
目标值大于等于数组最后一个值,由于循环退出条件为
综上所述,返回
有时复杂的边界条件处理可以通过『补项』这种优雅的方式巧妙处理。
关于lb 和 ub 的初始化,由于
Peak Element. 尤其是 Find Peak Element 中 lb 和 ub 的初始值如果初始化为-1和数组长度会带来一些麻烦。
模板 - lower/upper
bound
定义 lower bound 为在给定升序数组中大于等于目标值的最小索引,upper bound 则为小于等于目标值的最大索引,下面上代码和测试用例。
Java
import java.util.*; public class Main { public static void main(String[] args) { int[] nums = new int[]{1,2,2,3,4,6,6,6,13,18}; System.out.println(lowerBound(nums, 6)); // 5 System.out.println(upperBound(nums, 6)); // 7 System.out.println(lowerBound(nums, 7)); // 8 System.out.println(upperBound(nums, 7)); // 7 } /* * nums[index] >= target, min(index) */ public static int lowerBound(int[] nums, int target) { if (nums == null || nums.length == 0) return -1; int lb = -1, ub = nums.length; while (lb + 1 < ub) { int mid = lb + (ub - lb) / 2; if (nums[mid] < target) { lb = mid; } else { ub = mid; } } return lb + 1; } /* * nums[index] <= target, max(index) */ public static int upperBound(int[] nums, int target) { if (nums == null || nums.length == 0) return -1; int lb = -1, ub = nums.length; while (lb + 1 < ub) { int mid = lb + (ub - lb) / 2; if (nums[mid] > target) { ub = mid; } else { lb = mid; } } return ub - 1; } }
源码分析
以lowerBound的实现为例,以上二分搜索的模板有几个非常优雅的实现:
while循环中
lb + 1 < ub, 而不是等号,因为取等号可能会引起死循环。初始化
lb < ub时,最后循环退出时一定有
lb + 1 == ub.
mid = lb + (ub - lb) / 2, 可有效防止两数相加后溢出。
lb和
ub的初始化,初始化为数组的两端以外,这种初始化方式比起
0和
nums.length - 1有不少优点,详述如下。
如果遇到有问插入索引的位置时,可以分三种典型情况:
目标值在数组范围之内,最后返回值一定是
lb + 1
目标值比数组最小值还小,此时
lb一直为
-1,
故最后返回
lb + 1也没错,也可以将
-1理解为数组前一个更小的值
目标值大于等于数组最后一个值,由于循环退出条件为
lb + 1 == ub, 那么循环退出时一定有
lb = A.length - 1, 应该返回
lb + 1
综上所述,返回
lb + 1是非常优雅的实现。其实以上三种情况都可以统一为一种方式来理解,即索引
-1对应于数组前方一个非常小的数,索引
ub即对应数组后方一个非常大的数,那么要插入的数就一定在
lb和
ub之间了。
有时复杂的边界条件处理可以通过『补项』这种优雅的方式巧妙处理。
关于lb 和 ub 的初始化,由于
mid = lb + (ub - lb) / 2, 且有
lb + 1 < ub,故 mid 还是有可能为
ub - 1或者
lb + 1的,在需要访问
mid + 1或者
mid - 1处索引的元素时可能会越界。这时候就需要将初始化方式改为
lb = 0, ub = A.length - 1了,最后再加一个关于
lb, ub处索引元素的判断即可。如 Search for a Range 和 Find
Peak Element. 尤其是 Find Peak Element 中 lb 和 ub 的初始值如果初始化为-1和数组长度会带来一些麻烦。
相关文章推荐
- Binary Search 二分查找,二分搜索 C++
- 排序算法-11-二分搜索(Binary Search)
- 二分搜索(Binary Search)
- 《算法导论》习题2.3-5 二分搜索 Binary Search
- 二分搜索树(Binary search tree)
- 【菜鸟er】搜索专题_二分搜索模板
- 二分搜索 (Binary Search)
- 模板:二分搜索技术
- 模板:二分搜索技术
- 第五章 ALDS1_4_B Binary Search 二分搜索
- Leetcode ☞ 35. Search Insert Position 【binary search 二分查找 模板】
- 【LeetCode】 Binary Search 二分搜索 - Easy
- Binary Search 二分查找,二分搜索 C++
- 最大化平均值---二分搜索
- 特定字符输出及二分搜索 简单面试编程题
- 递归二分搜索
- sicily Binary Search 二分查找
- 深度优先搜索代码模板
- C# IB Google 搜索结果采集模板
- 搜索算法--线性搜索、二分搜索、内插搜索、剪枝搜索