在计算机科学中，**二分查找算法**（英語：），也称**折半搜索算法**（英語：）、**对数搜索算法**（英語：），是一种在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始，如果中间元素正好是要查找的元素，则搜索过程结束；如果某一特定元素大于或者小于中间元素，则在数组大于或小于中间元素的那一半中查找，而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空，则代表找不到。这种搜索算法每一次比较都使搜索范围缩小一半。

二分查找算法在最坏情况（英语：）下是对数时间复杂度的，需要进行O(\log n)次比较操作（n在此处是数组的元素数量，O是大O记号，\log 是对数）。二分查找算法使用常数空间，对于任何大小的输入数据，算法使用的空间都是一样的。除非输入数据数量很少，否则二分查找算法比线性搜索更快，但数组必须事先被排序。尽管一些特定的、为了快速搜索而设计的数据结构更有效（比如哈希表），二分查找算法应用面更广。

二分查找算法有许多种变种。比如分散层叠（英语：）可以提升在多个数组中对同一个数值的搜索的速度。分散层叠有效的解决了计算几何学和其他领域的许多搜索问题。指数搜索（英语：）将二分查找算法拓宽到无边界的列表。二叉搜索树和B树数据结构就是基于二分查找算法的。

演算法
---

二分搜索只对有序数组有效。二分搜索先比较数组中位元素和目标值。如果目标值与中位元素相等，则返回其在数组中的位置；如果目标值小于中位元素，则搜索继续在前半部分的数组中进行。如果目标值大于中位元素，则搜索继续在数组上部分进行。由此，算法每次排除掉至少一半的待查数组。

### 步驟

給予一個包含n個帶值元素的陣列A或是記錄{\displaystyle A\_{0},\cdots ,A\_{n-1}}，使{\displaystyle A\_{0}\leq \cdots \leq A\_{n-1}}，以及目標值T，還有下列用來搜尋T在A中位置的子程式。

1. 令L為{\displaystyle 0 }，R為{\displaystyle n-1}。
2. 如果{\displaystyle L>R}，則搜尋以失敗告終。
3. 令{\displaystyle m}（中間值元素）為{\displaystyle \lfloor (L+R)/2\rfloor }。（具体实现中，为防止算術溢出，一般采用{\displaystyle \lfloor L+(R-L)/2\rfloor }代替。）
4. 如果{\displaystyle A\_{m}<T}，令L為{\displaystyle m+1}並回到步驟二。
5. 如果{\displaystyle A\_{m}>T}，令R為{\displaystyle m-1}並回到步驟二。
6. 當{\displaystyle A\_{m}=T}，搜尋結束；回傳值{\displaystyle m}。

這個疊代步驟會持續透過兩個變數追蹤搜索的邊界。有些實際應用會在演算法的最後放入相等比較，讓比較迴圈更快，但平均而言會多一層疊代。

### 大致匹配

以上程序只適用於*完全*匹配，也就是尋找一個目標值的位置。不過，因為有序陣列的順序性，將二分搜索算法擴展到能適用大致匹配並不是很重要。舉例來說，二分搜索算法可以用來計算一個賦值的**排名**（或稱**秩**，比它更小的元素的數量）、**前趨**（下一個最小元素）、**後繼**（下一個最大元素）以及**最近鄰**。搜尋兩個值之間的元素數目的範圍查詢（英语：）可以藉由兩個排名查詢（又稱**秩查詢**）來執行。

* 排名查詢可以使用調整版的二分搜索來執行。藉由在成功的搜索回傳{\displaystyle m}，以及在失敗的搜索回傳L，就會取而代之地回傳了比起目標值小的元素數目。
* 前趨和後繼查詢可以藉由排名查詢來執行。一旦知道目標值的排名，其前趨就會是那個位於其排名位置的元素,或者排名位置的上一个元素（因為它是小於目標值的最大元素）。其後繼是（陣列中的）下一個元素，或是（非陣列中的）前趨的下一個元素。目標值的最近鄰可能是前趨或後繼，取決於何者較為接近。
* 範圍查詢也是直接了當的。一旦知道兩個值的排名，不小於第一個值且小於第二個值的元素數量就會是兩者排名的差。這個值可以根據範圍的端點是否算在範圍內，或是陣列是否包含其端點的對應鍵來增加或減少1。

复杂度分析
-----

时间复杂度折半搜索每次把搜索区域减少一半，时间复杂度为O\left(\log n\right)。（n代表集合中元素的个数）
空间复杂度O\left(1\right)。虽以递归形式定义，但是尾递归，可改写为循环。

应用
--

除直接在一个数组中查找元素外，可用在插入排序中。

示例代码
----

### C 版本- 递归

