所有的LeetCode题解索引,可以看这篇文章——【算法和数据结构】LeetCode题解。
题目
一、暴力穷解法
思路分析:看到查找数组这类题目,暴力穷解法一般都可以解决,那么我们首先调用库函数find()找到左边界,然后从左边界出发找右边界(left=right),这里要注意一个特殊的情况,就是左边界等于数组末端索引。这时候右边界等于左边界,可以直接返回。剩下的情况就是while循环找到第一个不等于target的值的索引,这个就是右边界(不包含target),返回时右边界要减去1。如果找不到,那么right本身就是右边界。
// 1、暴力穷解法
vector<int> searchRange2(vector<int>& nums, int target) {
auto it = find(nums.begin(), nums.end(), target); // 返回索引
if (it == nums.end()) {
return {
-1, -1 };
}
else {
int left = it - nums.begin(); // 左边界
int right = left;
if (left != nums.size() - 1) {
while (right < (nums.size() - 1) && nums[++right] == target); // 右边界
}
return {
left, nums[right] != target ? right - 1 : right};
}
}
和其他人的算法比较:速度稍微慢了点,而且内存占用多。
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),最坏的情况下要遍历整个数组。
- 空间复杂度: O ( 1 ) O(1) O(1),运行时占用的内存是一个常数。
二、二分法
思路分析:阅读题目我们可以得出三种情况:
- 1.target不在数组内,返回{-1, -1}。
- 2.target在数组内, 正常返回{ left, right}。
- 3.target在数组内,一些特殊情况。
第一种情况使用普通的二分法就可以搞定,第二种先找到target的索引middle,然后向两边延伸找到边界值,那么如何延伸呢?
我们同样使用while循环找到边界值,首先要限定左右边界索引的范围,比如左边界必须大于0,右边界必须小于数组容量。索引不断变换,直到数组的值不等于target,我们就找到边界值,正常的情况是能够找到不等于target的边界值,那么返回{left +1, right - 1 }就可以。第三种就是特殊情况,当找不到不等于target的边界值时,返回边界值索引本身。
// 2、二分法
vector<int> searchRange(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1, middle = 0;
while (left <= right) {
middle = (left + right) / 2;
if (nums[middle] < target) {
left = middle + 1;
}
else if (nums[middle] > target) {
right = middle - 1;
}
else {
left = middle;
right = middle;
while (right < (nums.size() - 1) && nums[++right] == target );
while (left > 0 && nums[--left] == target);
return {
nums[left] != target ? left + 1 : left, nums[right] != target ? right - 1 : right };
}
}
return {
-1, -1 };
}
复杂度分析:
- 时间复杂度: O ( n ) O(n) O(n),虽然用了二分法,但是实际时间复杂度没有达到题目要求,本质上是靠while循环找到边界值,最坏的情况就是遍历整个数组,后续有待改进。
- 空间复杂度: O ( 1 ) O(1) O(1),固定内存。
算法对比:较之暴力穷解法,速度和内存方面都有很大提升。
完整代码
// 34. 在排序数组中查找元素的第一个和最后一个位置
# include <iostream>
# include <vector>
# include <algorithm>
using namespace std;
class Solution {
public:
// 1、暴力穷解法
vector<int> searchRange2(vector<int>& nums, int target) {
auto it = find(nums.begin(), nums.end(), target); // 返回索引
if (it == nums.end()) {
return {
-1, -1 };
}
else {
int left = it - nums.begin(); // 左边界
int right = left;
if (left != nums.size() - 1) {
while (right < (nums.size() - 1) && nums[++right] == target); // 右边界
}
return {
left, nums[right] != target ? right - 1 : right};
}
}
// 2、二分法
vector<int> searchRange(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1, middle = 0;
while (left <= right) {
middle = (left + right) / 2;
if (nums[middle] < target) {
left = middle + 1;
}
else if (nums[middle] > target) {
right = middle - 1;
}
else {
left = middle;
right = middle;
while (right < (nums.size() - 1) && nums[++right] == target );
while (left > 0 && nums[--left] == target);
return {
nums[left] != target ? left + 1 : left, nums[right] != target ? right - 1 : right };
}
}
return {
-1, -1 };
}
};
void my_print(vector<int>& v, string msg)
{
cout << msg << endl;
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << ' ';
}
cout << endl;
}
int main()
{
int target = 8;
int arr[] = {
5, 7, 7, 8, 8, 10 };
vector<int> nums;
Solution s1;
for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
{
nums.push_back(arr[i]);
}
my_print(nums, "目标数组:");
vector<int> index = s1.searchRange(nums, target);
my_print(index, "查找结果:");
system("pause");
return 0;
}
end