是否有任何保持位置的方法来展平二叉树?

时间:2016-01-28 14:03:15

标签: algorithm data-structures tree language-agnostic binary-tree

简短版:

It is possible将2D空间展平为1D空间,这样如果2D空间上的2个点靠近在一起,那么它们与1D的映射也将靠近在一起。类似地,有没有办法将二叉树中的任意位置映射到数组中的平面索引,这样如果两个节点在树上靠近在一起,那么它们将在阵列上靠近在一起?

长版:

Tree成为标记二叉树的类型。

               A 
            /     \
           B       C
          / \     / \
         D   E   F   G
        / \ / \ / \ / \
        H I J K L M N O

Tree中的位置可由位字符串给出,0表示go left1表示go right,空字符串为root (A)。例如,011指向上方树上的K。让两个节点之间的distance成为从一个节点到另一个节点所需的步数。

从节点位置(位串)到自然数的地图F :: BitString -> Nat是什么?如果distance(A, B)很小,那么|F(A) - F(B)|很小?

3 个答案:

答案 0 :(得分:1)

也许你想看看空间填充曲线。最简单的是z-curve(也称为Morton顺序),它可以通过将x和y坐标的位交织成称为z值的东西来创建。如果两个2D点的结果z值具有小的距离,则原始点也不远处。遗憾的是,反过来并非总是如此,有些例外情况是相邻的2D点得到非常不同的z值。

使用Hilbert curves可以实现更好的接近度(小distance(A,B) - >小|F(A)-F(B)|的可能性,但是它们更难以计算。

对于这两种算法,有一些位级别的黑客可以非常快速地计算它们,例如参见优秀的Hacker's delight书(我不是附属的,我很喜欢它)。

答案 1 :(得分:1)

如果要平整具有已知高度的树,则基本方法可以是交错节点。对于上面的示例,这将导致具有[H,D,I,B,J,E,K,A,...]的数组,依此类推。接近度并不理想,但可能尽可能接近。要计算您可以按如下方式执行的位置。 让我们说h是节点的高度,从0(底部:H,I,J,K,..)到3(顶部:A)计数。如果您按照描述对节点进行编码,即H=000,B=00,I=001,,那么数组位置将为pos(node)=value(node)*2 + (2^h)-1。例如pos(H=000)=0pos(B=00)=1pos(I=001)=2,...

显然,增加树的高度最好通过在“A”上方插入节点来实现,否则你必须重新映射所有节点。

修改 对于顶级节点(A / B),该算法的局部性要求当然不是很好,但对于大多数较低级别的节点(K / L除外)相当好。这真的取决于什么类型的地方是重要的。如果高级节点的位置很重要,那么问题评论中的ABCDEFG排序可能是最好的。

我不认为有一个完美的解决方案适用于所有情况。

答案 2 :(得分:1)

我认为TilmannZ's answer(使用中缀排序)与它将获得的一样好。考虑到二叉树中除了根之外的每个节点都有三个邻居,而在一维内存布局中,只有两个节点实际上距离为1。此外,没有多少其他有用的排序可供考虑。

我写了一个小Perl script,它将默认排序与深度优先前缀排序和中缀排序进行比较。您可以将输出放在Gnuplot以获得以下图表(编辑:包括van-Emde-Boas布局): plot showing the memory locality of different node orderings

您可以看到原始排序对于小树距离具有相对较差的局部性,并且前缀和中缀排序都更好。然而,中缀排序产生三者的最佳行为(例如,树距离为3的节点平均分开6个存储器索引)。它也是一条几乎线性的曲线,应该是一个很好的指标,你不能做得更好。 V.E.B.根据您的距离标准,评论中提到的布局至少对于较小的节点距离不是很好。

为了完整起见,这里是我使用的(有点快速和肮脏)脚本:

#!/usr/bin/perl
# Compares node distances in memory for different layouts of the following
# binary tree:
#                      A
#                  /       \
#              B               C
#            /   \           /   \
#          D       E       F       G
#         / \     / \     / \     / \
#        H   I   J   K   L   M   N   O
#       / \ / \ / \ / \ / \ / \ / \ / \
#       P Q R S T U V W X Y Z 0 1 2 3 4

# Calculates the real node distance.
sub dist($$)
{
  my ($i, $j) = @_;
  return 0 if ($i == $j);
  ($j,$i) = ($i,$j) if ($i > $j);
  # $i<$j. Go one up from the larger node.
  return 1 + dist($i, ($j-1)>>1);
}

# Determines the average memory distance for nodes as a function of tree distance.
sub get_dists($$)
{
  my $tree = shift;     # Original tree ordering
  my $locality = shift; # Locality-based ordering

  my %dists = ();
  my %counts = ();

  for (my $i=0; $i<length($tree); $i++)
  {
    for (my $j=$i; $j<length($tree); $j++)
    {
      # Find location of $i and $j in the locality-based ordering.
      my $ni = substr($tree, $i, 1);
      my $nj = substr($tree, $j, 1);
      my $it = index($locality, $ni);
      my $jt = index($locality, $nj);
      my $d = (($i <= $j) ? ($j-$i) : ($i-$j));
      my $dt = (($it <= $jt) ? ($jt-$it) : ($it-$jt));
      my $treedist = dist($i, $j);
      $dists{$treedist} += $dt;
      $counts{$treedist}++;
    }
  }
  foreach my $k (sort keys %counts)
  {
    $dists{$k} /= $counts{$k};
  }
  return %dists;
}

my $tree = "ABCDEFGHIJKLMNOPQRSTUVWXYZ01234";    # original (breadth-first prefix) ordering
my $prefix = "ABDHPQIRSEJTUKVWCFLXYMZ0GN12O34";  # depth-first prefix ordering
my $infix = "PHQDRISBTJUEVKWAXLYFZM0C1N2G3O4";   # infix ordering
my $veb = "ABCDHPQIRSEJTUKVMFLXYMZ0GN12O34";     # V.E.B. layout (hope I got it right)

my %original_dists = get_dists($tree, $tree);
my %prefix_dists = get_dists($tree, $prefix);
my %infix_dists = get_dists($tree, $infix);
my %veb_dists = get_dists($tree, $veb);

print "set key top left;\n";
print "set title 'Locality ratios';\n";
print "set xlabel 'tree distance';\n";
print "set ylabel 'avg. memory distance';\n";

print "plot '-' w lp title 'original', '-' w lp title 'prefix ordering', '-' w lp title 'infix ordering', '-' w lp title 'van-Emde-Boas layout';\n";

foreach my $k (sort keys %original_dists)
{
  print "$k $original_dists{$k}\n";
}
print "e\n";
foreach my $k (sort keys %prefix_dists)
{
  print "$k $prefix_dists{$k}\n";
}
print "e\n";
foreach my $k (sort keys %infix_dists)
{
  print "$k $infix_dists{$k}\n";
}
print "e\n";
foreach my $k (sort keys %veb_dists)
{
  print "$k $veb_dists{$k}\n";
}
print "e\n";