我有这样的数据结构:
struct X {
float value;
int id;
};
那些(大小 N (想想100000)的向量,按值排序(在程序执行期间保持不变):
std::vector<X> values;
现在,我想写一个函数
void subvector(std::vector<X> const& values,
std::vector<int> const& ids,
std::vector<X>& out /*,
helper data here */);
使用值的已排序子集填充 out 参数,由传递的 ID (大小 M >&lt; N (约0.8倍 N ),快(内存不是问题,这将重复进行,因此构建lookuptables(来自函数参数的辅助数据)或其他只做一次的事情就完全可以了。)
到目前为止我的解决方案:
构建包含 id 的可查找表 lut - &gt; 值中的偏移量(准备,因此恒定运行时间)
创建std::vector<X> tmp
,大小为N,填充无效ID( N 中的线性)
对于每个ID,请将values[lut[id]]
复制到tmp[lut[id]]
( M 中的线性)
循环遍历 tmp ,将项目复制到 out (线性在 N )
这在 N 中是线性的(因为它大于 M ),但是临时变量和重复复制会让我感到困惑。有没有办法比这更快?请注意, M 将接近 N ,因此O( M log N )的内容不利。
编辑:http://ideone.com/xR8Vp是所提算法的一个示例实现,为了使所需的输出清晰并证明它在线性时间内是可行的 - 问题是关于避免临时变量或加速它的可能性换句话说,非线性的东西并不快:)。
答案 0 :(得分:2)
您可以尝试的另一种方法是使用哈希表而不是向量来查找ID:
void subvector(std::vector<X> const& values,
std::unordered_set<int> const& ids,
std::vector<X>& out) {
out.clear();
out.reserve(ids.size());
for(std::vector<X>::const_iterator i = values.begin(); i != values.end(); ++i) {
if(ids.find(i->id) != ids.end()) {
out.push_back(*i);
}
}
}
这是以线性时间运行的,因为unordered_set::find
是恒定的预期时间(假设我们没有问题哈希值)。但是我怀疑它在实践中可能没有你最初使用矢量描述的方法那么快。
答案 1 :(得分:1)
由于您的向量已经排序,并且您希望它的子集以相同的方式排序,我假设我们可以在不重新排列的情况下切出所需的块。
为什么不只使用find_if()两次。一旦找到你想要的范围的开始,一次找到范围的结束。这将为您提供子向量的开始和结束迭代器。使用这些迭代器构造一个新的向量。其中一个向量constructor重载需要两个迭代器。
那个或partition算法应该有用。
答案 2 :(得分:0)
如果我理解你的问题,你实际上会尝试创建一个线性时间排序算法(取决于数字M的输入大小)。 那是不可能的。
您当前的方法是获得可能值的排序列表。 这需要线性时间到可能值N的数量(理论上,假设地图搜索花费O(1)时间)。
您可以做的最好的事情是使用快速排序方法(O(MlogM)fe quicksort,mergesort等)对值(您从地图中找到)进行排序,以获得较小的M值,并且可以对较大的值进行线性搜索M.的价值观 例如,如果N为100000且M为100,则使用排序算法要快得多。
我希望你能理解我说的话。如果您仍有疑问,我会尝试回答:)
编辑:(评论) 我会进一步解释我的意思。 假设您知道您的号码范围是1到100。 你把它们排序到某个地方(实际上它们是“自然”排序的)你想要以排序的形式得到它们的一个子集。 如果可以比O(N)或O(MlogM)更快地执行,则排序算法将仅使用此方法进行排序。
F.e。通过拥有一组数字{5,10,3,8,9,1,7},知道它们是有序数字集的子集{1,2,3,4,5,6,7,8 ,9,10}你仍然不能比O(N)(N = 10)或O(MlogM)(M = 7)更快地对它们进行排序。