第十周(11)学习总结。(二分)

每周学习总结: 第十周(11)。
本周学习:二分查找算法;

概念:
在一个 有序序列 中,定义一个起始位置start(序列第一个元素)和一个终止位置end(序列最后一个元素),通过mid=(start+end)/2计算出中间位置,通过待查找元素与mid中间位置的元素进行比较,如果待查找元素比中间位置mid对应的值小,那么将end = mid -1(即将end结束位置移动到mid中间左边一个位置),如果比中间对应的值大,那么将start = mid + 1 (即将start的位置移动到mid右边一个位置),
一直循环查找,直到start > end,证明没找到对应的元素,停止循环。
时间复杂度:
O(logN)。
基本代码:

//x:待查找的值,Caculate():所要查找的函数,在这里单调递增
     //需保证查找的值在区间范围内

double end,start,mid;
while(end - start > 1.0e-6)
{
	 mid = (start+ end)/2;
    if(Caculate(mid)<x)
        start=mid;
    else
        end=mid;
}

使用条件:
至少满足:(有序序列)(需要查找某个或某多个值)。
拓展说明:
1
若是right = mid - 1, 则前面mid 语句要加1(left=mid + 1)(记住与r平衡就行)
原因:
本算法的搜索区间是两端都闭的,即 [left, right]。那么当我们发现索引 mid 不是要找的 target 时,当然是去搜索 [left, mid - 1] 或者 [mid + 1, right],因为 mid 已经搜索过。
2、
其实二分并不一定局限在有序区间内,只要区间具有二段性就可以二分,即区间具有某个性质可以将区间分为两段,使得每次搜索时都能将区间缩小而且保证答案仍在区间内
二段性:两端分开的数列,整体是单调的。
算法缺陷:
有序数组 nums = [1,6,6,6,3],target = 6,此算法返回的索引是 2,没错。但是如果我想得到 target 的左侧边界,即索引 1,或者我想得到 target 的右侧边界,即索引 3,这样的话此算法是无法处理的。
处理方法:
(两种不同的二分查找法。)
寻找左侧边界的二分搜索

double left_bound(nums, target)
{
    if (nums.length === 0) 
    return -1;
    double left = 0;
    double right = nums.length-1; // 注意

    while (left <= right) { // 注意
        double mid = ((left + right) / 2);
        if (nums[mid] === target) {
     // 找到 target 时不要立即返回,而是缩小「搜索区间」的上界 right,
   //在区间 [left, mid-1] 中继续搜索,即不断向左收缩,达到锁定左侧边界的目的。
            right = mid-1;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid-1; // 注意
        }
    }
    return left;
};
// 算法会返回 1。这个 1 的含义可以这样解读:nums 中小于 2 的元素有 1 个。
double left_bound([1,2,2,2,3,4],2);
// 算法会返回 4。这个 4 的含义可以这样解读:nums 中小于 3 的元素有 4 个。
 double left_bound([1,2,2,2,3,3,3,3,4],3);

寻找右侧边界的二分搜索

double right_bound(nums, target)
{
    if(nums.length<=0) return -1;
    double left=0,right=nums.length-1;
    while(left<=right){
        double mid = ((left + right) / 2);
        if (nums[mid] === target) 
        {
   // 找到 target 时不要立即返回,而是增大「搜索区间」的上界left,
 //在区间 [mid+1,right] 中继续搜索,即不断向右收缩,达到锁定右侧边界的目的。
            left = mid+1;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid-1; // 注意
        }
    }
    // 其实return left-1还是right没有区别
    return right;
};
// 算法会返回 5。这个 3 的含义就是右侧边界索引是5。
double  right_bound([1,2,2,2,3,3,4],3);
// 算法会返回 7。这个 7 的含义就是右侧边界索引是7。
 double   right_bound([1,2,2,2,3,3,3,3,4],3);

感想以及注意事项:
1 、多多检查区间,while循环,检查看看有没有漏掉的元素,及时修改。
2、如需要搜所左右边界,只要在num[mid]==target 时做修改。
(搜索右侧是要减去1)。
3、用下标代替数组数本身,相对更简单一点。更好理解。

下周目标:
1、每天 1~2 题 , 周末 2 ~3 题。
🆗

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值