簡介

二元搜索法 (Binary Search) 又稱折半搜索,搜索演算法的一種,可使用 Divide and Conquer 或直接使用迴圈 (迭代) 來實作,搜索的目標資料必須是已經排序過的 (以小到大排序為例)。其概念是每次挑選中間位置的資料來比對,若該資料小於目標值,則縮小範圍為左半部,反之亦然;因此使用這個方法每次比對後都可以濾掉一半的資料,以增快搜索速度。過程依以下步驟進行:

  1. 取出搜索範圍中點的元素。
  2. 與搜索目標比較,若相等則回傳位址。若小於則將左邊邊界改為中點右側,若大於則將右邊邊界改為中點左側。
  3. 左邊邊界不超過右邊邊界(還有資料)則重複以上動作,若已無資料則回傳-1(找不到)。

流程範例如圖所示,例如搜索值為4的元素:
二元搜索法

分析

  • 最佳時間複雜度:O(1)
  • 平均時間複雜度:O(log n)
  • 最差時間複雜度:O(log n)
  • 空間複雜度:O(1)

虛擬碼

迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function search(list, target)
var left = 0, right = list.length - 1
while left <= right
var middle = 取出 list 中點
if list[middle] == target
return middle
// 比目標大,再搜索左半邊
else if list[middle] > target
right = middle - 1
// 比目標小,再搜索右半邊
else if list[middle] < target
left = middle + 1
end if
end while
return -1
end function

Divide and Conquer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function search(list, target, left, right)
if left > right
return -1
end
var middle = 取出 list 中點
if list[middle] == target
return middle
// 比目標大,再搜索左半邊
else if list[middle] > target
return search(list, target, left, middle - 1)
// 比目標小,再搜索右半邊
else if list[middle] < target
return search(list, target, middle + 1, right)
end if
return
end function

語法

C#

迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BinarySearch
{
class Iterative
{
static public int Search(int[] array, int num)
{
int left = 0, right = array.Length - 1;
while (left <= right)
{
int middle = (right + left) / 2;

if (array[middle] == num)
return middle;

if (array[middle] > num)
right = middle - 1;
else
left = middle + 1;
}
return -1;
}
}
}

Divide and Conquer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BinarySearch
{
class DivideAndConquer
{
static public int Search(int[] array, int num)
{
return Search(array, num, 0, array.Length - 1);
}

static public int Search(int[] array, int num, int left, int right)
{
if (left > right)
return -1;

int middle = (right + left) / 2;

if (array[middle] == num)
return middle;

if (array[middle] > num)
return Search(array, num, left, middle - 1);

return Search(array, num, middle + 1, right);
}
}
}

Java

迭代

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Iterative {
static public int Search(int[] array, int num)
{
int left = 0, right = array.length - 1;
while (left <= right)
{
int middle = (right + left) / 2;

if (array[middle] == num)
return middle;

if (array[middle] > num)
right = middle - 1;
else
left = middle + 1;
}
return -1;
}
}

Divide and Conquer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DivideAndConquer {
static public int Search(int[] array, int num)
{
return Search(array, num, 0, array.length - 1);
}

static public int Search(int[] array, int num, int left, int right)
{
if (left > right)
return -1;

int middle = (right + left) / 2;

if (array[middle] == num)
return middle;

if (array[middle] > num)
return Search(array, num, left, middle - 1);

return Search(array, num, middle + 1, right);
}
}

C++

迭代

Iterative.h

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef ITERATIVE_H
#define ITERATIVE_H

class Iterative
{
public:
static int Search(int* array, int length, int num);
protected:
private:
};

#endif // ITERATIVE_H

Iterative.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "Iterative.h"

int Iterative::Search(int* array, int length, int num)
{
int left = 0, right = length - 1;
while (left <= right)
{
int middle = (right + left) / 2;

if (array[middle] == num)
return middle;

if (array[middle] > num)
right = middle - 1;
else
left = middle + 1;
}
return -1;
}

Divide and Conquer

DivideAndConquer.h

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef DIVIDEANDCONQUER_H
#define DIVIDEANDCONQUER_H

class DivideAndConquer
{
public:
static int Search(int* array, int length, int num);
static int Search(int* array, int num, int left, int right);
protected:
private:
};

#endif // DIVIDEANDCONQUER_H

DivideAndConquer.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include "DivideAndConquer.h"

int DivideAndConquer::Search(int* array, int length, int num)
{
return Search(array, num, 0, length - 1);
}

int DivideAndConquer::Search(int* array, int num, int left, int right)
{
if (left > right)
return -1;

int middle = (right + left) / 2;

if (array[middle] == num)
return middle;

if (array[middle] > num)
return Search(array, num, left, middle - 1);

return Search(array, num, middle + 1, right);
}

產生1-100000之陣列,並搜索1-100000實測

1
2
3
4
5
6
7
8
9
10
11
C#
Iterative: 30ms - 40ms
DivideAndConquer: 60ms - 70ms

Java
Iterative: 10ms - 20ms
DivideAndConquer: 20ms - 30ms

C++
Iterative: 10ms - 30ms
DivideAndConquer: 20ms - 30ms

延伸閱讀

分治法 (Divide and conquer)