2019年终记事:一年的经历那么多,回忆起来却短的不可想象。2019年的最后一篇博客,回想第一篇到现在,虽然一路上走走停停,但学习的脚步还在前进。短短二十年放弃的东西比一声叹息多多了,回头看来人生得要有一件能坚持下去的东西……新年的钟声带不走2019的遗憾,希望它能带来2020的希望。
引言
在计算机科学中,二分搜索,也称为半间隔搜索,对数搜索,是一种搜索算法,用于查找排序数组中目标值的位置。二分搜索将目标值与数组的中间元素进行比较。
如果它们不相等,则消除目标不能位于其中的那一半,并在剩余的一半上继续搜索,再次将中间元素与目标值进行比较,并重复此过程直到找到目标值。
如果搜索以其余一半为空结束,则目标不在数组中。
在最坏的情况下,二分搜索会以对数时间运行,进行 O ( log n ) O(\log_{}n) O(logn)比较,其中 n {n} n是数组中元素的数量, O {O} O是Big O表示法, 而 log {\log} log是对数。 除了小数组以外,二分搜索比线性搜索快。 但是,必须首先对数组进行排序才能应用二进制搜索。有专门为快速搜索而设计的专用数据结构,例如哈希表,可以比二进制搜索更有效地进行搜索。 但是,二分搜索可用于解决更广泛的问题,例如,即使数组中不存在目标,也要在数组中找到相对于目标而言第二小的元素。
二进制搜索树和B树数据结构基于二分搜索。
——wiki
在leetcode刷题时我们总能遇到可以利用二分搜索解决的问题,但往往我们写得出来的二分搜索代码并不能work或者潜藏有bug。造成这种现象的原因是纷杂的情况导致算法的细节处理不同而容易忽视边界的细节问题。我们需要一个简洁明了的思路来将问题一般化。矛盾的特殊性应该包含在矛盾的一般性当中,用高度抽象化的过程对具体问题降维打击。
编写博客时翻阅了不少leetcode中的二分题解,在此借鉴并总结一下。在完成的过程中翻阅了《计算机程序设计艺术》的相关章节,受益颇多,一部分内容会穿插在文中讲述。
入题
一些背景——
《计算机程序设计艺术》的作者 Donald Knuth:
Although the basic idea of binary search is comparatively straightforward, the details can be surprisingly tricky …
译:“虽然二分搜索的基本思想很直白,但细节出奇的难以应对…”
他在《计算机程序设计艺术(第三卷)》中也指出了(大意):也许是第一部出版的关于非数值程序设计方法的书(1946)中,首先提出了二分查找。再到后来的许多人对二分算法的改进直至60年代往后,所有工作才算是基本完成。
一些待解决的问题——
- 中位数索引值的获取
- 循环执行条件的设置与返回值的选择
- 分支语句的选择
关于模板——
这里借助Thomas H. Cormen在《算法基础–打开算法之门》中的一段伪代码:
程序 BINARY-SEARCH(A,n,x)
输入:
·A:一个数组;
·n:要查找的数组A中元素的个数;
·x:要查找的值;
输出:要么是满足A[i]=x的索引i,要么是一个特殊值 NOT-FOUND(可取相对数组A的任何无效索引值,例如0或任意负整数)。
//左右索性记为p,r;中间位置记为q
1. 将p赋值为1,将r赋值为n。
2. 只要p≤r,执行如下操作:
A. 将q赋值为⌊(p+r)/2⌋。
B. 如果A[q]=x,那么返回q。
C. 否则(A[q]≠x),如果A[q]>x,那么将r赋值为q-1。
D. 否则(A[q]<x),那么将p赋值为q+1。
3. 返回 NOT-FOUND。
这段算法描述是我们常规的理解和处理方法,这次打算讨论的是在它的基础上进行一定的改变,并简单谈谈优劣。
基本思想————
二分的基本思想其实就是每次可以将当前的数中将近一半的不满足要求的数全部除掉,所以大O时间复杂度是