惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

宝玉的分享
宝玉的分享
NISL@THU
NISL@THU
E
Exploit-DB.com RSS Feed
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
K
Kaspersky official blog
Project Zero
Project Zero
Cisco Talos Blog
Cisco Talos Blog
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
T
Threatpost
S
Schneier on Security
G
GRAHAM CLULEY
The Hacker News
The Hacker News
T
Threat Research - Cisco Blogs
Scott Helme
Scott Helme
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
P
Privacy & Cybersecurity Law Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
Cyberwarzone
Cyberwarzone
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
AWS News Blog
AWS News Blog
Simon Willison's Weblog
Simon Willison's Weblog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
爱范儿
爱范儿
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG
P
Proofpoint News Feed
S
Securelist
G
Google Developers Blog
The Last Watchdog
The Last Watchdog
Google Online Security Blog
Google Online Security Blog
美团技术团队
F
Fortinet All Blogs
小众软件
小众软件
Recorded Future
Recorded Future
V
Visual Studio Blog
B
Blog RSS Feed
H
Help Net Security
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
博客园 - 聂微东
Stack Overflow Blog
Stack Overflow Blog
Martin Fowler
Martin Fowler
Latest news
Latest news
Spread Privacy
Spread Privacy
H
Heimdal Security Blog

博客园 - headchen

博文阅读密码验证 - 博客园 二进制集合运算 - headchen - 博客园 OI中字符串读入和处理 完全二叉树的性质 扫描线算法 评论备份(3) 评论备份(2) 用户中心 - 博客园 sam模板 二分图相关 KMP算法详解 用户中心 - 博客园 中国国家集训队论文集目录(1999-2009) 最短路Dijkstra算法的一些扩展问题 用户中心 - 博客园 async await 异步编程杂记 IOS 杂记 合并 ios 静态库 android studio 偶记
二分法的注意事项
headchen · 2018-04-27 · via 博客园 - headchen

二分法有多少种写法都不重要,

重要的是要会写一种对的。

首先有几个数字要注意

中位数有两个,

  1. 下位中位数:lowerMedian = (length - 2) / 2
  2. 上位中位数:upperMedian = length / 2

常用的是下位中位数,通用的写法如下,语言int经常自动向下取整,

median = (length - 1) / 2

指针的区间当然可以开区间,也可以闭区间,也可以半开半闭。但老老实实两头取闭区间总是不会错。上面的中位数,转换成两头闭区间 [low,high] 就变成下面这样:

median = low + (high - low) / 2

这里还有个常见的坑,不要图快用加法,会溢出,

median = ( low + high ) / 2 // OVERFLOW

另外一个关键点是“终结条件”

不要以 low == high 做终结条件。会被跳过的,

if (low == high) { 
    return (nums[low] >= target)? low : ++low;
}

不相信在 [1, 5] 里找 0 试试?

正确的终结条件是:

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***,等等)。而且这并不考验什么逻辑能力,只是仔细的推演罢了。拿个笔出来写一写,算一算不丢人。很多问题彻底搞清楚以后,经常就是豁然开朗,然后以后妥妥举一反三。以上。