任意树的DFS排序的比较函数

时间:2013-11-15 03:26:38

标签: algorithm tree

想象一个完整的二叉树,其中每个深度级别的节点从左到右编号。

  • 节点1有2个孩子和3个孩子。
  • 节点2有4个孩子和5个孩子。
  • 节点3有6个孩子和7个孩子。

在深度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遍历是一种方法,但我更喜欢不需要计算和存储该信息的数学解决方案。

5 个答案:

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

如果您注意到该模式,可以尝试以下操作。我不确定,它可能需要一些改进,但它给你一些想法进一步探讨。

  1. 对于每个节点,找到小于该值的最高2的幂。例如,如果我们比较7和13,那么对于7,它将是4,对于13,它将是8。

  2. 现在计算两个值之间的差值以及它们各自的2的幂。对于7,它将是3和13,它将是5.

  3. 如果两个的力量相同,则差异较小的一个应位于差异较大的那个之前。

  4. 对于我们的情况,计算与7相同的行中的13的父级。这将是13/2 ^(7到13之间的级别的差异)。

  5. 因此,父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操作符来获取离散值。