我有一个整数列表,例如1,2,2,3,4,1
。我需要能够检查不同列表之间的等价性(==)。
但是,我并不是指一个简单的数字比较。这些列表中的每一个实际上表示集合分区,其中列表中的位置表示元素的索引,而数字表示组的索引。例如,在前者中,元素0和元素5在同一组中,元素1和2在同一组中,元素3和4都在它们各自的组中。 该组的实际索引并不重要,只有分组。
我需要能够在这个意义上测试等价,所以例如前面的列表相当于5,3,3,2,9,5,
,因为它们具有相同的分组。
我这样做的方法是将数组缩减为一种正常形式。我发现所有数字都与第一个数字具有相同的值,并将这些全部设置为0。 然后我继续在列表中找到一个新的数字,找到相同值的所有数字,并将它们全部设置为1.我继续这样。
在我的示例中,两个数字都会减少到会减少到0,1,1,2,3,0
,当然我可以只使用一个简单的比较来查看它们是否相同。
然而这很慢,因为我必须在列表上进行几次线性传递。那么为了减少追逐,是否有更有效的方法将这些数字减少到这种正常形式?
然而,更一般地说,我可以一起避免这种减少,并以不同的,可能更有效的方式比较数组吗?
实施细节
答案 0 :(得分:18)
尝试并行迭代这两个序列,将第一个数组中的值(std::map
或数组)保持为第二个数组中的值,反之亦然。如果你找到一个不在你表中的一对,那么添加它,除非表中有第一个或第二个数字的东西(因为这表明不平等)。例如:
1,2,2,3,4,1
5,3,3,2,9,5
您将添加1-> 5,2-> 3,3-> 2和4-> 9,并且比较将通过。对于略有不同的东西:
5,3,3,2,9,5
1,2,2,3,2,1
你会添加5-> 1,3-> 2,2-> 3,然后9-> 2会失败,因为在第二个序列中已经存在2的结合;因此,你会知道序列不相等。
对于创建哈希函数,您可能需要进行正在进行的规范化,但它只需要一次遍历序列。同样,保持两个方向的地图,但如果在输入序列中找到未知元素,则将其映射到下一个可用数字,否则使用地图将输入序列转换为标准化序列。
答案 1 :(得分:2)
对于K
符号的字母和这些符号的N
数组,您应该能够生成签名(或规范表示O(N)
中数组的em>),使用哈希表,或O(N log K)
使用二叉搜索树。
诀窍是在一次传递中执行所有数字的转换:
std::unordered_map<std::size_t,std::size_t> map;
std::vector<std::size_t> signature;
signature.reserve(array.size());
for (std::size_t i: array) {
// insert only inserts if they key is not already present
// it returns std::pair<iterator,bool> with iterator pointing
// to the pair {key: i, value: index}
size_t index = map.insert({i, map.size()}).first->second;
signature.push_back(index);
}
然后,数组的散列是其签名的散列。
但更重要的是,没有理由不将所有数组一次性放入规范表示 。
答案 2 :(得分:1)
您可以覆盖哈希算法并创建一个唯一编码分组的哈希键。这样,当每个数组插入到散列表中时,具有相同组编码的所有数组将被链接到相同的散列位置。插入所有数组后,阵列将已经分组。
可能的编码可能是:[1 2 2 3 4 1]将散列到162345.(嗯...抱歉编码是非唯一的)。
我们需要一个独特的编码,因此我们需要记录数组中分组的位置和计数。那怎么样呢?
[1 2 2 3 4 1] - &gt; 1622324151(来自 从左到右,随后是群体指标 通过设置基数)
[5 5 5 9 9 9] - &gt; 12334563
[1 2 3 4 5 6] - &gt; 112131415161
我确信有更聪明的编码,但这将是一个非常快速的哈希。
保
答案 3 :(得分:0)
如果您知道最大可能的“组”,那么您可以执行类似的操作(伪代码,但您应该能够弄明白:)
for i = 0; i < listLength; i++
if !mapping[list1[i]]
mapping[list1[i]] = list2[i]
if mapping[list1[i]] != list2[i]
return false;
return true;