答案 0 :(得分:13)
是的,你可以做得更好。
对它们进行排序(对于简单整数为O(n),一般为O(n * log n)),然后重复保证相邻,使得快速找到它们O(n)
使用哈希表,也是O(n)。对于每个项目,(a)检查它是否已经在哈希表中;如果是的话,它是重复的;如果没有,请将其放在哈希表中。
您正在使用的方法似乎进行了O(N ^ 2)比较:
for i = 0; i < length; ++i // will do length times
for j = i+1; j < length; ++j // will do length-i times
compare
因此,对于长度5,你做4 + 3 + 2 + 1 = 10比较;对于6你做15,等等(N ^ 2)/ 2 - N / 2是准确的。对于任何合理的高N值,N * log(N)都较小。
你的案件中N有多大?
就减少散列冲突而言,最好的方法是获得更好的散列函数:-D。假设这是不可能的,如果你可以做一个变体(例如,不同的modulous),你可以做一个嵌套的哈希。
答案 1 :(得分:7)
<强> 1。在最坏的情况下对数组O(n log n)进行排序 - mergesort / heapsort / binary tree sort
<强> 2。比较邻居并将匹配拉出O(n)
答案 2 :(得分:5)
保持基于散列表的结构从值到计数;如果你的C ++实现没有提供std::hash_map
(到目前为止还不是C ++标准的一部分! - ),请使用Boost或从网上获取一个版本。对集合进行一次传递(即O(N))可以进行值 - >计数映射;再一次通过哈希表(&lt; = O(N),清楚地)以识别具有计数&gt;的值。 1并适当地发射它们。总体O(N),这不是你的建议。
答案 3 :(得分:3)
您可以使用从代表元素到其他元素的列表/向量/双端队列的映射。这需要在插入容器时进行相对较少的比较,这意味着您可以在不进行任何比较的情况下迭代生成的组。
此示例始终将第一个代表性元素插入到映射的双端队列存储中,因为它使整个组的后续迭代在逻辑上变得简单,但如果此重复证明存在问题,那么仅执行push_back
将很容易if (!ins_pair.second)
。
typedef std::map<Type, std::deque<Type> > Storage;
void Insert(Storage& s, const Type& t)
{
std::pair<Storage::iterator, bool> ins_pair( s.insert(std::make_pair(t, std::deque<Type>())) );
ins_pair.first->second.push_back(t);
}
然后(相对)简单且便宜地迭代这些组:
void Iterate(const Storage& s)
{
for (Storage::const_iterator i = s.begin(); i != s.end(); ++i)
{
for (std::deque<Type>::const_iterator j = i->second.begin(); j != i->second.end(); ++j)
{
// do something with *j
}
}
}
我进行了一些比较和对象计数的实验。在以10000个对象随机顺序形成50000组(即每组平均2个对象)的测试中,上述方法花费了以下数量的比较和副本:
1630674 comparisons, 443290 copies
(我尝试降低副本数量,但只是以牺牲比较为代价,这似乎是您方案中的高成本操作。)
使用多图,并在最后一次迭代中保留前一个元素以检测组转换成本:
1756208 comparisons, 100000 copies
使用单个列表并弹出前面元素并对其他组成员执行线性搜索:
1885879088 comparisons, 100000 copies
是的,那是~1.9b的比较,相比之下,我最好的方法是~1.6m。为了使列表方法能够在最佳数量的比较附近执行,它必须进行排序,这将花费相似数量的比较,因为首先构建一个固有排序的容器。
修改强>
我使用你发布的代码并运行隐含的算法(我必须对代码做出一些假设的假设定义)和我之前使用的相同测试数据集,并且我计算了:
1885879088 comparisons, 420939 copies
即。与我的哑列表算法完全相同的比较数,但有更多的副本。我认为这意味着我们在这种情况下使用基本相似的算法。我看不到任何替代排序顺序的证据,但看起来你想要一个包含多个等效元素的组列表。这可以通过添加Iterate
子句在我的if (i->size > 1)
函数中简单地实现。
我仍然看不到任何证据表明构建一个排序容器(如此deques地图)不是一个好的(即使不是最佳的)策略。
答案 4 :(得分:1)
你尝试过排序吗?例如使用快速排序等算法?如果性能足够好,那么这将是一个简单的方法。
答案 5 :(得分:1)
如果已知它是整数列表,并且如果已知它们都在A和B之间(例如A = 0,B = 9),则创建一个BA元素数组,并创建BA容器。
在非常具体的情况下(普通整数列表),我建议你只计算它们,因为你不能区分不同的整数:
for(int i = 0; i < countOfNumbers; i++)
counter[number[i]]++;
如果 可区分,请创建一个列表数组,并将它们添加到相应的列表中。
如果它们不是数字,请使用std :: map或std :: hash_map,将键映射到值列表。
答案 6 :(得分:0)
最简单的可能就是对列表进行排序,然后迭代它以查找重复。
如果您对数据有所了解,可以使用更有效的算法。
例如,如果您知道列表很大,并且只包含1..n中的整数,其中n相当小,则可以使用一对布尔数组(或位图),并执行以下操作:< / p>
bool[] once = new bool[MAXIMUM_VALUE];
bool[] many = new bool[MAXIMUM_VALUE];
for (int i = 0; i < list.Length; i++)
if (once[ value[i] ])
many[ value[i] ] = true;
else
once[ value[i] ] = true;
现在,许多[]包含多个值被多次看到的数组。
答案 7 :(得分:0)
大多数提到哈希/无序地图解决方案的人都假设O(1)插入和查询时间,但它可能是O(N)最坏情况。此外,您还可以消除对象散列的成本。
我个人会将对象插入二叉树(每个插入一次O(logn)),并在每个节点上保留计数器。这将产生O(nlogn)构造时间和O(n)遍历以识别所有重复。
答案 8 :(得分:0)
如果我正确理解了这个问题,那么这是我能想到的最简单的解决方案:
std::vector<T> input;
typedef std::vector<T>::iterator iter;
std::vector<std::pair<iter, iter> > output;
sort(input.begin(), input.end()); // O(n lg n) comparisons
for (iter cur = input.begin(); cur != input.end();){
std::pair<iter, iter> range = equal_range(cur, input.end(), *cur); // find all elements equal to the current one O(lg n) comparisons
cur = range.second;
output.push_back(range);
}
总运行时间:O(n log n)
。
您有一个排序过程O(n lg n)
,然后是第二个过程,其中{em>每个组执行O(lg n)
比较(所以最多 {{ 1}}次,也产生n
。
请注意,这取决于输入是向量。只有随机访问迭代器在第二遍中具有对数复杂度。双向迭代器将是线性的。
这不依赖于哈希(按要求),它保留所有原始元素(而不是仅返回每个组的一个元素,以及它发生频率的计数)。
当然,可以进行一些较小的常量优化。输出向量上的O(n lg n)
是个好主意,以避免重新分配。 output.reserve(input.size())
的使用频率高得多,而且可以轻松缓存。
根据假设的群体大小,input.end()
可能不是最有效的选择。我假设它进行二进制搜索以获得对数复杂度,但如果每个组只有几个元素,则简单的线性扫描会更快。无论如何,初始排序虽然支配了成本。
答案 9 :(得分:0)
注意我在我正在使用的三重商店的规范化过程中遇到了同样的问题。我使用Allegro Common Lisp中的哈希表功能实现了Charles Bailey在Common Lisp中总结的方法3。
功能“代理人相等?”用于测试TS中的两个代理何时相同。函数“merge-nodes”合并每个集群上的节点。在下面的代码中,“...”用于删除不那么重要的部分。
(defun agent-equal? (a b)
(let ((aprops (car (get-triples-list :s a :p !foaf:name)))
(bprops (car (get-triples-list :s b :p !foaf:name))))
(upi= (object aprops) (object bprops))))
(defun process-rdf (out-path filename)
(let* (...
(table (make-hash-table :test 'agent-equal?)))
(progn
...
(let ((agents (mapcar #'subject
(get-triples-list :o !foaf:Agent :limit nil))))
(progn
(dolist (a agents)
(if (gethash a table)
(push a (gethash a table))
(setf (gethash a table) (list a))))
(maphash #'merge-nodes table)
...
)))))
答案 10 :(得分:0)
自C ++ 11以来,STL使用std::unordered_map提供了哈希表。
所以O(N)解决方案是将您的值放入unordered_map< T, <vector<T> >
。