C ++:2个数组之间的差异
Posted
技术标签:
【中文标题】C ++:2个数组之间的差异【英文标题】:C++: Differences between 2 arrays 【发布时间】:2014-08-21 15:20:52 【问题描述】:我有两个单一简单元素类型的未排序随机访问数组(int/string/等,所有比较运算符也是如此,可以散列等)。两个数组中不应有重复的元素。
寻找给定这些数组 A 和 B 的通用算法会告诉我:
-
A 和 B 中都有哪些元素
哪些元素在 A 而不是 B
哪些元素在 B 中但不在 A 中
我想我可以使用下面的集合运算符来做到这一点,但是否有更快的解决方案(例如不需要我构建两个排序集的解决方案)?
r1 = std::set_intersection(a,b);
r2 = std::set_difference(a,b);
r3 = std::set_difference(b,a);
【问题讨论】:
99% 确定最好的解决方案是对两个数组进行排序,这将花费你 O(nlog(n)),我看到的任何其他解决方案都是 O(n^2) 然后使用那三个函数?或者有更好的算法来考虑所有三种情况(O(min(a.size(),b.size()) 甚至可能)? 过早的优化是万恶之源。您是否分析过您的应用程序并发现这些操作是您的程序出现瓶颈的原因? 不要使用std::set
:对向量进行排序。从理论上讲,您只需在源向量上运行一个循环就可以稍微快一点,但我怀疑这种差异是否重要。
如果没有范围限制的查找表,我相信 O(NlogN) 是您从中获得的最好结果,假设您按照 James 的建议花时间对两个范围进行排序。如果可以使用范围表(通常用于较小的域,例如 char
或 short
,无论是否签名),它在 O(N+M) 中是可行的,但听起来不像是适合您的数据。
【参考方案1】:
首先,从您的问题中不清楚您的意思是
std::set
当您谈到排序集时。如果是这样,那么您的
第一反应应该是使用std::vector
,如果可以的话,就
原始向量。只需对它们进行排序,然后:
std::vector<T> r1;
std::set_intersection( a.cbegin(), a.cend(), b.cbegin(), b.cend(), std::back_inserter( r1 ) );
r2
和 r3
也是如此。
除此之外,我怀疑你能做的还有很多。只有一个 循环可能会改善一些事情:
std::sort( a.begin(), a.end() );
std::sort( b.begin(), b.end() );
onlyA.reserve( a.size() );
onlyB.reserve( b.size() );
both.reserve( std::min( a.size(), b.size() ) );
auto ita = a.cbegin();
auto enda = a.cend();
auto itb = b.cbegin();
auto endb = b.cend();
while ( ita != enda && itb != endb )
if ( *ita < *itb )
onlyA.push_back( *ita );
++ ita;
else if ( *itb < *ita )
onlyB.push_back( *itb );
++ itb;
else
both.push_back( *ita );
++ ita;
++ itb;
onlyA.insert( onlyA.end(), ita, enda );
onlyB.insert( onlyB.end(), itb, endb );
reserve
可能会有所作为,除非大多数
元素最终在同一个向量中,可能不会花费太多
额外的内存。
【讨论】:
为简洁起见,清理 while 循环可以写成对onlyA.insert(onlyA.end(), ita, enda);
的调用,对于 B 也是如此。
@Novelocrat 好点。我实际上是在转录我用 Python 编写的代码,而这种可能性不存在;这显然是一个更好的解决方案。
如果 |A|,这可能是最快的解决方案+ |B|相对较小(例如,小于 25 或其他东西)。不过,在某些时候,O(n) 解决方案将击败 O(n log(n)) 解决方案。
@jxh 你的意思是保留专门设计的散列结构?过去,我所做的测量表明std::unordered_map
和std::map
之间的差异在您有几千个条目之前并没有真正开始产生影响。 (当然,只有这样,如果你有一个好的散列函数。)在这种情况下,std::vector
更好的位置可能意味着在std::unordered_map
开始获得回报之前你需要数十万个条目。与否:我们实际上必须测量才能确定。
我不是指代表集合的数据结构,我只是指你的算法与我的比较。【参考方案2】:
类似下面的算法将运行 O(|A|+|B|)(假设 unordered_map
的行为为 O(1)):
onlyA
最初包含所有 A,而列表 onlyB
和 bothAB
开始时为空。
让哈希表Amap
将onlyA
中的元素与onlyA
中对应的迭代器关联起来。
对于B
中的每个元素b
如果b在Amap中找到对应的迭代器ai
将b添加到bothAB
使用 ai 从 onlyA
中删除 b
否则,将b添加到onlyB
在上述算法结束时,
onlyA 包含 A 中的元素,但 B 中不包含元素, onlyB 包含 B 中的元素,但不包含 A 中的元素, bothAB 包含 A 和 B 中的元素。下面是上面的实现。结果以元组 onlyA, onlyB
, bothAB
> 的形式返回。
template <typename C>
auto venn_ify (const C &A, const C &B) ->
std::tuple<
std::list<typename C::value_type>,
std::list<typename C::value_type>,
std::list<typename C::value_type>
>
typedef typename C::value_type T;
typedef std::list<T> LIST;
LIST onlyA(A.begin(), A.end()), onlyB, bothAB;
std::unordered_map<T, typename LIST::iterator> Amap(2*A.size());
for (auto a = onlyA.begin(); a != onlyA.end(); ++a) Amap[*a] = a;
for (auto b : B)
auto ai = Amap.find(b);
if (ai == Amap.end()) onlyB.push_back(b);
else
bothAB.push_back(b);
onlyA.erase(ai->second);
return std::make_tuple(onlyA, onlyB, bothAB);
【讨论】:
【参考方案3】:您可以通过将 A 的元素放入 unordered_map 以线性时间执行此操作,其中 A 的元素是键。检查 B 的元素是否在 map 中的键中。
【讨论】:
那将是 O(n * log(N))。这也是NOTstd::set_
函数的实现方式。
@Xarn 对不起,我的意思是 unordered_map。您对 std::set 的看法是正确的,我刚刚检查了时间复杂度。以上是关于C ++:2个数组之间的差异的主要内容,如果未能解决你的问题,请参考以下文章