```
int binary\_search(const int arr[], int start, int end, int key) {
 if (start > end)
 return -1;

 int mid = start + (end - start) / 2; //直接平均可能會溢位，所以用此算法
 if (arr[mid] > key)
 return binary\_search(arr, start, mid - 1, key);
 else if (arr[mid] < key)
 return binary\_search(arr, mid + 1, end, key);
 else
 return mid; //最後檢測相等是因為多數搜尋狀況不是大於要不就小於
}

```

### C 版本- while 循环

```
int binary\_search(const int arr[], int start, int end, int key) {
 int ret = -1; // 未搜索到数据返回-1下标
 
 int mid;
 while (start <= end) {
 mid = start + (end - start) / 2; //直接平均可能會溢位，所以用此算法
 if (arr[mid] < key)
 start = mid + 1;
 else if (arr[mid] > key)
 end = mid - 1;
 else { // 最後檢測相等是因為多數搜尋狀況不是大於要不就小於
 ret = mid; 
 break;
 }
 }
 
 return ret; // 单一出口
}

```

### javascript 版本

```
var arr = [1, 3, 5, 7, 9, 10, 11, 12, 14, 15, 19, 20];
const binarySearch = (arr, target) => {
 const search = (start, end) => {
 if (start > end) return -1;
 const mid = start + Math.floor((end - start) / 2);
 if (arr[mid] > target) {
 return search(0, mid - 1);
 } else if (arr[mid] < target) {
 return search(mid + 1, end);
 } else {
 return mid;
 }
 }
 return search(0, arr.length - 1);
}
console.log( binarySearch(arr, 4) );

```

### Python3 版本 while 循环

```
def binary\_search(arr, left, right, key):
    while left <= right:
        mid = left + (right - left) // 2
        if arr[mid] == key:
            return mid
        elif arr[mid] < key:
            left = mid + 1
        elif arr[mid] > key:
            right = mid - 1
    return -1

```

### Python3 版本 递归

```
def binary\_search(arr, start, end, key):
	if start > end:
		return -1
	mid = start + (end - start) // 2
	if arr[mid] > key:
		return binary\_search(arr, start, mid - 1, key)
	if arr[mid] < key:
		return binary\_search(arr, mid + 1, end, key)
	return mid

```

### C# 版本

```
static int binary\_search(int[] arr, int start, int end, int key)
{
 int mid;
 while (start <= end)
 {
 mid = (start + end) / 2;
 if (arr[mid] < key)
 start = mid + 1;
 else if (arr[mid] > key)
 end = mid - 1;
 else
 return mid; 
 }
 return -1;
}

```

### Swift 版本

```
import Foundation
/// 二分搜索完全匹配
///
/// - Parameters:
/// - arr: 有序数组
/// - start: 起始位置
/// - end: 结束点
/// - key: 特点目标值
/// - Returns: 返回查找结果
func binarySearch(arr: [Int], start: Int, end: Int, key: Int) -> Int? {
    if start > end {
        return nil
    }
    let mid = start + (end - start) / 2
    if arr[mid] > key {
        return binarySearch(arr: arr, start: start, end: mid - 1, key: key)
    } else if arr[mid] < key {
        return binarySearch(arr: arr, start: mid + 1, end: end, key: key)
    } else {
        return mid
    }
}

```

### golang 递归版本

```
func binary\_search(arr []int, low, high, key int) int {
 if low > high {
 return -1
 }
 mid := low + (high-low)/2

 if arr[mid] > key {
 return binary\_search(arr, low, mid-1, key)
 } else if arr[mid] < key {
 return binary\_search(arr, mid+1, high, key)
 }

 return mid
}

```

### golang 非递归版本

```
func binarySearch(arr []int, key int) int {
 low, high := 0, len(arr)-1
 for low <= high {
 mid := low + (high-low)/2
 if arr[mid] == key {
 return mid
 } else if key < arr[mid] {
 high = mid - 1
 } else if key > arr[mid] {
 low = mid + 1
 }
 }
 return -1
}

```

### Java 递归

```
public static int binarySearch(int[] arr, int start, int end, int key){
 if (start > end)
 return -1;

 int mid = start + (end - start)/2; //防止溢位
 if (arr[mid] > key)
 return binarySearch(arr, start, mid - 1, key);
 if (arr[mid] < key)
 return binarySearch(arr, mid + 1, end, key);
 return mid; 

}

```

### Java while 循环

```
public static int binarySearch(int[] arr, int start, int end, int key){
 int result = -1;

 while (start <= end){
 int mid = start + (end - start)/2; //防止溢位
 if (arr[mid] > key)
 end = mid - 1;
 else if (arr[mid] < key)
 start = mid + 1;
 else {
 result = mid ; 
 break;
 }
 }

 return result;

}

```

### Julia版本

