实现思路
1 难点是 转化题意:
- 等于target的最左侧位置: >=target的 最小值
- 等于target的最右侧位置: <=target的 最大值
转化后,就是一个典型的 二分查找问题
2 对 mid取值的 理解:
2.1 如果你要保留包含 mid 的那半边区间, 那么 mid 的取值方向,必须要能让新区间变小, 否则就可能陷入死循环
2.2 记忆方法:
- 想保留左边[l, mid],mid 就要偏左
- 想保留右边[mid, r],mid 就要偏右
- "想保留哪边,mid 就要往哪个方向偏"
参考文档
代码实现
方法1.1: 二分查找 时间复杂度: O(logn) 空间复杂度: O(1)
const readline = require("readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
let inputArr = [];
let n, m;
let q = [];
rl.on("line", function (line) {
inputArr.push(line.trim());
// 第一行输入 n 和 m
if (inputArr.length === 1) {
[n, m] = inputArr[0].split(" ").map(Number);
}
// 第二行输入数组
else if (inputArr.length === 2) {
q = inputArr[1].split(" ").map(Number);
}
// 处理每个查询
else if (inputArr.length === 2 + m) {
// 处理所有查询
for (let i = 2; i < inputArr.length; i++) {
const x = Number(inputArr[i]);
console.log(findRange(q, x));
}
rl.close();
}
});
function findRange(nums, x) {
const len = nums.length;
// 查找左边界: >=x的最小值,就必然是 等于x的最左侧位置
let l = 0, r = len - 1;
// 获取 >=x 的最小值
while (l < r) {
let mid = (l + r) >> 1;
const midVal = nums[mid];
// 如果 midVal < x,说明 [l, mid]都<x,不是我们所需要的,所以左区间向右扩增
if (midVal < x) l = mid + 1;
// 如果 midVal >= x,说明 [mid, r]都>=x,而 我们要找的是最小的>=x的位置, 所以 右区间需要收缩
else r = mid;
}
// 如果没找到目标值,说明nums中必然没有x,直接返回 -1 即可
if (nums[l] !== x) return "-1 -1";
// 记录左边界
const resL = l;
// 查找右边界: <=x的最大值,就必然是 等于x的最右侧位置
l = 0, r = len - 1;
while (l < r) {
// 易错点: 这里需要 +1, 是因为在相邻数位置情况下,如果还是向下取整,l会一直赋值为mid,陷入死循环
let mid = (l + r + 1) >> 1;
const midVal = nums[mid];
// 如果arr[mid] <= x,说明 [l, mid]都<=x,而 我们要找的是最大的<=x的位置, 所以 左区间需要扩增
if (midVal <= x) l = mid;
// 如果 arr[mid] > x,说明 [mid, r]都>x,不是我们所需要的, 所以 右区间需要收缩
else r = mid - 1;
}
// 返回结果
return `${resL} ${l}`;
}
方法1.2: 二分查找-递归 时间复杂度: O(logn) 空间复杂度: O(logn)
function findRange(nums, x) {
// 起始位置:>=x的 最小值的idx; 结束位置:<=x的最大值的idx
let frontIdx = findFront(nums, 0, nums.length - 1, x)
let tailIdx = findTail(nums, 0, nums.length - 1, x)
return `${frontIdx} ${tailIdx}`
}
// 起始位置:>=x的 最小值的idx
function findFront(arr, l, r, x) {
if (l >= r) return arr[l] === x ? l : -1;
const mid = l + (r - l >> 1)
if (arr[mid] < x) {
return findFront(arr, mid + 1, r, x)
} else {
return findFront(arr, l, mid, x)
}
}
// 结束位置:<=x的最大值的idx
function findTail(arr, l, r, x) {
if (l >= r) return arr[l] === x ? l : -1;
const mid = l + (r - l + 1 >> 1)
if (arr[mid] <= x) {
return findTail(arr, mid, r, x)
} else {
return findTail(arr, l, mid - 1, x)
}
}