我有一个关于实现二进制搜索的算法测试,最长时间为2秒。
首先,我实现了二进制搜索的递归版本,但在某些测试用例中完成了将近3.6秒。然后,我将其更改为迭代版本,但在同一测试用例中需要2.6秒。但是,我认为使用while loop
是需要花费大量时间的原因。
我的问题是:我需要改进什么才能最多花费2秒钟?
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int iterBinarySearch(vector<int> A, int low, int high, int key) {
int mid;
while (low <= high) {
mid = low + ((high - low)/2);
if (key < A[mid]) {
high = mid -1;
} else if (key > A[mid]) {
low = mid +1;
} else {
return mid;
}
}
return -1;
}
int main() {
vector<int>dict;
vector<int>keys;
int dictSize;
cin >> dictSize;
while (dictSize--) {
int val;
cin >> val;
dict.push_back(val);
}
int keysSize;
cin >> keysSize;
while (keysSize--) {
int val;
cin >> val;
keys.push_back(val);
}
sort(dict.begin(), dict.end());
int size = (int)dict.size() -1;
for(int i = 0; i< keys.size(); ++i) {
if ((dict[0] > keys[i]) || (dict[size] < keys[i])) {
cout << "-1" << ' ';
} else {
int res = iterBinarySearch(dict, 0, size, keys[i]);
cout << res << ' ';
}
}
return 0;
}
答案 0 :(得分:3)
只有两件事显然是浪费的:
int iterBinarySearch(vector<int> A, int low, int high, int key)
复制矢量(可能会有评论中的100,000个元素),而
int iterBinarySearch(const vector<int> &A, int low, int high, int key)
(或任何其他const-ref拼写)将直接搜索原始矢量,无需复制
当您事先知道大小时,您对字典和关键向量的初始push_back
是浪费的:因为您没有告诉向量它将有多大,它必须继续调整大小和复制。只需添加
cin >> dictSize;
dict.reserve(dictSize); // grow to the correct size just once
while (dictSize--) {
int val;
cin >> val;
dict.push_back(val);
}
和钥匙相同。
现在,除了跳出这两件事之外,理想情况下你应该尝试分析你的代码,而不是仅仅猜测缓慢的位置。
答案 1 :(得分:2)
<强> 1。主要问题是当你将dict参数作为值传递时。
只需将其作为 const 引用传递。
int iterBinarySearch(const vector<int> &A, int low, int high, int key) {
// your code
}
<强> 2。也尝试更改此行
mid = low + ((high - low)/2);
到
mid = (low + high)/2;
注意:仅当矢量大小不大于INT_MAX / 2时才进行第二次更改。
答案 2 :(得分:1)
如前所述,将向量作为const引用传递是一个重点,使用reserve
另一个。根本不分配密钥也可以为您提供更多性能:
sort(dict.begin(), dict.end());
int keysSize;
cin >> keysSize;
// this is a constant loop constraint, so move it out, too...
int size = (int)dict.size() - 1;
while (keysSize--)
{
int val;
cin >> val;
if (val < dict[0] || val > dict[size])
{
cout << "-1" << ' ';
}
else
{
int res = iterBinarySearch(dict, 0, size, keys[i]);
cout << res << ' ';
}
}
return 0;
您可以安全地进行一次额外的函数调用:
cout << "-1 ";
当然,不会给你太多,但这很简单所以无论如何我都提到它......
只是旁注:当处理不能通过其性质(大小,数组索引等)得到否定的值时,我更喜欢签名数据类型的无符号计数器部分(在您的情况下为unsigned int
) 。这对性能没有任何影响,就像现代的二进制补码架构一样,将使用完全相同的操作(除了一些比较),只是更清楚地显示变量的有效范围的意图和(部分)远离数据类型(提到一个例外:想象你需要int64_t用于签名,但可以用uint32_t,你有32位架构,例如微控制器 - 然后你真的得到一些最小的性能提升......)。