题目解析
公司正在进行优秀项目组评比的初选工作,每个项目组的得分已知。评委希望设定一个晋级分数线 ,使得得分大于 的项目组晋级,而得分小于等于 的项目组淘汰。
要求晋级的项目组数量和淘汰的项目组数量均满足区间 。需要根据得分数组 找到满足条件的最小分数线 。若不存在这样的 ,输出 。
思路分析
-
排序简化判断条件:
- 对得分数组 排序,方便直接以每个得分为候选分数线 。
- 计算得分大于 和小于等于 的人数只需统计数组中间的索引位置。
-
二分查找:
- 候选分数线 应为数组中的某个元素。
- 使用二分查找快速定位满足条件的最小 。
- 每次二分时,判断当前分数线是否符合条件:
- 晋级人数(得分大于 )和淘汰人数(得分小于等于 )均满足区间 。
- 若符合条件,记录当前分数线并继续尝试更小的 ;否则根据条件调整查找范围。
-
特殊情况:
- 如果遍历所有分数后仍无满足条件的 ,输出 。
代码实现
import java.util.Arrays;
public class Main {
public static int solution(int m, int n, int[] a) {
// 1. 排序
Arrays.sort(a);
int length = a.length;
int result = -1;
// 2. 二分查找
int left = 0, right = length - 1;
while (left <= right) {
int mid = (left + right) / 2;
int x = a[mid];
// 计算晋级和淘汰人数
int greater = length - (mid + 1); // 得分大于 x 的数量
int lesserOrEqual = mid + 1; // 得分小于等于 x 的数量
// 判断是否满足条件
if (greater >= m && greater <= n && lesserOrEqual >= m && lesserOrEqual <= n) {
result = x; // 更新满足条件的分数线
right = mid - 1; // 尝试找更小的分数线
} else if (greater < m) {
// 晋级人数太少,降低分数线
right = mid - 1;
} else {
// 晋级人数太多,提升分数线
left = mid + 1;
}
}
return result;
}
public static void main(String[] args) {
// 测试用例
System.out.println(solution(2, 3, new int[]{1, 2, 3, 5, 6, 4}) == 3);
System.out.println(solution(1, 2, new int[]{7, 8, 9, 3, 5}) == -1);
System.out.println(solution(1, 4, new int[]{7, 8, 9, 3, 5}) == 3);
}
}
代码详解
-
排序:
- 使用
Arrays.sort(a)将数组按升序排序。 - 排序后,对于任意分数 ,其左侧为淘汰项目组,右侧为晋级项目组。
- 使用
-
二分查找:
- 定义初始左右边界
left = 0, right = length - 1。 - 中间位置
mid对应的分数 是当前候选分数线。 - 对每个候选分数线 :
- 计算晋级人数
greater = length - (mid + 1)。 - 计算淘汰人数
lesserOrEqual = mid + 1。 - 检查
greater和lesserOrEqual是否都满足 :- 若满足,更新结果为当前分数线 ,并继续向左搜索更小的分数线。
- 若晋级人数不足,说明 过高,降低分数线范围。
- 若晋级人数过多,说明 过低,提高分数线范围。
- 计算晋级人数
- 定义初始左右边界
-
返回结果:
- 若找到满足条件的 ,返回结果。
- 若查找结束后未找到,返回 。
时间复杂度
-
排序:
- 排序操作的时间复杂度为 ,其中 是得分数组的长度。
-
二分查找:
- 二分查找最多执行 次。
- 每次计算晋级和淘汰人数需要 时间。
总时间复杂度为:
总结
-
优点:
- 利用排序和二分查找,使得算法高效,能快速定位满足条件的最小分数线。
-
局限:
- 对于边界情况,例如得分数组较小或分数分布不均匀时,可能无满足条件的分数线,需提前考虑输出 。
-
扩展:
- 可以进一步优化统计过程,例如通过预处理前缀和数组快速计算晋级/淘汰人数。
这套方法逻辑清晰、性能良好,是解决类似区间筛选问题的通用方案。