二分查找(Binary Search)
- 给定包含 n 个元素的已排序数组 sorted_array[],求给定元素 x 的位置。
- 给定包含 n 个元素的已排序数组 sorted_array[],求小于等于给定元素 x 的最近位置(Floor Value)。
- 给定包含 n 个元素的已排序数组 sorted_array[],求大于等于给定元素 x 的最近位置(Ceiling Value)。
- 给定包含 n 个元素的已排序数组 sorted_array[],其中可能包含若干重复的元素,求给定元素 x 重复的次数。
- 给定包含 n 个元素的已排序数组 sorted_array[],但数组被从中间某未知点翻转为 A[],求 A[] 数组中的最小元素。
- 给定包含 n 个元素的已排序数组 sorted_array[],查找其中元素位置等于元素值的位置(Fixed Point)。
- 给定包含 n 个元素的数组 array[],查找某高点的值大于左右两侧的值的位置(Peak Position)。
给定包含 n 个元素的已排序数组 sorted_array[],求给定元素 x 的位置。
最简单直接的办法就是线性查找(Linear Search),从数组的最左端开始,逐个值与 x 进行比较,如果匹配则返回元素位置,如果不匹配则右移一位继续比较,如果比较到末尾仍为找到则返回 -1。由此可知,线性查找的时间复杂度为 O(n)。
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
6
7 int index = LinearSearch(sorted_array, 6);
8 Console.WriteLine(index);
9
10 Console.ReadKey();
11 }
12
13 static int LinearSearch(int[] sorted_array, int x)
14 {
15 for (int i = 0; i < sorted_array.Length; i++)
16 {
17 if (sorted_array[i] == x)
18 {
19 return i;
20 }
21 }
22
23 return -1;
24 }
25 }
二分查找(Binary Search)算法使用了分治法(Divide and Conquer)来不断缩小查找范围,并充分利用已知的信息将查找时间复杂度降低到 O(logn)。
那已知信息就是:数组是已排序的。
这样通过如下步骤可以减少比较次数:
- 将 x 与数组的中间的值进行比较;
- 如果 x 与中间的值相等,则直接返回中间值的位置;
- 如果 x 较小,则若存在必出现在中间值的左侧;
- 如果 x 较大,则若存在必出现在中间值的右侧;
可以通过递归(Recursive)和迭代(Iterative)两种方式来实现。
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
4
5 int index = BinarySearchByRecursive(
6 sorted_array, 0, sorted_array.Length, 6);
7 Console.WriteLine(index);
8
9 Console.ReadKey();
10 }
11
12 static int BinarySearchByRecursive(
13 int[] sorted_array,
14 int left,
15 int right,
16 int x)
17 {
18 if (left <= right)
19 {
20 int middle = left + (right - left) / 2;
21
22 if (x == sorted_array[middle])
23 return middle;
24
25 if (x < sorted_array[middle])
26 return BinarySearchByRecursive(
27 sorted_array, left, middle - 1, x);
28
29 return BinarySearchByRecursive(
30 sorted_array, middle + 1, right, x);
31 }
32
33 return -1;
34 }
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
4
5 int index = BinarySearchByIterative(
6 sorted_array, 0, sorted_array.Length, 6);
7 Console.WriteLine(index);
8
9 Console.ReadKey();
10 }
11
12 static int BinarySearchByIterative(
13 int[] sorted_array,
14 int left,
15 int right,
16 int x)
17 {
18 while (left <= right)
19 {
20 int middle = left + (right - left) / 2;
21
22 if (x == sorted_array[middle])
23 return middle;
24
25 if (x < sorted_array[middle])
26 right = middle - 1;
27 else
28 left = middle + 1;
29 }
30
31 return -1;
32 }
从上面的代码可知,在最坏情况下需要 logn + 1 次比较操作。每个迭代周期内进行了 2 次比较,除非成功匹配了元素。现在我们来尝试尽量减少比较操作的数量,在 while 循环内仅进行一次比较。
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
4
5 for (int i = 0; i < sorted_array.Length; i++)
6 {
7 int index = BinarySearchByIterativeWithLessComparison(
8 sorted_array, 0, sorted_array.Length, i);
9 Console.WriteLine(index);
10 }
11
12 Console.ReadKey();
13 }
14
15 static int BinarySearchByIterativeWithLessComparison(
16 int[] sorted_array,
17 int left,
18 int right,
19 int x)
20 {
21 int middle;
22
23 while (right - left > 1)
24 {
25 middle = left + (right - left) / 2;
26
27 if (sorted_array[middle] <= x)
28 left = middle;
29 else
30 right = middle;
31 }
32
33 if (sorted_array[left] == x)
34 return left;
35 else
36 return -1;
37 }
现在我们通过使用二分查找(Binary Search)来解决一些常见问题。
给定包含 n 个元素的已排序数组 sorted_array[],求小于等于给定元素 x 的最近位置(Floor Value)。
例如:sorted_array[] = [2, 3, 4, 6, 7, 8, 10, 12],x = 9,则 FloorValue = 8。
这里需要考虑几个边界条件:
- 如果数组内的所有元素都小于 x,则最后一个元素即为 FloorValue。
- 如果数组内的所有元素都大于 x,则实际上不存在 FloorValue,属于异常情况,返回 -1。
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[8] { 2, 2, 4, 6, 7, 8, 10, 12 };
4
5 int index = -1;
6
7 for (int i = 0; i < sorted_array.Length; i++)
8 {
9 index = Floor(sorted_array, sorted_array[i]);
10 Console.WriteLine(index);
11 }
12
13 index = Floor(sorted_array, 1);
14 Console.WriteLine(index);
15
16 index = Floor(sorted_array, 5);
17 Console.WriteLine(index);
18
19 index = Floor(sorted_array, 9);
20 Console.WriteLine(index);
21
22 index = Floor(sorted_array, 13);
23 Console.WriteLine(index);
24
25 Console.ReadLine();
26 }
27
28 static int Floor(
29 int[] sorted_array,
30 int x)
31 {
32 if (x < sorted_array[0])
33 return -1;
34
35 return BinarySearchFloorPosition(
36 sorted_array, 0, sorted_array.Length, x);
37 }
38
39 static int BinarySearchFloorPosition(
40 int[] sorted_array,
41 int left,
42 int right,
43 int x)
44 {
45 int middle;
46
47 while (right - left > 1)
48 {
49 middle = left + (right - left) / 2;
50
51 if (sorted_array[middle] <= x)
52 left = middle;
53 else
54 right = middle;
55 }
56
57 return left;
58 }
给定包含 n 个元素的已排序数组 sorted_array[],求大于等于给定元素 x 的最近位置(Ceiling Value)。
例如:sorted_array[] = [2, 3, 4, 6, 7, 8, 10, 12],x = 9,则 CeilingValue = 10。
这里需要考虑几个边界条件:
- 如果数组内的所有元素都大于 x,则第一个元素即为 CeilingValue。
- 如果数组内的所有元素都小于 x,则实际上不存在 CeilingValue,属于异常情况,返回 -1。
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[8] { 2, 2, 4, 6, 7, 7, 10, 12 };
4
5 int index = -1;
6
7 for (int i = 0; i < sorted_array.Length; i++)
8 {
9 index = Ceiling(sorted_array, sorted_array[i]);
10 Console.WriteLine(index);
11 }
12
13 index = Ceiling(sorted_array, 1);
14 Console.WriteLine(index);
15
16 index = Ceiling(sorted_array, 5);
17 Console.WriteLine(index);
18
19 index = Ceiling(sorted_array, 9);
20 Console.WriteLine(index);
21
22 index = Ceiling(sorted_array, 13);
23 Console.WriteLine(index);
24
25 Console.ReadLine();
26 }
27
28 static int Ceiling(
29 int[] sorted_array,
30 int x)
31 {
32 if (x > sorted_array[sorted_array.Length - 1])
33 return -1;
34 if (x <= sorted_array[0])
35 return 0;
36
37 return BinarySearchCeilingPosition(
38 sorted_array, 0, sorted_array.Length, x);
39 }
40
41 static int BinarySearchCeilingPosition(
42 int[] sorted_array,
43 int left,
44 int right,
45 int x)
46 {
47 int middle;
48
49 while (right - left > 1)
50 {
51 middle = left + (right - left) / 2;
52
53 if (sorted_array[middle] < x)
54 left = middle;
55 else
56 right = middle;
57 }
58
59 return right;
60 }
给定包含 n 个元素的已排序数组 sorted_array[],其中可能包含若干重复的元素,求给定元素 x 重复的次数。
由于是已排序数组,则相同的元素肯定是连续的。这样可以通过查找 x 的最左侧出现和 x 的最右侧出现,则中间的部分都是 x,出现次数 = right - left + 1。
例如:例如:sorted_array[] = [-1, 2, 3, 8, 8, 8, 8, 10],x = 8,则重复的位置为 [8, 8, 8, 8],则重复次数为 6 - 3 + 1 = 4 次。
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[8] { -1, 2, 3, 8, 8, 8, 8, 10 };
4
5 int count = CountOccurrences(sorted_array, 8);
6 Console.WriteLine(count);
7
8 Console.ReadKey();
9 }
10
11 static int GetLeftPosition(
12 int[] sorted_array,
13 int left,
14 int right,
15 int x)
16 {
17 int middle;
18
19 while (right - left > 1)
20 {
21 middle = left + (right - left) / 2;
22
23 if (sorted_array[middle] >= x)
24 right = middle;
25 else
26 left = middle;
27 }
28
29 return right;
30 }
31
32 static int GetRightPosition(
33 int[] sorted_array,
34 int left,
35 int right,
36 int x)
37 {
38 int middle;
39
40 while (right - left > 1)
41 {
42 middle = left + (right - left) / 2;
43
44 if (sorted_array[middle] <= x)
45 left = middle;
46 else
47 right = middle;
48 }
49
50 return left;
51 }
52
53 static int CountOccurrences(
54 int[] sorted_array,
55 int x)
56 {
57 int left = GetLeftPosition(sorted_array, -1, sorted_array.Length - 1, x);
58 int right = GetRightPosition(sorted_array, 0, sorted_array.Length, x);
59
60 return (sorted_array[left] == x && x == sorted_array[right]) ?
61 (right - left + 1) : 0;
62 }
给定包含 n 个元素的已排序数组 sorted_array[],但数组被从中间某未知点翻转为 A[],求 A[] 数组中的最小元素。
实际上数组中的最小元素 x 将数组分成了左右两侧,左侧的大于 x,右侧的也大于 x。
1 static void Main(string[] args)
2 {
3 int[] A = new int[8] { 6, 7, 8, 9, 10, 2, 3, 4 };
4
5 int minimum = BinarySearchIndexOfMinimumRotatedArray(
6 A, 0, A.Length - 1);
7 Console.WriteLine(minimum);
8
9 Console.ReadKey();
10 }
11
12 static int BinarySearchIndexOfMinimumRotatedArray(
13 int[] A,
14 int left,
15 int right)
16 {
17 int middle;
18
19 if (A[left] <= A[right])
20 return left;
21
22 while (left <= right)
23 {
24 if (left == right)
25 return left;
26
27 middle = left + (right - left) / 2;
28
29 if (A[middle] < A[right])
30 right = middle;
31 else
32 left = middle + 1;
33 }
34
35 return -1;
36 }
给定包含 n 个元素的已排序数组 sorted_array[],查找其中元素位置等于元素值的位置(Fixed Point)。
例如:sorted_array[] = { -10, -1, 0, 3, 10, 11, 30, 50 },则 Fixed Point = 3。
1 static void Main(string[] args)
2 {
3 int[] sorted_array = new int[8] { -10, -1, 0, 3, 10, 11, 30, 50 };
4
5 int index = -1;
6
7 index = BinarySearchFixedPosition(
8 sorted_array, 0, sorted_array.Length - 1);
9 Console.WriteLine(index);
10
11 Console.ReadLine();
12 }
13
14 static int BinarySearchFixedPosition(
15 int[] array,
16 int left,
17 int right)
18 {
19 if (right >= left)
20 {
21 int middle = (left + right) / 2;
22
23 if (middle == array[middle])
24 return middle;
25 else if (middle > array[middle])
26 return BinarySearchFixedPosition(array, (middle + 1), right);
27 else
28 return BinarySearchFixedPosition(array, left, (middle - 1));
29 }
30
31 return -1;
32 }
给定包含 n 个元素的数组 array[],查找某高点的值大于左右两侧的值的位置(Peak Position)。
例如:array[] = { 1, 3, 20, 4, 1 },则 Peak Value = 20,Peak Position = 2。
1 static void Main(string[] args)
2 {
3 int[] array = new int[5] { 1, 3, 20, 4, 1 };
4
5 int index = -1;
6
7 index = FindPeakPosition(array);
8 Console.WriteLine(index);
9
10 Console.ReadLine();
11 }
12
13 static int FindPeakPosition(int[] array)
14 {
15 return BinarySearchPeakPosition(
16 array, 0, array.Length - 1);
17 }
18
19 static int BinarySearchPeakPosition(
20 int[] array,
21 int left,
22 int right)
23 {
24 int middle = left + (right - left) / 2;
25
26 // compare middle element with its neighbors (if neighbors exist)
27 if ((middle == 0 || array[middle - 1] <= array[middle])
28 && (middle == array.Length - 1
29 || array[middle + 1] <= array[middle]))
30 return middle;
31
32 // if middle element is not peak and its left neighbor is greater than it
33 // then left half must have a peak element
34 else if (middle > 0 && array[middle - 1] > array[middle])
35 return BinarySearchPeakPosition(array, left, (middle - 1));
36
37 // if middle element is not peak and its right neighbor is greater than it
38 // then right half must have a peak element
39 else
40 return BinarySearchPeakPosition(array, (middle + 1), right);
41 }

浙公网安备 33010602011771号