```
# Julia Sample : BinarySearch
function BinarySearch(A,Key)
 left,right = 1,length(A)

 while(left<=right)
 mid=left+floor(Int,((right-left)/2))
 
 if A[mid]==Key
 return mid
 elseif Key<A[mid]
 right = mid-1
 elseif Key>A[mid]
 left = mid+1
 end
 end
 return -1
end

# Main Code
A = [1,3,16,31,43,354,586] # Already Arrange
println(A) # Original Array
println(BinarySearch(A,43)) # BinarySearch Search Array
println(BinarySearch(A,354)) # BinarySearch Search Array
println(BinarySearch(A,3)) # BinarySearch Search Array

```

历史
--

在1946年，约翰·莫奇利在摩尔学院讲座（英语：）上第一次提出二分搜索的概念。1957年，威廉·皮特逊（英语：）发表了第一个应用插值搜索的算法。在此时，每个发表的二分搜索算法只对长度为2的幂减一的数组有用。直到1960年，德里克·亨利·莱默发表了一个对于所有长度的数组都适用的算法。1962年，赫尔曼·博滕布鲁赫发表了一个用ALGOL 60写的二分搜索，将判断相等的步骤放到算法末尾。虽然将平均迭代次数增加一，但是每次迭代中的比较次数减少了1次。均匀二分搜索则是史丹佛大學的A. K.钱德拉在1971年发明的。1986年，伯纳德·查泽尔和列奥尼达斯·吉巴斯引入了分散层叠来解决计算几何中大量存在的搜索问题。

实现中的问题
------

> 
> 尽管二分查找的基本思想相对简单，但细节可以令人難以招架 ... — 高德纳
> 
> 
> 

当乔恩·本特利将二分搜索问题布置给专业编程课的学生时，百分之90的学生在花费数小时后还是无法给出正确的解答，主要因为这些错误程序在面对边界值的时候无法运行，或返回错误结果。1988年开展的一项研究显示，20本教科书里只有5本正确实现了二分搜索。不仅如此，本特利自己1986年出版的《编程珠玑》一书中的二分搜索算法存在整数溢出的问题，二十多年来无人发现。Java语言的库所实现的二分搜索算法中同样的溢出问题存在了九年多才被修复。

外部链接
----

* NIST Dictionary of Algorithms and Data Structures: binary search（页面存档备份，存于）
* Google Research: Nearly All Binary Searches and Mergesorts are Broken（页面存档备份，存于）.
* Binary search implemented in 12 languages（页面存档备份，存于）.
* 程序员编程艺术第二十五章：Jon Bentley：90%无法正确实现二分查找（页面存档备份，存于）
* 


|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
算法 |
| --- |
|  |
| 排序 | 

|  |  |
| --- | --- |
| 比较排序 | 
* 冒泡排序
* 选择排序
* 插入排序
* 希尔排序
* 快速排序
* 归并排序
* 堆排序
* 鸡尾酒排序
* 梳排序
* 侏儒排序
* 图书馆排序
* 内省排序
* 奇偶排序

 |
|  |
| 线性时间排序 | 
* 鸽巢排序
* 基数排序
* 計數排序
* 桶排序

 |
|  |
| 并行排序 | 
* 排序网络（英语：）
* Batcher归并网络

 |
|  |
| 不实用的 | 
* Bogo排序
* 臭皮匠排序

 |
|  |
| 图 | 
* 拓撲排序

 |

 |
|  |
| 搜索 | 

|  |  |
| --- | --- |
| 列表 | 
* 线性搜索
* 二分搜索
* 插值搜尋

 |
|  |
| 树・图 | 
* 广度优先搜索
	+ 最良優先搜索（英语：）
	+ 均一开销搜索
	+ A\*
* 深度优先搜索
	+ 迭代深化深度优先搜索
	+ 深度限制搜索（日语：）
	+ 双向搜索
* 分枝限定法（英语：）

 |
|  |
| 字符串 | 
* KMP算法
* 博耶-穆尔字符串搜索算法
* AC自动机算法
* 拉宾-卡普算法
* bitap算法

 |

 |
|  |
| 最短路问题 | 
* 戴克斯特拉算法
* 贝尔曼-福特算法
* A\*搜尋演算法
* Floyd-Warshall算法

 |
|  |
| 最小生成树 | 
* 普林姆算法
* 克鲁斯克尔演算法

 |
|  |
| 最大流最小割 | 
* 福特-富尔克森算法
* 埃德蒙兹-卡普算法
* 迪尼茨算法

 |
|  |
| 线性规划 | 
* 单纯形法
* 卡马卡尔算法（英语：）

 |
|  |
| 順序統計量 | 
* 选择算法
* 中位数的中位数（英语：）

 |
|  |
| 種類 | 
* 精确算法
* 近似算法
* 随机化算法

 |
|  |
| 其他 | 
* 分治法
* 动态规划
* 贪心算法

 |
|  |
| **Category:算法** |

 |