【数据结构与算法】二分查找法
在开始将二分查找法之间先看一个需求,在一个有序数组中,查找一个目标值,如果该值存在,则返回对应的索引值,否则返回-1
(代表没找到)。
比如数组是[1,2,3,5,7,9]
,查找目标值3
所在的索引,这个问题很简单,我们只需要遍历数组判断即可。
public class Solution {
public int search(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
if (nums[i] == target) {
return i;
}
}
return -1;
}
public static void main(String[] args) {
System.out.println(new Solution().search(new int[]{1, 2, 3, 5, 7, 9}, 3)); // 2
System.out.println(new Solution().search(new int[]{1, 2, 3, 5, 7, 9}, 0)); // -1
}
}
上面这种算法可以解决问题,但是它并不优秀,最好的情况下,可能第一个元素就是目标值,时间复杂度是O(1)
,最坏的情况下,可能数组最末尾一个才是目标值,那么就要比较n
次,所以最坏的时间复杂度是O(n)
。
有没有更优秀的算法呢?答案是有的,比如二分查找法就是其中一种,二分查找法,也叫做二分搜索法,当数组是有序数组的时候,使用二分查找法的时间复杂度是O(logN)
。
有序数组是说一个数组中的元素是有序的(升序或者降序),比如
[1, 2, 3, 4, 5, 10]
,或者[10, 9, 4, 2, 1]
,特别要说明的是像[10, 10, 9, 8, 4]
这样的有重复但是整体有序的数组也是符合条件的!!!
二分查找的精髓体现在二分这两个字上,意思是对数组每次都分成两部分,我以如下数组为例:
如图所示数组被分为左右两部分,被中间值所分开,首先用中间值与目标值进行比较,如果中间值等于目标值,那么说明找到了,如果中间值大于目标值,说明目标值应该是在左半部分
,需要向左侧查找,否则向右查找。
那么如何确定左半部分、右半部分、以及中间值呢,肯定得用指针,二分查找法中我们首先要定义两个指针,一个做指针left
,一个右指针right
,初始的时候left = 0
,right = N - 1
,mid = (left + right) / 2
, 如下图所示:
情况1:
如果target = 7
说明正好就找到了,那么mid = 4
这个索引就是查找结果。
情况2:
目标值等于3
的时候。
通过上面的分析,可以得出二分查找法的算法步骤:
- 设置两个指针
left
和right
,初始值left = 0, right = N - 1
。 - 计算中间索引,计算公式为
mid = (left + right) / 2
。 - 如果target = nums[mid],找到了。
- 如果target > nums[mid],则将左指针移动到mid后面的位置,也就是
left = mid + 1
,回到第2步,继续计算。 - 如果target < nums[mid],则将右指针移动到mid前面的位置,也就是
right = mid - 1
,回到第2步,继续计算。 - 如果
left > right
的时候,则停止计算。
代码如下:
class Solution {
public int binarySearch(int[] nums, int target) {
int N = nums.length;
int left = 0;
int right = N - 1;
while (left <= right) {
int mid = (left + right) >>> 1;
if (target > nums[mid]) {
left = mid + 1;
} else if (target < nums[mid]) {
right = mid - 1;
} else {
return mid;
}
}
return -1;
}
public static void main(String[] args) {
int[] nums = new int[] { 1, 2, 3, 5, 7, 9, 20, 30, 88 };
System.out.println(new Solution().binarySearch(nums, 3)); // 2
System.out.println(new Solution().binarySearch(nums, 0)); // -1
System.out.println(new Solution().binarySearch(nums, 100)); // -1
int[] nums2 = new int[] { 1, 2, 2, 2, 3, 5, 7, 9, 20, 30, 88 };
System.out.println(new Solution().binarySearch(nums2, 2)); // 2
}
}