二分算法:致敬经典,超越经典

二分算法的基础知识

二分查找算法是二分算法的一个子类
二分查找是对顺序查找的一种优化

  • 在一个有序查找区间内
  • min是头指针,max是尾指针,mid = (min + max) / 2
  • 如果arr[mid] < x min =mid + 1
  • 如果arr[mid] > x max =mid - 1
  • 如果arr[mid] = x 找到结果
  • 终止条件:min >= max

二分算法 二分的就是查找区间的范围 n->n/2, 二分的是问题的规模

int binary_search(int *arr, int n, int x){
	int head = 0, tail = n - 1, mid;
	while(head <= tail){
		mid = head + ((tail - head) >> 1);
		if(arr[mid] == x) return mid;
		if(arr[mid] < x) head = mid + 1;
		else tail = mid - 1;
	}
	return -1;
}

二分查找 - 泛型情况
0 - 1 查找模型 0: 条件不成立, 1:条件成立
前面一堆0 ,后面一堆1,找到第一个1

int binary_search_01(int *arr, int n, int x){
	int head = 0, tail = n - 1, mid;
	while(head < tail){
		mid = head + ((tail - head) >> 1);
		if(arr[mid] < x) head = mid + 1;
		else tail = mid;
	}
	return head;
}

经典面试题 - 简单二分应用

69. x 的平方根

  • x越大,根号x就越大
  • 因此是个单调区间
class Solution {
public:
    int mySqrt(int x) {
        double head = 0, tail = x, mid;
        tail += 1;
        for(int i = 0; i < 100; i++){
            mid = head + ((tail - head) / 2.0);
            if(mid * mid <= x) head = mid;
            else tail = mid;
        }
        return floor(head);
    }
};

35. 搜索插入位置

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int head = 0, tail = nums.size() - 1, mid;
        while(tail - head > 3){
            mid = (head + tail) >> 1;
            if(nums[mid] >= target) tail = mid;
            else head = mid + 1;
        }
        for(int i = head; i <= tail; i++){
            if(nums[i] >= target) return i;
        }
        return nums.size();
    }
};

1. 两数之和

  • 套娃: 下标数组,通过下标数组对数组进行排序
  • 选中某个值val,二分查找剩下一个值为 target - val
class Solution {
public:
    int binary_search(vector<int> &nums, vector<int> &ind, int head, int x){
        int tail = ind.size() - 1, mid;
        while(head <= tail){
            mid = (head + tail) >> 1;
            if(nums[ind[mid]] == x) return mid;
            if(nums[ind[mid]] < x) head = mid + 1;
            else tail = mid - 1;
        }
        return -1;
    }
    vector<int> twoSum(vector<int>& nums, int target) {
        vector<int> ind(nums.size());
        for(int i = 0; i < ind.size(); i++) ind[i] = i;
        sort(ind.begin(), ind.end(), [&](int i, int j){ return nums[i] < nums[j];});
        vector<int> ret(2);
        for(int i = 0; i < ind.size(); i++){
            int val = nums[ind[i]];
            int j = binary_search(nums, ind, i + 1, target - val);
            if(j == -1) continue;
            int a = ind[i];
            int b = ind[j];
            if(a > b) swap(a, b);
            ret[0] = a;
            ret[1] = b;
        }
        return ret;
    }
};

34. 在排序数组中查找元素的第一个和最后一个位置

class Solution {
public:
    int binary_search(vector<int> &nums, int x){
        int head = 0, tail = nums.size() - 1, mid;
        while(tail - head > 3){
            mid = (head + tail) >> 1;
            if(nums[mid] >= x) tail = mid;
            else head = mid + 1;
        }
        for(int i = head; i <= tail; i++){
            if(nums[i] >= x) return i;
        }
        return nums.size();
    }
    vector<int> searchRange(vector<int>& nums, int target) {
       vector<int> ret(2);
       ret[0] = ret[1] = -1;
       ret[0] = binary_search(nums, target);
       if(ret[0] == nums.size() || nums[ret[0]] != target){
           ret[0] = ret[1] = -1;
           return ret;
       }
       ret[1] = binary_search(nums, target + 1) - 1;
       return ret;
    }
};

1658. 将 x 减到 0 的最小操作数

class Solution {
public:
    int binary_search(vector<int> &nums, int x){
        int head = 0, tail = nums.size() - 1, mid;
        while(head <= tail){
            mid = (head + tail) >> 1;
            if(nums[mid] == x) return mid;
            if(nums[mid] < x) head = mid + 1;
            else tail = mid -1;
        }
        return -1;
    }
    int minOperations(vector<int>& nums, int x) {
        vector<int> suml(nums.size() + 1);
        vector<int> sumr(nums.size() + 1);
        suml[0] = sumr[0] = 0;
        for(int i = 0; i < nums.size(); i++) suml[i + 1] = suml[i] + nums[i];
        for(int i = nums.size() - 1; i >= 0; --i) sumr[nums.size() - i] = sumr[nums.size() - i - 1] + nums[i];
        int ans = -1;
        for(int i = 0; i < suml.size(); i++){
            int j = binary_search(sumr, x - suml[i]);
            if(j == -1) continue;
            if(i + j > nums.size()) continue;
            if(ans == -1 || ans > i + j) ans = i + j;
        }
        return ans;
    }
};

475. 供暖器

class Solution {
public:
    int binary_search_01(vector<int> &nums, int x){
        int head = 0, tail = nums.size() - 1, mid;
        while(head < tail){
            mid = (head + tail) >> 1;
            if(nums[mid] >= x) tail = mid;
            else head = mid + 1;
        }
        return head;
    }
    int findRadius(vector<int>& houses, vector<int>& heaters) {
        sort(heaters.begin(), heaters.end());
        int ans = 0;
        for(auto x: houses){
            int j = binary_search_01(heaters, x);
            int a = abs(heaters[j] - x);
            int b = (j ? x - heaters[j-1] : a + 1);
            ans = max(ans, min(a, b));
        }
        return ans;
    }
};

81. 搜索旋转排序数组 II

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        if(nums[0] == target || nums[nums.size() - 1] == target) return true;
        int l = 0, r = nums.size() - 1, mid, head, tail;
        while(l < nums.size() && nums[l] == nums[0]) ++l;
        while(r >= 0 && nums[r] == nums[0]) --r;
        while(l <= r){
            mid = (l + r) >>1;
            if(nums[mid] == target) return true;
            if(nums[mid] <= nums[tail]){
                if(target <= nums[tail] && target > nums[mid]) l = mid + 1;
                else r = mid - 1; 
            }else{
                if(target < nums[mid] && target >= nums[head]) r = mid - 1;
                else l = mid + 1;
            }
        }
        return false;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值