我不知道该怎么称呼这个算法,所以这部分原因我不确定它是否存在。
(注意:我正在使用一些宏来简化输入以用于说明目的)
std::vector<BYTE> itemlist;
itemlist.push_back( 1000 );
itemlist.push_back( 0001 );
itemlist.push_back( 0001 );
itemlist.push_back( 0010 );
itemlist.push_back( 0100 );
itemlist.push_back( 0011 );
itemlist.push_back( 0001 );
itemlist.push_back( 1000 );
std::list<std::vector<int>> results = Confab(itemlist);
预期结果:
{ [ 1,2,3,5,6], [4], [0,7] }
所以我使用BYTE作为一个位掩码来找到成员的联合。关键是我也在更新面具。因此,第1项和第2项没有任何共同之处,但第5项与之合并。
我正在寻找有关如何以最少的通行证执行此操作的任何见解。在现实世界中我使用的是DWORD。
编辑我忘了提及任何带有0000的项目都意味着该项目有自己的条目。
答案 0 :(得分:2)
从一个列表指针数组开始,该列表指针数量等于你拥有的位数,包括从中开始的空指针。
对于每个元素:
如果没有设置与这些位对应的数组元素,则创建一个新列表并将所有数组元素设置为指向列表。
否则,对于每个初始化的数组元素,合并列表,使指针指向新列表。同时将所有未初始化的数组元素设置为新列表。
将自动递增的值添加到新列表中。
浏览数组,输出所有唯一列表(仅处理唯一列表,您可以在处理完列表后设置一些指标,因此不要处理两次)。
哦,对了,我们这里也需要union-find - 否则合并列表可能会留下一些指向旧列表的其他元素。
关于union-find算法的更多内容:
此算法使用森林(一组树数据结构),但不是父母指向子项的标准树格式,而是指向父项,而父项可以包含任意数量的子项。我们从一个只包含每个元素的单个元素的树开始(因此,在我们的例子中,每当我们创建一个新列表时,我们都会创建一个新集合)。根可以被认为是整个树所属的集合的指示符。要合并两个集合,我们将其中一个根指向另一个根,因此树将在另一个根下合并。
在我们的例子中,我们让每个根也存储一个元素列表。每当我们想要合并上面指定的列表时,我们首先迭代每个树以查看它属于哪个集合,并在它们属于不同集合时合并集合(及其列表),否则不执行任何操作。
这只是一个基本的描述union-find,如果你在努力理解这里的一些复杂性,你可能还需要阅读它。
运行时间分析:
您可以在固定时间内合并列表(请参阅list::splice
)。对于32位,总共不会有超过31个合并,因为永远不会有比列数更多的集合,因此列表,所以你只需要进行检查。
union-find算法增加的复杂性在这里应该可以忽略不计,特别是如果你使用一些众所周知的优化。
这(如果正确实现)是位数乘以输入元素数量的线性,你不能比(asymptotically说话)更好,因为你至少需要看一下每一位输入中的每个元素。
示例:(为简单起见,未显示union-find)
4位,因此是4个列表的数组。
[NULL,NULL,NULL,NULL]
处理1000
。第一位未设置,因此请创建一个包含0
的新列表,并使该元素指向它。
[0]
^
|
[ ,NULL,NULL,NULL]
处理0001
。最后一位未设置,因此请创建一个包含1
的新列表,并使该元素指向它。
[0] [1]
^ ^
| |
[ ,NULL,NULL, ]
处理0001
。最后一位已设置,因此只需添加2
。
[0] [1,2]
^ ^
| |
[ ,NULL,NULL, ]
处理0010
。未设置倒数第二位,因此创建一个包含3
的新列表,并使该元素指向它。
[0] [3] [1,2]
^ ^ ^
| | |
[ ,NULL, , ]
处理0100
。第二位未设置,因此请创建一个包含4
的新列表,并使该元素指向它。
[0] [4] [3] [1,2]
^ ^ ^ ^
| | | |
[ , , , ]
处理0011
。设置倒数第二位和最后一位,因此合并它们并添加5
。
[0] [4] [1,2,3,5]
^ ^ ^ ^
| | | |
[ , , , ]
处理0001
。最后一位已设置,因此只需添加6
。
[0] [4] [1,2,3,5,6]
^ ^ ^ ^
| | | |
[ , , , ]
处理1000
。第一位已设置,因此只需添加7
。
[0,7] [4] [1,2,3,5,6]
^ ^ ^ ^
| | | |
[ , , , ]
现在我们根据需要提供了3个列表。
答案 1 :(得分:0)
'不相交集'是一种非常通用的方法(http://en.wikipedia.org/wiki/Disjoint-set_data_structure)。这里也可以使用一些改编。
一种可能的方法:
A = {1000, 0100, 0010, 0001}
- 每个元素代表一个集合,在开始时所有集合都是不相交的。v
(例如1010
),在A
(e
st A
中的所有e & v != 0
中找到相应的集合)。e1,e2,...
:从A
移除它们,然后将e1|e2|...
添加到A
。最后,A
代表所有集合。您可以在e
s.t中找到a
,对每个项A
进行分类。 a & e == e
。