简短版:
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 left
,1
表示go right
,空字符串为root (A
)。例如,011
指向上方树上的K
。让两个节点之间的distance
成为从一个节点到另一个节点所需的步数。
从节点位置(位串)到自然数的地图F :: BitString -> Nat
是什么?如果distance(A, B)
很小,那么|F(A) - F(B)|
很小?
答案 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)=0
,pos(B=00)=1
,pos(I=001)=2
,...
显然,增加树的高度最好通过在“A”上方插入节点来实现,否则你必须重新映射所有节点。
修改强> 对于顶级节点(A / B),该算法的局部性要求当然不是很好,但对于大多数较低级别的节点(K / L除外)相当好。这真的取决于什么类型的地方是重要的。如果高级节点的位置很重要,那么问题评论中的ABCDEFG排序可能是最好的。
我不认为有一个完美的解决方案适用于所有情况。
答案 2 :(得分:1)
我认为TilmannZ's answer(使用中缀排序)与它将获得的一样好。考虑到二叉树中除了根之外的每个节点都有三个邻居,而在一维内存布局中,只有两个节点实际上距离为1。此外,没有多少其他有用的排序可供考虑。
我写了一个小Perl script,它将默认排序与深度优先前缀排序和中缀排序进行比较。您可以将输出放在Gnuplot以获得以下图表(编辑:包括van-Emde-Boas布局):
您可以看到原始排序对于小树距离具有相对较差的局部性,并且前缀和中缀排序都更好。然而,中缀排序产生三者的最佳行为(例如,树距离为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";