二分法有多少种写法都不重要,数组
重要的是要会写一种对的。函数
首先有几个数字要注意测试
中位数有两个,spa
经常使用的是下位中位数,通用的写法以下,语言int常常自动向下取整,翻译
median = (length - 1) / 2
指针的区间固然能够开区间,也能够闭区间,也能够半开半闭。但老老实实两头取闭区间老是不会错。上面的中位数,转换成两头闭区间 [low,high] 就变成下面这样:指针
median = low + (high - low) / 2
这里还有个常见的坑,不要图快用加法,会溢出,code
median = ( low + high ) / 2 // OVERFLOW
另一个关键点是“终结条件”。blog
不要以 low == high 作终结条件。会被跳过的,递归
if (low == high) { return (nums[low] >= target)? low : ++low; }
不相信在 [1, 5] 里找 0 试试?ip
正确的终结条件是:
low > high
也就是搜索空间为空。
知足终结条件之后,返回值彻底不须要纠结,直接返回低位 low。
由于回过头去放慢镜头,二分查找的过程就是一个 维护 low 的过程:
low从0起始。只在中位数遇到肯定小于目标数时才前进,而且永不后退。low一直在朝着第一个目标数的位置在逼近。知道最终到达。
至于高位 high,就放心大胆地缩小目标数组的空间吧。
因此最后的代码很是简单,
public int binarySearch(int[] nums, int target) { int low = 0, high = nums.length-1; while (low <= high) { int mid = low + (high - low) / 2; if (nums[mid] < target) { low = mid + 1; } if (nums[mid] > target) { high = mid - 1; } if (nums[mid] == target) { return mid; } } return low; }
递归版也同样简单,
public int binarySearchRecur(int[] nums, int target, int low, int high) { if (low > high) { return low; } //base case int mid = low + (high - low) / 2; if (nums[mid] > target) { return binarySearchRecur(nums,target,low,mid-1); } else if (nums[mid] < target) { return binarySearchRecur(nums,target,mid+1,high); } else { return mid; } }
但上面的代码能正常工做,有一个前提条件:
元素空间没有重复值
推广到有重复元素的空间,二分查找问题就变成:
寻找元素第一次出现的位置。
也能够变相理解成另外一个问题,对应C++的 lower_bound() 函数
寻找第一个大于等于目标值的元素位置。
但只要掌握了上面说的二分查找的心法,代码反而更简单,
public int firstOccurrence(int[] nums, int target) { int low = 0, high = nums.length-1; while (low <= high) { int mid = low + (high - low) / 2; if (nums[mid] < target) { low = mid + 1; } if (nums[mid] >= target) { high = mid - 1; } } return low; }
翻译成递归版也是同样,
public int firstOccurrenceRecur(int[] nums, int target, int low, int high) { if (low > high) { return low; } int mid = low + (high - low) / 2; if (nums[mid] < target) { return firstOccurrenceRecur(nums,target,mid + 1,high); } else { return firstOccurrenceRecur(nums,target,low,mid-1); } }
以上代码均经过leetcode测试。标准银弹。天天早起写一遍,锻炼肌肉。
最后想说,不要怕二分查找难写,边界状况复杂。实际状况是,你以为烦躁,大牛也曾经由于这些烦躁过。一些臭名昭著的问题下面,常常是各类大牛的评论(恶心,变态,F***,等等)。并且这并不考验什么逻辑能力,只是仔细的推演罢了。拿个笔出来写一写,算一算不丢人。不少问题完全搞清楚之后,常常就是豁然开朗,而后之后妥妥触类旁通。以上。