一个简单的问题,但答案一直折磨着我......
我输入了一个包含2个别名的数组(php), 让我们说:
Array(
Array(1,5),
Array(6,8),
Array(6,1),
Array(9,3),
)
每个状态“1”与“5”相同,“6”与“8”相同,...... 很简单,现在我需要分组那些,看看上面的例子,算法应该给我,如果我问得好,两组:
Array(1,5,6,8) and Array(9,3)
简单的换向逻辑,但我找不到用代码处理它的方法!任何指南都将非常感谢!!
答案 0 :(得分:2)
您可以使用union-find算法快速完成此操作。
这个想法是拥有一片树林,每棵树代表“相等”的元素。您将此树表示为一个简单数组,其中a [i]可以是i的父项,如果i是根,则为-1,如果i尚未存在于树中,则为-2。
在你的情况下,你将从简单的树开始:
1
\
5
接下来你希望它加入6和8.但是,它们都是未分配的,所以你要添加一个新树。 (那是[6] = - 1,a [8] = 6):
1 6
\ \
5 8
现在你要加入6和1.你可以通过跟随他们的父母到达顶部来找出他们属于哪个集合。巧合的是,它们都是根源。在这种情况下,我们将最小的树作为另一棵树的孩子。 (A [6] = 1)
1
/ \
6 5
\
8
最后我们要加入9和3,它们都是未分配的,所以我们创建了一个新的树。 (a [3] = - 1,a [9] = 3)
1 9
/ \ \
6 5 3
\
8
说你还有5 = 3?跟随他们的父母,直到你到达根。您发现它们已经不相等,因此您将合并树。由于9控制一个不太高的树,我们将它添加到一棵树,得到:
.1.
/ | \
6 9 5
\ \
8 3
如您所见,现在可以轻松检查两个元素是否在同一个集合中。假设您想测试8和3是否相等?只要按照他们的路径向上,你会看到8在一个由1代表的集合中,而在由9代表的集合中有3个。因此它们是不相等的。
使用始终将较小的树放在较大的树下的启发式,给出log(n)平均查找或合并时间。维基百科的文章解释了一个额外的技巧,这将使它基本上保持不变。
答案 1 :(得分:1)
我会从这些构建某种树,并使用着色来分离组件。 例如,令G = [E,V],E = {1,5,6,7,9},V = {{1,5},{6,8},{6,1},{9,3其中G是具有V顶点和E边的图。现在从一个随机顶点开始,而不是递归地将它的所有颜色与颜色C1相邻(使用广度优先搜索)。如果你找不到新的邻居,你就得到了第一组。现在从一个新的无色顶点和一个新的颜色C2开始。重复此操作,直到没有顶点为止。
答案 2 :(得分:1)
<?php
class AliasesFinder
{
/** @var array */
protected $chains;
/** @var array */
protected $aliases;
protected $digits;
public function __construct($aliases)
{
$this->aliases = $aliases;
//collect all digits
$digits = array();
foreach ($this->aliases as $alias_pair)
{
if (!in_array($alias_pair[0], $digits)) $digits[] = $alias_pair[0];
if (!in_array($alias_pair[1], $digits)) $digits[] = $alias_pair[1];
}
$this->digits = $digits;
}
public function find_all_aliases($digit, &$chain = array())
{
//if $digit already in some chain - return, don't spend time to count another time
if (!empty($this->chains))
{
foreach ($this->chains as $existing_chain)
{
if (in_array($digit, $existing_chain)) return false;
}
}
//$digit is part of chain already
$chain[] = $digit;
foreach ($this->aliases as $alias_pair)
{
//if alias of digit not in chain yet - add this alias-digit and all aliases of this alias-digit to chain
if ($digit==$alias_pair[0] && (empty($chain) || !in_array($alias_pair[1], $chain)))
{
$this->find_all_aliases($alias_pair[1], $chain);
}
elseif ($digit==$alias_pair[1] && (empty($chain) || !in_array($alias_pair[0], $chain)))
{
$this->find_all_aliases($alias_pair[0], $chain);
}
}
return $chain;
}
public function getChains()
{
foreach ($this->digits as $digit)
{
$aliases = $this->find_all_aliases($digit);
if ($aliases!==false) $this->chains[] = $aliases;
}
return $this->chains;
}
}
$aliases = Array(
Array(1, 5),
Array(6, 8),
Array(6, 1),
Array(9, 3),
);
$finder = new AliasesFinder($aliases);
print_r($finder->getChains());
答案 3 :(得分:0)
这是一个不相交的集合联合问题的实例。
维基百科有一个关于它的页面:
http://en.wikipedia.org/wiki/Disjoint-set_data_structure
有一些示例算法可以解决维基百科页面上的问题。维基百科是否足够清晰?