二分查找

128 阅读1分钟

二分查找实际上是一棵二叉递归树,每次进行一半的剪枝,是不是就明白了?原来二分查找对应的数据结构是二叉树,那么二分查找的时间复杂度就是树高 log(n)log(n)

原理:把原序列划分成元素个数尽量相同的两个子序列,然后递归查找(但一般写成非递归的 )。

二分查找适用于有序序列。

方法一

最经典:查找的是全部 target 中的其中一个

bool binarySearch(vector<int>& arr, int target){
    if(arr.size() == 0) return false;
    int left = 0, right = arr.size() - 1;
    while(left <= right){
        int mid = left + (right - left) / 2;  
        // mid = (left + right) / 2 会越界!
        if(arr[mid] > target) right = mid - 1;
        else if(arr[mid] < target) left = mid + 1;
        else{
            return true;
        }
    }
    // left > right 结束 while
    return false;

查找等于 target 的第一个元素下标,即查找 target 左边界

int binarySearch(vector<int>& nums, int target){
    if(nums.size() == 0)
        return -1;

    int left = 0, right = nums.size() - 1;
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] < target) { 
            left = mid + 1; 
        } else { 
            right = mid - 1; 
        }
    }

    if (left != nums.size() && nums[left] == target) return left;
    return -1;
}

查找等于 target 的最后一个元素下标,即查找 target 右边界


int binarySearch(vector<int>& nums, int target){
    if(nums.size() == 0)
        return -1;

    int left = 0, right = nums.size() - 1;
    while(left <= right) {
        int mid = left + (right - left) / 2;
        if(nums[mid] <= target) { 
            left = mid + 1; 
        } else { 
            right = mid - 1; 
        }
    }
    
    if (right != -1 && nums[right] == target) return right;
    return -1;

方法二

还有另一种更简单的方法求左边界和右边界:

查找左边界

// q[] 为有序数组,x 为要查询的数
int bsearch_1(int l, int r)
{
    while (l < r)
    {
        int mid = l + r >> 1;
        if (q[mid] >= x) r = mid;    
        else l = mid + 1;
    }
    return l;
}

查找右边界

int bsearch_2(int l, int r)
{
    while (l < r)
    {
        int mid = l + r + 1 >> 1;  // 注意 +1,否则会进入死循环
        if (q[mid] <= x) l = mid;
        else r = mid - 1;
    }
    return l;
}

示例

611. 有效三角形的个数 - 力扣(LeetCode)

相当于在 j + 1 到 n 中找第三条边的右端点。如果找不到,此时 left = n + 1,所以二分之后给了一个判断。

class Solution {
public:
    int triangleNumber(vector<int>& nums) {
        int n = nums.size();
        sort(nums.begin(), nums.end());
        
        int res = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                int left = j + 1, right = n - 1;
                while (left < right) {
                    int mid = (left + right + 1) >> 1;
                    if (nums[mid] < nums[i] + nums[j]) left = mid;
                    else right = mid - 1;
                }
                if (left != n && nums[i] + nums[j] > nums[left])  res += left - j;  // left <= n
            }
        }
        return res;
    }
};