寻找算法来分组相似的数据

时间:2011-05-23 19:12:48

标签: php algorithm

一个简单的问题,但答案一直折磨着我......

我输入了一个包含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)

简单的换向逻辑,但我找不到用代码处理它的方法!任何指南都将非常感谢!!

4 个答案:

答案 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

有一些示例算法可以解决维基百科页面上的问题。维基百科是否足够清晰?