|
| 1 | +# 二分查找 |
| 2 | + |
| 3 | +二分查找是一种非常高效的查找算法,高效到什么程度呢?我们来分析一下它的时间复杂度。 |
| 4 | + |
| 5 | +我们假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。被查找区间的大小变化: |
| 6 | + |
| 7 | +``` |
| 8 | +n, n/2, n/4, n/8, ..., n/(2^k) |
| 9 | +``` |
| 10 | + |
| 11 | +可以看出来,这是一个等比数列。其中 n/(2^k)=1 时,k 的值就是总共缩小的次数。而每一次缩小操作只涉及两个数据的大小比较,所以,经过了 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/(2^k)=1,我们可以求得 k=log2n,所以时间复杂度就是 O(logn)。 |
| 12 | + |
| 13 | +## 代码示例 |
| 14 | + |
| 15 | +注意容易出错的 3 个地方。 |
| 16 | + |
| 17 | +1. 循环退出条件是 `low <= high`,而不是 `low < high`; |
| 18 | +1. mid 的取值,可以是 `mid = (low + high) / 2`,但是如果 low 和 high 比较大的话,`low + high` 可能会溢出,所以这里写为 `mid = low + ((high - low) >> 1)`; |
| 19 | +1. low 和 high 的更新分别为 `low = mid + 1`、`high = mid - 1`。 |
| 20 | + |
| 21 | +<!-- tabs:start --> |
| 22 | + |
| 23 | +### **Java** |
| 24 | + |
| 25 | +非递归实现: |
| 26 | + |
| 27 | +```java |
| 28 | +public class BinarySearch { |
| 29 | + |
| 30 | + private static int search(int[] nums, int low, int high, int val) { |
| 31 | + while (low <= high) { |
| 32 | + int mid = low + ((high -low) >> 1); |
| 33 | + if (nums[mid] == val) { |
| 34 | + return mid; |
| 35 | + } else if (nums[mid] < val) { |
| 36 | + low = mid + 1; |
| 37 | + } else { |
| 38 | + high = mid - 1; |
| 39 | + } |
| 40 | + } |
| 41 | + return -1; |
| 42 | + } |
| 43 | + |
| 44 | + /** |
| 45 | + * 二分查找(非递归) |
| 46 | + * |
| 47 | + * @param nums 有序数组 |
| 48 | + * @param val 要查找的值 |
| 49 | + * @return 要查找的值在数组中的索引位置 |
| 50 | + */ |
| 51 | + private static int search(int[] nums, int val) { |
| 52 | + return search(nums, 0, nums.length - 1, val); |
| 53 | + } |
| 54 | + |
| 55 | + public static void main(String[] args) { |
| 56 | + int[] nums = {1, 2, 5, 7, 8, 9}; |
| 57 | + |
| 58 | + // 非递归查找 |
| 59 | + int r1 = search(nums, 7); |
| 60 | + System.out.println(r1); |
| 61 | + |
| 62 | + // 递归查找 |
| 63 | + int r2 = search(nums, 7); |
| 64 | + System.out.println(r2); |
| 65 | + } |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +递归实现: |
| 70 | + |
| 71 | +```java |
| 72 | +public class BinarySearch { |
| 73 | + |
| 74 | + private static int searchRecursive(int[] nums, int low, int high, int val) { |
| 75 | + while (low <= high) { |
| 76 | + int mid = low + ((high - low) >> 1); |
| 77 | + if (nums[mid] == val) { |
| 78 | + return mid; |
| 79 | + } else if (nums[mid] < val) { |
| 80 | + return searchRecursive(nums, mid + 1, high, val); |
| 81 | + } else { |
| 82 | + return searchRecursive(nums, low, mid - 1, val); |
| 83 | + } |
| 84 | + } |
| 85 | + return -1; |
| 86 | + } |
| 87 | + |
| 88 | + /** |
| 89 | + * 二分查找(递归) |
| 90 | + * |
| 91 | + * @param nums 有序数组 |
| 92 | + * @param val 要查找的值 |
| 93 | + * @return 要查找的值在数组中的索引位置 |
| 94 | + */ |
| 95 | + private static int searchRecursive(int[] nums, int val) { |
| 96 | + return searchRecursive(nums, 0, nums.length - 1, val); |
| 97 | + } |
| 98 | + |
| 99 | + public static void main(String[] args) { |
| 100 | + int[] nums = {1, 2, 5, 7, 8, 9}; |
| 101 | + |
| 102 | + // 非递归查找 |
| 103 | + int r1 = search(nums, 7); |
| 104 | + System.out.println(r1); |
| 105 | + |
| 106 | + // 递归查找 |
| 107 | + int r2 = search(nums, 7); |
| 108 | + System.out.println(r2); |
| 109 | + } |
| 110 | +} |
| 111 | +``` |
| 112 | + |
| 113 | +<!-- tabs:end --> |
0 commit comments