想象一个完整的二叉树,其中每个深度级别的节点从左到右编号。
等
在深度D处将有2 ^ D个节点,数字为2 ^ D ... 2 ^(D + 1)-1
任何深度的完整树的深度优先搜索遍历都是确定性的。
例如,将始终遍历深度为4的树: 1,2,4,8,9,5,10,11,3,6,12,13,7,14,15。
我正在寻找一种方法来对数字列表进行排序,以便它们落入任何树的DFS遍历中。
特别是,我想要一个比较函数,它可以取两个数字并确定哪个在DFS遍历中首先出现。
有什么想法吗?
为某些最大树大小预先计算DFS遍历是一种方法,但我更喜欢不需要计算和存储该信息的数学解决方案。
答案 0 :(得分:1)
具有最佳性能的算法将是FUD建议的算法,因为您只需要遍历树一次,然后比较将只是O(1)
。
但是如果你不想遍历整个树,只想要一个比较器,那就有一个O(log n)
比较器(可以优化到O(log log n)
,或者实际上O(1)
)。
这个想法是:
观察1:如果两个节点处于相同深度,则稍后将遍历编号较高的节点。
观察2:如果两个节点不在相同的深度上,通过注意父元素总是在后代之前被访问,我们采用与较浅节点相同深度的较深节点的祖先。然后使用观察1进行比较。
在完整的二叉树中使用您的数字系统,您可以通过n
获取节点n/2
的父级。因此,在获得每个节点的深度(可以在O(log n)
或预先计算中完成)之后,比如d1 < d2
,我们将更深的节点与2^(d2-d1)
分开(可以在{{中完成) 1}},在这种情况下O(log p)
是p
,所以它是O(log n)
)。然后看看哪一个更大=)
在C ++中:
O(log log n)
答案 1 :(得分:1)
这是@ Abhishek答案的C实现:
//returns -1 if a before b; 0 if same; else 1
int treesort(unsigned int a,unsigned int b)
{
int diff, swap=1, side=0;
unsigned int ra, rb;
if (a==b) return 0;
//ensure deeper node is always in b.
if (b<a) { int t=a;a=b;b=t;swap=-1;}
//treat 0 as before everything else
if (a==0) return -1*swap;
//clear all but the msb
ra=base(a); rb=base(b);
//move up to same level, tracking child side
while (rb!=ra) { side=b&1;rb/=2;b/=2; }
//compare parents at same level
diff = (b-rb)-(a-ra);
//if same parent, answer depends on side.
if (diff==0) diff = side;
//restrict to [-1,1], be sure to handle swap
return (diff>0?-1:1)*swap;
}
base
是一个清除除最高位之外的所有函数的函数。我用测试了
int base(unsigned int x){return 1<<(msb32(x)-1);}
使用Sir Slick's msb32()中的this question。
答案 2 :(得分:0)
您可以预先计算完整树中的最大深度。并为每个节点分配一组增加的值,例如,在你的深度4树
v[1]=1, v[2]=3, v[4]=3 ...
然后比较函数就是
int cmp(i,j):
return v[i]<v[j]
答案 3 :(得分:0)
如果您注意到该模式,可以尝试以下操作。我不确定,它可能需要一些改进,但它给你一些想法进一步探讨。
对于每个节点,找到小于该值的最高2的幂。例如,如果我们比较7和13,那么对于7,它将是4,对于13,它将是8。
现在计算两个值之间的差值以及它们各自的2的幂。对于7,它将是3和13,它将是5.
如果两个的力量相同,则差异较小的一个应位于差异较大的那个之前。
对于我们的情况,计算与7相同的行中的13的父级。这将是13/2 ^(7到13之间的级别的差异)。
因此,父13 = 13/2 ^ 1 = 6 从6&lt; 7,我们可以说13在7之前。
编辑:按照建议,删除了不必要的第3步。
答案 4 :(得分:0)
这是问题的数学解决方案,但我无法得到等式的闭合形式: -
在总节点N的树中查找节点k的DFS索引: -
DFS-order(k) = DFS-order((k-1)/2) + 2^(log(N+1) - log((k-1)/2) - 1) if k is odd
DFS-order(k) = DFS-order(k/2) + 1 if k is even
Base Case: DFS-order(1) = 0
您可以找到上述等式的封闭形式,我认为这是更高级别的数学。
说明: -
对于奇数节点,我们首先遍历父节点的所有左子节点,再加上新索引的1节点。我们知道左子树有完整二叉树的一半节点,这些节点的根节点不包括父节点。完整BT中的总节点为2^d - 2
,不包括根。左子树有一半2^(d-1) - 1
。 d是父树的树根深度。以节点k为根的树的深度为Total depth - log(k)
。总深度为log(N+1)
。因此,左子树中的节点数为2^(log(N+1) - log((k-1)/2) - 1) - 1
。我们为父节点的另一个遍历添加1到当前节点final sum = 2^(log(N+1) - log((k-1)/2) - 1)
。我们将其添加到父索引以获取当前节点索引DFS-order(k) = DFS-order((k-1)/2) + 2^(log(N+1) - log((k-1)/2) - 1)
。
对于Even节点,它的索引是父索引+ 1
是微不足道的注意:公式可以在O(log(k))时间和时间中找到节点k的索引。 log函数使用floor操作符来获取离散值。