查找给定排序数组中不存在的最小数字 >= x
Finding smallest number >= x not present in the given sorted array
我在编写修改后的二进制搜索算法时遇到困难,该算法 returns 大于或等于 X 的最小数字 不 存在于排序数组中。
例如,如果数组是{1,2,3,5,6}
和x = 2
,那么答案就是4。请指导我如何为此编写二进制搜索。我必须在 O(log n) 时间内为每个 x 回答这个问题。由于我将此数组作为输入,最初需要线性时间,因此如果需要,您可以先对数组进行某种预处理。
x 也作为输入,可能存在也可能不存在于数组中。
输入数组可能有重复元素。
我的输入数字可以在 [0,10^9] 范围内,因此首先将所有缺失值放入数组中是不可行的,因为 space 限制。
此外,您可以进行预处理,这需要 O(n) 时间,因为您将数组作为线性时间的输入。在那之后,让我们说 X 的 10^6 个查询,你必须在 O(log n) 时间内回答每个
如果数组中不存在 x
,则 return x
。
如果 x
存在,则说它位于 l
位置。另外,我们将 missing(i)
表示为 i
左侧缺失的元素数。在 1 索引数组中,这等于 A[i]-i
。然后从 l
一直向右移动直到 missing(i) - missing(l) = 0
。您可以为此使用修改后的二进制搜索。假设 p
是最后一个元素的位置,其中 missing(p) - missing(l) = 0
那么 A[p]+1
是第一个大于 x
.
的缺失数
看看这个:
#include <iostream>
#include <vector>
using namespace std;
int greaterValue(const vector<int>& elements, int x){
int low = 0,
high = elements.size() -1,
answer = x + 1;
while (low <= high) {
int mid = (low + high) / 2;
if (elements[mid] <= answer) {
if (elements[mid] == answer) {
answer++;
high = elements.size() - 1;
}
low = mid + 1;
}
else {
high = mid - 1;
}
}
return answer;
}
int main() {
vector<int> elements = { 1, 2, 3, 5, 6 };
int x = 2;
int result = greaterValue(elements, x);
cout << "The element is: " << result;
return 0;
}
测试:
{ 1, 2, 3, 5, 6 }
结果:
The element is: 4
时间复杂度:
O(log(n))
如果我没理解错的话,你可以进行任何类型的预处理,并且只发现不同 x
的结果必须是 O(log n)
。如果是这样的话,在预处理之后找到结果并不是什么大问题。 O(log n)
确实存在搜索算法。好的候选人是 std::binary_search
或 std::lower_bound
。
一种非常幼稚的方法是准备一个包含所有缺失元素的向量,然后 std::lower_bound
在其上:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> input{1,2,3,5,6,10,12};
std::vector<int> missing_elements{4,7,8,9,11};
int x = 2;
auto it = std::lower_bound(missing_elements.begin(),missing_elements.end(),x);
std::cout << *it << "\n";
}
填充 missing_elements
可以在 O(1)
中完成。但是,一个missing_elements
,大小为10^9
量级,当然是行不通的。此外,这种方法对于像 [1,100000000]
这样的输入非常浪费(不是在时间复杂度方面,而是在运行时和内存使用方面)。
Jarod42 在评论中提出的一个想法是准备一个分段向量,然后 std::lower_bound
在其上。首先假设预处理已经完成:
#include <iostream>
#include <vector>
#include <algorithm>
int find_first_missing(const std::vector<std::pair<int,int>>& segments,int x){
std::pair<int,int> p{x,x};
auto it = std::lower_bound(segments.begin(),segments.end(),p,[](auto a,auto b){
return a.second < b.second;
});
if (it == segments.end()) return x;
if (it->first > x) return x;
return it->second+1;
}
int main() {
std::vector<int> input{1,2,3,5,6,10,12};
std::vector<std::pair<int,int>> segments{{1,3},{5,6},{10,10},{12,12}};
for (int x=0; x<13;++x) std::cout << x << " -> " << find_first_missing(segments,x) << "\n";
}
0 -> 0
1 -> 4
2 -> 4
3 -> 4
4 -> 4
5 -> 7
6 -> 7
7 -> 7
8 -> 8
9 -> 9
10 -> 11
11 -> 11
12 -> 13
因为input
是排序的,而segments
是排序的,我们可以使用自定义比较器,只比较段的末尾。段向量也根据该比较器进行排序。对 lower_bound
returns 的调用是指向 x
位于内部或 x
低于该段的段的迭代器,因此 if (it->first > x) return x;
否则我们知道 it->second+1
是下一个缺失的数字。
现在只剩下创建分段向量了:
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
std::vector<std::pair<int,int>> segment(const std::vector<int>& input){
std::vector<std::pair<int,int>> result;
if (input.size() == 0) return result;
int current_start = input[0];
for (int i=1;i<input.size();++i){
if (input[i-1] == input[i] || input[i-1]+1 == input[i]) continue;
result.push_back({current_start,input[i-1]});
current_start = input[i];
}
result.push_back({current_start,input.back()});
return result;
}
int main() {
std::vector<int> input{1,2,3,5,6,10,12};
std::vector<std::pair<int,int>> expected{{1,3},{5,6},{10,10},{12,12}};
auto result = segment(input);
for (const auto& e : result){
std::cout << e.first << " " << e.second << "\n";
}
assert(expected == result);
}
我在编写修改后的二进制搜索算法时遇到困难,该算法 returns 大于或等于 X 的最小数字 不 存在于排序数组中。
例如,如果数组是{1,2,3,5,6}
和x = 2
,那么答案就是4。请指导我如何为此编写二进制搜索。我必须在 O(log n) 时间内为每个 x 回答这个问题。由于我将此数组作为输入,最初需要线性时间,因此如果需要,您可以先对数组进行某种预处理。
x 也作为输入,可能存在也可能不存在于数组中。
输入数组可能有重复元素。
我的输入数字可以在 [0,10^9] 范围内,因此首先将所有缺失值放入数组中是不可行的,因为 space 限制。
此外,您可以进行预处理,这需要 O(n) 时间,因为您将数组作为线性时间的输入。在那之后,让我们说 X 的 10^6 个查询,你必须在 O(log n) 时间内回答每个
如果数组中不存在 x
,则 return x
。
如果 x
存在,则说它位于 l
位置。另外,我们将 missing(i)
表示为 i
左侧缺失的元素数。在 1 索引数组中,这等于 A[i]-i
。然后从 l
一直向右移动直到 missing(i) - missing(l) = 0
。您可以为此使用修改后的二进制搜索。假设 p
是最后一个元素的位置,其中 missing(p) - missing(l) = 0
那么 A[p]+1
是第一个大于 x
.
看看这个:
#include <iostream>
#include <vector>
using namespace std;
int greaterValue(const vector<int>& elements, int x){
int low = 0,
high = elements.size() -1,
answer = x + 1;
while (low <= high) {
int mid = (low + high) / 2;
if (elements[mid] <= answer) {
if (elements[mid] == answer) {
answer++;
high = elements.size() - 1;
}
low = mid + 1;
}
else {
high = mid - 1;
}
}
return answer;
}
int main() {
vector<int> elements = { 1, 2, 3, 5, 6 };
int x = 2;
int result = greaterValue(elements, x);
cout << "The element is: " << result;
return 0;
}
测试:
{ 1, 2, 3, 5, 6 }
结果:
The element is: 4
时间复杂度:
O(log(n))
如果我没理解错的话,你可以进行任何类型的预处理,并且只发现不同 x
的结果必须是 O(log n)
。如果是这样的话,在预处理之后找到结果并不是什么大问题。 O(log n)
确实存在搜索算法。好的候选人是 std::binary_search
或 std::lower_bound
。
一种非常幼稚的方法是准备一个包含所有缺失元素的向量,然后 std::lower_bound
在其上:
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> input{1,2,3,5,6,10,12};
std::vector<int> missing_elements{4,7,8,9,11};
int x = 2;
auto it = std::lower_bound(missing_elements.begin(),missing_elements.end(),x);
std::cout << *it << "\n";
}
填充 missing_elements
可以在 O(1)
中完成。但是,一个missing_elements
,大小为10^9
量级,当然是行不通的。此外,这种方法对于像 [1,100000000]
这样的输入非常浪费(不是在时间复杂度方面,而是在运行时和内存使用方面)。
Jarod42 在评论中提出的一个想法是准备一个分段向量,然后 std::lower_bound
在其上。首先假设预处理已经完成:
#include <iostream>
#include <vector>
#include <algorithm>
int find_first_missing(const std::vector<std::pair<int,int>>& segments,int x){
std::pair<int,int> p{x,x};
auto it = std::lower_bound(segments.begin(),segments.end(),p,[](auto a,auto b){
return a.second < b.second;
});
if (it == segments.end()) return x;
if (it->first > x) return x;
return it->second+1;
}
int main() {
std::vector<int> input{1,2,3,5,6,10,12};
std::vector<std::pair<int,int>> segments{{1,3},{5,6},{10,10},{12,12}};
for (int x=0; x<13;++x) std::cout << x << " -> " << find_first_missing(segments,x) << "\n";
}
0 -> 0
1 -> 4
2 -> 4
3 -> 4
4 -> 4
5 -> 7
6 -> 7
7 -> 7
8 -> 8
9 -> 9
10 -> 11
11 -> 11
12 -> 13
因为input
是排序的,而segments
是排序的,我们可以使用自定义比较器,只比较段的末尾。段向量也根据该比较器进行排序。对 lower_bound
returns 的调用是指向 x
位于内部或 x
低于该段的段的迭代器,因此 if (it->first > x) return x;
否则我们知道 it->second+1
是下一个缺失的数字。
现在只剩下创建分段向量了:
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
std::vector<std::pair<int,int>> segment(const std::vector<int>& input){
std::vector<std::pair<int,int>> result;
if (input.size() == 0) return result;
int current_start = input[0];
for (int i=1;i<input.size();++i){
if (input[i-1] == input[i] || input[i-1]+1 == input[i]) continue;
result.push_back({current_start,input[i-1]});
current_start = input[i];
}
result.push_back({current_start,input.back()});
return result;
}
int main() {
std::vector<int> input{1,2,3,5,6,10,12};
std::vector<std::pair<int,int>> expected{{1,3},{5,6},{10,10},{12,12}};
auto result = segment(input);
for (const auto& e : result){
std::cout << e.first << " " << e.second << "\n";
}
assert(expected == result);
}