LeetCode 面试题 10.05. 稀疏数组搜索
题目描述

题意分析
非空字符串按字典序排列,但数组中夹有空字符串占位。寻找目标单词的实际数组下标,不存在则返回 -1。空字符串不代表这一位置应按字典序排在所有单词之前,不能直接用它判断目标在哪一侧。
解法:二分 + 向右探测非空字符串
核心思路
[!blue]
保留二分框架,中点为空时向右寻找可比较的单词。 当前搜索范围为闭区间[lo, hi],取中点mid,再令probe从mid向右跳过空串。探测只能在当前区间内进行,不能越过hi去使用已经排除的位置。若
probe > hi,说明[mid, hi]全是空位,其中不存在要找的单词,直接令hi = mid - 1。若找到非空项,则[mid, probe - 1]已确定全为空,可以在比较时一起排除。若
words[probe]小于目标,所有更左的非空项也不大于它,因此目标只能在probe右侧,令lo = probe + 1。若它大于目标,probe及其右侧的非空项都过大,而mid到probe - 1又全为空,所以可以直接令hi = mid - 1,不必仅退到probe - 1。相等时返回的是probe,因为它才是单词所在的真实下标。每轮要么返回答案,要么至少排除从中点开始的一侧范围,保证区间持续收缩。向右扫描过的空位也会随本轮被排除,不会在后续反复扫描;空位很多时可能需要线性探测,但不会因此形成无限循环。
解题步骤
- 建立闭区间搜索范围。
- 从中点向右跳过空串,探测不越过当前右界。
- 整段为空则收缩到左半区。
- 找到非空项后比较:相等返回
probe,较小则移动左界到probe + 1,较大则移动右界到mid - 1。lo > hi时返回 -1。区间里全部是空串时,也会逐次缩小直到结束。
代码实现
class Solution {
public int findString(String[] words, String s) {
int lo = 0;
int hi = words.length - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
int probe = mid;
// 在当前搜索范围内跳过空串,找到可作比较的探针。
while (probe <= hi && words[probe].isEmpty()) {
probe++;
}
// 中点到右界全为空串,整段可以直接排除。
if (probe > hi) {
hi = mid - 1;
continue;
}
int cmp = words[probe].compareTo(s);
if (cmp == 0) {
return probe;
} else if (cmp < 0) {
lo = probe + 1;
} else {
hi = mid - 1;
}
}
return -1;
}
}
func findString(words []string, s string) int {
lo, hi := 0, len(words)-1
for lo <= hi {
mid := lo + (hi-lo)/2
probe := mid
// 在当前搜索范围内跳过空串,找到可作比较的探针。
for probe <= hi && words[probe] == "" {
probe++
}
// 中点到右界全为空串,整段可以直接排除。
if probe > hi {
hi = mid - 1
continue
}
if words[probe] == s {
return probe
} else if words[probe] < s {
lo = probe + 1
} else {
hi = mid - 1
}
}
return -1
}
复杂度分析
- 时间复杂度:上界 $O(n+L\log(n+1))$,其中 $n$ 为数组长度,$L$ 为参与比较字符串的最大长度。所有空位累计最多扫描 $O(n)$ 次;每轮范围至少减半,至多进行 $O(\log(n+1))$ 次非空字符串比较,每次最坏花 $O(L)$。
- 空间复杂度:$O(1)$。
关键点总结
[!green]
- 有效比较点可能不同于二分中点。
- 探测过的空段可以一并排除。
- 没有非空探针时,也必须让搜索区间收缩。
易错点总结
[!yellow]
- 直接拿空串作字典序判断:可能错误丢掉左侧目标。
- 探测不限制右界:可能越界或读取已排除范围。
- 命中返回 mid:它可能仍指向空串。
- 探测失败直接 continue 而不更新边界:重复同一区间无法结束。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 704. 二分查找 | 简单 | 有序二分框架可复用,但本题中点为空串时没有有效比较信息,必须先寻找非空候选。 |
| 81. 搜索旋转排序数组 II | 中等 | 同样存在无法直接判断保留半区的比较情形,需要谨慎缩边而不是强行按普通二分推进。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!