二分法的使用以及边界设定

二分法的使用以及边界设定

二分法用于有序序列的查找,其平均复杂度为O(lg(n)),二分法的算法比较简单,但是其边界的处理却让人很头疼,为方便以后复习,下面总结出了4中目前我遇到的模板

二分法的几个要素:
1、left
2、mid
3、right
一般我们要查找的结果也是这三个中的其中一个,目前我遇到的是第一种和第三种

题目背景:

找出一个有序序列中某元素第一次或最后一次出现的位置

显然序列中某个元素可能存在多个,下面以 arr = [1,2,3,3,3,3,5]这个序列为例

找第一次出现的位置


整体思路:

1、 当mid 处的值明确小于目标值时,将左边界右移 即arr[mid] < target 时 left = mid + 1

2、当 mid 处的值大于目标值时,将右边界左移 即arr[mid] > target时, right = mid

3、 目标是找到某元素第一次出现的位置,若在某个mid位置的值刚好是目标元素,

重点来了:
    接下来要做的就是看当前元素前面有没有与目标元素一样的,有可能有,也有可能没有,所以要保留当前mid, 这种情况其实就是查找范围要往左边缩减,即
        arr[mid] >= target时 right = mid

4、最终 left 和 right 相等,循环结束,left就是要找的位置

模板一: 查找left

 int binarySearchFirst(vector<int> arr, int target) {
    int left = 0;
    int right = arr.size() - 1;
    int mid;
    //此处的while为关键
    while (left < right) {
        mid = left + (right - left) / 2;
        if (arr[mid] < target) left = mid + 1; //因为已经明确mid 处的值明确小于目标值时,所以目标一定在[mid+1,right]中
        else right = mid;
    }

    //还要判断left的值是否真的是target,即判断目标是否存在
    if (arr[left] == target) return left;
    else return -1;
}

while循环内取 left<right 而不是 left<=right ,是因为若 取到等号会陷入死循环

模板二: 查找right

int binarySearchFirst(vector<int> arr, int target) {
    int left = 0;
    int right = arr.size() - 1;
    int mid;
    //此处的while为关键
    while (left + 1 < right) {
        mid = left + (right - left) / 2;
        if (arr[mid] < target) left = mid ; 
        else right = mid;
    }

    //还要判断right的值是否真的是target,即判断目标是否存在
    //这种情况就变成了取右值
    if (arr[right] == target) return left;
    else return -1;
}

找最后一次出现的位置

整体思路:
1、 mid 处的值明确大于目标值时,将右边界左移
2、 当 mid 处的值小于目标值时,将左边界右移
3、 当mid处的值等于目标值时,需要继续观察mid后面有没有等于目标值的地方,即
arr[mid]<=target left = mid
4、left 和 right 相等,循环终止,该位置就是目标值最后一次出现的位置。

模板三:找left

public static void binarySearchLast(vetor<int> arr, int target) {
    int left = 0;
    int right = arr.size() - 1;
    int mid;

    while (left < right) {
        mid = left + (right - left + 1) / 2;  //+1是为了向上取整,mid 会偏向右边界,因此 left = mid 可以确保左边界往右移动,缩小查找范围。
        if (arr[mid] > t) right = mid - 1;
        else left = mid;
    }

    if (arr[left] == target) return left;
    else return -1;
}

模板四:找left

public static void binarySearchLast(vetor<int> arr, int target) {
    int left = 0;
    int right = arr.size() - 1;
    int mid;

    while (left + 1 < right) {
        mid = left + (right - left + 1) / 2;  //+1是为了向上取整,mid 会偏向右边界,因此 left = mid 可以确保左边界往右移动,缩小查找范围。
        if (arr[mid] > t) right = mid;
        else left = mid;
    }

    if (arr[left] == target) return left;
    else return -1;
}

这几种模板可能对有些题目不起作用(具体还没分析过),但弄明白这几种,其他的也可以熟悉的分析出来

附上LeetCode875. 爱吃香蕉的珂珂的C++代码

方法一:

class Solution {
public:
    int minEatingSpeed(vector<int>& piles, int H) {
        int r = pow(10,9);
        int l = 0, mid;
        while (left + 1 < right) {
            mid = left +(right - left) / 2;
            if (f(piles, H, mid)) {
                left = mid;
            } else {
                right = mid;
            }
        }
        return right;
    }
    bool f(vector<int>& piles, int H, int m) {
        int t = 0;
        for (auto p : piles) {
            t += p / m + (p % m != 0);
            if (t > H) return true;
        }
        return false;
    }
};

方法二:

class Solution {
public:
	int minEatingSpeed(vector<int>& piles, int H) {
		int left = 1;
		int right = pow(10, 9);
		int mid;
		while (left < right) {
			mid = left + (right - left) / 2;
			if (findKey(piles, H, mid)) {
				left = mid + 1;
			}
			else {
				right = mid;
			}
		}
		return left;
	}
	bool findKey(vector<int>&piples, int H, int mid) {
		int t = 0;
		for (auto pile :  piples) {
			t += pile / mid + (pile % mid == 0 ? 0 : 1);
		}
		if (t <= H) {
			return false;
		}
		return true;
	}
	
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值