一种针对有序数据集合的查找方法:二分查找(Binary Search),折半查找。
二分思想
二分查找是一种非常简单易懂的快速查找算法。
猜测0-99中的目标数字,target = 23 。如果是 0 到 999 的数字,最多也只要 10 次就能猜中。
对有序表折半查找,其最坏情况下查找一个元素的最大比较次数将介于1和 [ log2n ] + 1 ( n为元素的个数)之间。
假设有 10 个订单,订单金额分别是:8,11,19,23,27,33,45,55,67,98。利用二分思想,每次都与区间的中间数据比对大小,缩小查找区间的范围。如图,其中 low 和 high 表示待查找区间的下标,mid 表示待查找区间的中间元素下标。
🧨二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想 —— 每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为 0。
O(logn) 查找速度 —— 时间复杂度
假设数据大小是 n,每次查找后数据都会缩小为原来的一半,也就是会除以 2。最坏情况下,直到查找区间被缩小为空,才停止。
可以看出来,上述是一个等比数列。其中 n/(2^k)=1 时,k 的值就是总共缩小的次数,每一次区间缩小的操作只涉及两个数据的大小比较,所以经过 k 次区间缩小操作,时间复杂度就是 O(k)。通过 n/(2^k)=1,可以求得 k=log2n,即时间复杂度为 O(logn)。
二分查找的时间复杂度为 O(logn) —— 堆、二叉树等。
O(logn) :对数时间复杂度,是一种极其高效的时间复杂度,有时甚至比时间复杂度是常量级 O(1) 的算法还要高效。
用大 O 标记法表示时间复杂度的时候,会省略掉常数、系数和低阶。对于常量级时间复杂度的算法来说,O(1) 有可能表示的是一个非常大的常量值,比如 O(1000)、O(10000)。所以常量级时间复杂度的算法有时候可能还没有 O(logn) 的算法执行效率高。
指数时间复杂度的算法在大规模数据面前是无效的。
二分查找的递归与非递归实现
如何来写 简单的二分查找 。最简单的情况就是有序数组中不存在重复元素,在其中用二分查找值等于给定值的数据。
// 非递归解法:循环实现
class Solution {
public int search(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while(left <= right){
// 防止数据溢出
int mid = left +