二分查找---查找区间

查找区间

34. Find First and Last Position of Element in Sorted Array

Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]

Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
题目描述:

  统计一个数字在排序数组中出现的次数。

思路分析:

  由题目知道这道题是属于排序数组中的查找问题,那么我们首先想到的就是二分查找。由于要在排序数组中找一个数字出现的次数,那么我们只要确定这个数字第一次出现的地方和最后一个出现的位置那么我们就可以算出它在数组出现的次数。二分查找算法首先拿数组的中间数字和K作比较,如果中间的数比K大,那么我们下一轮只需要在前半段进行查找。如果比K小,那我们在后半段进行查找。如果等于K那我们要判断这个K是不是第一个K,那么我们就需要看前一个数字是不是K,如果不是那么中间数就是第一个K,如果前一个数字是K,那么第一个K肯定在数组的前段,下一阶段我们就在前半段进行查找。同样的要找最后一个K首先拿数组的中间数字和K作比较,如果中间的数比K大,那么我们下一轮只需要在前半段进行查找。如果比K小,那我们在后半段进行查找。如果等于K那我们要判断这个K是不是最后一个K,那么我们就需要看后一个数字是不是K,如果不是那么中间数就是最后一个K,如果后一个数字是K,那么最后一个K肯定在数组的后半段,下一阶段我们就在后半段进行查找。

代码:
public int[]searchRange(int []nums,int target){
    if(nums==null||nums.length==0)
        return null;
    int first=0;
    int last=0;
    first=getFirst(nums,0,nums.length-1,target);
    last=getLast(nums,0,nums.length-1,target);
    if(first>-1&&last>-1){
        return new int[]{first,end};
    }
    return null;
}
public int getFirst(int []nums,int l,int h,int target){
    if(l>h)
        return -1;
    int mid=l+(h-l)/2;
    if(nums[mid]==target){
        if(mid>0&&nums[mid-1]!=target||mid==0){
            return mid
        }else{
            h=mid-1;
        }
    }else if(nums[mid]>target){
        h=mid-1;
    }else{
        l=mid+1;
    }   
     return getFirst(nums,l,h,target);
}
public int getLast(int []nums,int l,int h,int target){
    if(l>h)
        return -1;
    int mid=l+(h-l)/2;
    if(nums[mid]==target){
        if(mid<nums.length-1&&nums[mid+1]!=target||mid==nums.length-1){
            return mid;
        }else{
            l=mid+1;
        }
    }else if(nums[mid]>target){
        h=mid-1;
    }else{
        l=mid+1;
    }
    return getLast(nums,l,h,target);
}

转载于:https://www.cnblogs.com/yjxyy/p/11106736.html

<think>好的,我现在需要帮用户解答关于进阶二分查找算法实现及优化的问题。首先,我得回想一下用户提供的引用内容,看看有哪些相关的信息可以利用。用户提到了引用[2]、[3]、[4],这些内容主要涉及二分答案转化为判定、数列分段以及货物运输等应用场景,这些都是二分查找的进阶用法。 首先,我应该先回顾基础的二分查找,然后过渡到进阶部分。用户可能已经了解标准二分查找,所以重点放在如何扩展和应用上。根据引用[2],二分答案的思想是将最优化问题转化为判定问题,这需要找到单调性来确定分界点。例如,在数列分段问题中,通过二分枚举最大值,然后检查是否能将数列分成指定的段数,这属于典型的二分答案应用。 接下来,引用[4]中的货物运输问题同样使用了二分答案,将运载能力作为二分的目标,计算所需天数。这里的关键是确定单调性,即运载能力越大,所需天数越少,因此可以通过二分找到最小的满足条件的能力值。 此外,用户可能想知道如何实现这些进阶用法,比如如何设计检查函数(check函数),如何处理边界条件,以及如何优化计算效率。例如,在代码示例中,使用左闭右闭或左闭右开区间,以及如何调整左右边界来寻找左边界或右边界。 还需要考虑优化点,比如循环条件的写法,避免死循环,以及如何处理重复元素的情况。比如在寻找左侧边界的二分中,当找到目标值时,继续向左收缩右边界,以确保找到第一个出现的位置。 可能用户还会对实际应用场景感兴趣,比如最大值最小化或最小值最大化问题,这类问题通常可以通过二分答案来解决。例如,分配问题、资源优化等。 最后,我需要确保回答结构清晰,分点说明实现方法、优化技巧和应用场景,并给出代码示例帮助理解。同时,根据用户要求,生成相关问题,引导进一步学习。</think>### 进阶二分查找算法实现及优化 #### 一、基础二分查找回顾 标准二分查找用于在**有序数组**中快速定位元素,时间复杂度为$O(\log n)$。例如查找$target=5$: $$ \text{arr} = [1,3,5,7,9] $$ 通过比较中间元素与目标值,逐步缩小搜索范围。 #### 二、进阶实现方法 进阶二分查找的核心是**将问题抽象为单调性判定**,通过二分答案转化为可行性验证[^2]。 ##### 1. 二分答案框架 ```python left, right = 最小可能值, 最大可能值 while left < right: mid = (left + right) // 2 if check(mid): # 检查mid是否满足条件 right = mid else: left = mid + 1 return left ``` ##### 2. 关键设计要素 - **单调性验证**:确保问题的解空间具有单调性,例如“最大值最小化”问题中,当运载能力$x$增加时,所需天数$f(x)$单调递减[^4] - **检查函数设计**:实现`check(mid)`函数需满足: $$ \exists x \leq mid \Rightarrow check(mid) = True $$ $$ \forall x > mid \Rightarrow check(mid) = False $$ #### 三、优化技巧 1. **边界初始化优化** - 左边界取单个元素最大值(如包裹运输中取最大包裹重量) - 右边界取总和(如数列分段问题取所有元素和)[^3] 2. **循环条件选择** - `while left < right`避免死循环 - 终止时`left == right`即为解 3. **防止整数溢出** 使用`mid = left + (right - left) // 2`代替`(left+right)//2` 4. **左边界/右边界处理** - 找第一个满足条件的位置(左边界):`right=mid` - 找最后一个满足条件的位置(右边界):`left=mid` #### 四、典型应用场景 1. **最大值最小化问题** - 例:将数列分成$m$段,求各段和的最大值最小[^3] ```python def check(x): cnt, current = 1, 0 for num in arr: if current + num > x: cnt += 1 current = num else: current += num return cnt <= m ``` 2. **最小值最大化问题** - 例:在书籍页数分配中,使最少页数的最大值最大 3. **隐式有序序列查找** - 例:求平方根精度问题 ```python def sqrt_precision(n, eps=1e-6): left, right = 0, n while right - left > eps: mid = (left + right) / 2 if mid*mid < n: left = mid else: right = mid return left ``` #### 五、代码示例(货物运输优化) 参考引用[4]的运载能力优化问题: ```java class Solution { public int shipWithinDays(int[] weights, int days) { int left = 0, right = 0; for(int w : weights){ left = Math.max(left, w); // 最小运力下限 right += w; // 最大运力上限 } while(left < right){ int mid = left + (right - left)/2; if(calcDays(weights, mid) <= days){ right = mid; }else{ left = mid + 1; } } return left; } // 计算给定运载能力所需天数 private int calcDays(int[] weights, int capacity){ int days = 1, current = 0; for(int w : weights){ if(current + w > capacity){ days++; current = w; }else{ current += w; } } return days; } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值