是否有用于查找列表项联合的标准算法

时间:2014-01-07 22:03:26

标签: c++ algorithm

我不知道该怎么称呼这个算法,所以这部分原因我不确定它是否存在。

(注意:我正在使用一些宏来简化输入以用于说明目的)

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的项目都意味着该项目有自己的条目。

2 个答案:

答案 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),在Ae st A中的所有e & v != 0中找到相应的集合)。
  • 加入这些元素e1,e2,...:从A移除它们,然后将e1|e2|...添加到A

最后,A代表所有集合。您可以在e s.t中找到a,对每个项A进行分类。 a & e == e