文章目录
- 0. Leetcode [35. 搜索插入位置](https://leetcode.cn/problems/search-insert-position/)
- 1. Leetcode [704. 二分查找](https://leetcode.cn/problems/binary-search/)
- 2. Leetcode [剑指 Offer 53 - I. 在排序数组中查找数字 I](https://leetcode.cn/problems/zai-pai-xu-shu-zu-zhong-cha-zhao-shu-zi-lcof/)
- 3. Leetcode [911. 在线选举](https://leetcode.cn/problems/online-election/)
- 总结
0. Leetcode 35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n) 的算法。
分析与解答
典型的二分查找问题,套用模板即可。
开区间
(
l
,
r
)
(l, r)
(l,r) 二分查找
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l(-1), r(nums.size());
while (l < r - 1) { // 中间区间长度为 1
int mid = (l + r) / 2;
// (l, r) 两端开区间搜索
if (nums[mid] > target) {
r = mid;
} else if (nums[mid] < target) {
l = mid;
} else {
return mid;
}
}
// r 总是指向大于 target 的位置,l 总是指向小于 taeget 的位置
return r;
}
};
闭区间 [ l , r ] [l, r] [l,r] 二分查找
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l(0), r(nums.size() - 1);
// 注意闭区间 [l, r] 查找时的边界条件和更新与上一种方法的区别
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] > target) {
r = mid - 1;
} else if (nums[mid] < target) {
l = mid + 1;
} else {
return mid;
}
}
// 最后 l 所指为满足 nums[mid] < target 的后一个数
return l;
}
};
这里给出两种二分查找模板以供对比。算法时间复杂度为 O ( l o g N ) O(logN) O(logN)
1. Leetcode 704. 二分查找
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
分析与解答
同样给定一个有序数组,依然为典型的二分查找问题。
class Solution {
public:
int search(vector<int>& nums, int target) {
int l(0), r(nums.size() - 1);
while (l <= r) {
int mid = (l + r) / 2;
if (nums[mid] < target) {
l = mid + 1;
} else if (nums[mid] > target) {
r = mid - 1;
} else {
return mid;
}
}
return -1;
}
};
算法时间复杂度为 O ( l o g N ) O(logN) O(logN)
2. Leetcode 剑指 Offer 53 - I. 在排序数组中查找数字 I
统计一个数字在排序数组中出现的次数。
分析与解答
由于数组为已排序数组,因此要统计某个数字出现的次数只要找到满足 n u m s [ i ] < t a r g e t nums[i] < target nums[i]<target 的最大下标与满足 n u m s [ j ] > t a r g e t nums[j] > target nums[j]>target 的最小下标即可,此时数字的出现次数为 j − i − 1 j - i - 1 j−i−1 次。这种情况下就将原问题转化为两次二分查找问题:
class Solution {
public:
int search(vector<int>& nums, int target) {
int lb(0), rb(0);
int l(-1), r(nums.size());
// 查找左边界,满足 nums[i] < target 的最大 i
while (l < r - 1) {
int mid = (l + r) / 2;
if (nums[mid] >= target) {
r = mid;
} else {
l = mid;
}
}
lb = l;
l = -1;
r = nums.size();
// 查找右边界,满足 nums[j] > target 的最小 j
while (l < r - 1) {
int mid = (l + r) / 2;
if (nums[mid] > target) {
r = mid;
} else {
l = mid;
}
}
rb = r;
return rb - lb - 1;
}
};
算法时间复杂度为 O ( l o g N ) O(logN) O(logN)
3. Leetcode 911. 在线选举
给你两个整数数组 persons 和 times 。在选举中,第 i 张票是在时刻为 times[i] 时投给候选人 persons[i] 的。
对于发生在时刻 t 的每个查询,需要找出在 t 时刻在选举中领先的候选人的编号。
在 t 时刻投出的选票也将被计入我们的查询之中。在平局的情况下,最近获得投票的候选人将会获胜。
实现 TopVotedCandidate 类:
TopVotedCandidate(int[] persons, int[] times) 使用 persons 和 times 数组初始化对象。
int q(int t) 根据前面描述的规则,返回在时刻 t 在选举中领先的候选人的编号。
提示:
1 <= persons.length <= 5000
times.length == persons.length
0 <= persons[i] < persons.length
0 <= times[i] <= 109
times 是一个严格递增的有序数组
times[0] <= t <= 109
每个测试用例最多调用 104 次 q
分析与解答
该问题为前缀和与二分查找的应用。首先在初始化时使用前缀和,计算每个 times[i] 时刻投票后对应的获胜者,将其存入 m_winner 中。之后,查询时使用二分查找,找到满足
t
<
t
i
m
e
s
[
i
]
t < times[i]
t<times[i] 的最大
i
i
i,此时 m_winner[i - 1] 即为最终答案。
class TopVotedCandidate {
private:
vector<int> m_winner; // 存储 times[i] 投票后对应的获胜者
vector<int> m_persons;
vector<int> m_times; // 查询时二分查找使用,查找满足 t < times[i] 的最大 i
public:
TopVotedCandidate(vector<int>& persons, vector<int>& times) {
m_persons = persons;
m_times = times;
// 计算每个时刻对应的获胜者
// 对应时刻 times[i] 时的获胜者,-1 表示没有计算过
m_winner.resize(persons.size(), -1);
int curWinner(-1); // 当前获胜者
vector<int> vote;
vote.resize(5000, 0); // 最多 5000 个候选人
for (int i= 0; i < persons.size(); ++i) {
vote[persons[i]]++;
if (curWinner < 0) { // 第一次投票
curWinner = persons[i];
} else { // 后续根据最高票选择获胜者,平手选最近的
if (vote[persons[i]] >= vote[curWinner]) {
curWinner = persons[i];
}
}
m_winner[i] = curWinner; // 更新获胜者向量
}
}
int q(int t) {
// 二分查找时间 t 在 times 中的位置,times[i - 1] <= t < times[i]
int l(-1), r(m_times.size());
while (l < r - 1) {
int mid = (l + r) / 2;
if (m_times[mid] <= t) {
l = mid;
} else {
r = mid;
}
}
return m_winner[r - 1];
}
};
/**
* Your TopVotedCandidate object will be instantiated and called as such:
* TopVotedCandidate* obj = new TopVotedCandidate(persons, times);
* int param_1 = obj->q(t);
*/
查询算法时间复杂度为 O ( l o g N ) O(logN) O(logN)
总结
二分查找模板理解之后十分简单,适用于大部分有序数组中的查找问题(例如前几天的两数和问题),是一种适用性很广的算法。
3378

被折叠的 条评论
为什么被折叠?



