具有重复值的大型二叉树中两个值之间的最小距离

时间:2013-11-24 04:47:29

标签: algorithm binary-tree

给定可能包含重复值的二叉树,您需要找到两个给定值之间的最小距离。请注意,二叉树可能很大。

例如:

        5
      /   \
    1       7
   / \     / \
  4   3   8   2
 / \
1   2

该函数应返回2(1和2作为输入) (如果没有重复项,我们可以找到LCA,然后计算距离。)

我编写了以下代码,但是当不同的子树和以下情况中存在值时,我无法处理这些情况:

  1. root = 1,root.left = 4,root.left.left = 3,root.left.right = 2,root.left.left.left = 1
  2. root = 1,root.left = 4,root.left.left = 3,root.left.left.left = 1,root.left.left.right = 2
  3. void dist(struct node* root,int& min,int n1,int n2,int pos1,int pos2,int level) {
        if(!root)
            return;
        if(root->data==n1){
            pos1 = level;
            if(pos2>=0)
                if(pos1-pos2 < min)
                    min = pos1-pos2;
        }
        else if(root->data==n2){
            pos2 = level;
            if(pos1>=0)
                if(pos2-pos1 < min)
                    min = pos2-pos1;
        }
        dist(root->left,min,n1,n2,pos1,pos2,level+1);
        dist(root->right,min,n1,n2,pos1,pos2,level+1);
    }
    

    我认为在每个节点我们都可以找到该节点是否为值的LCA。如果该节点是LCA,那么找到距离并相应地更新min,但这将需要O(n 2 )。

3 个答案:

答案 0 :(得分:2)

以下是解决问题的算法: -

遍历所有树并使用二进制字符串表示计算每个节点的路径并存储到哈希映射

例如。对于您的树,hashmap将是

1 => 0,000
2 => 001,11
3 => 01
...

当查询(u,v)之间的距离时,检查每对并计算它们之间的距离。从字符串中删除公共前缀,然后将剩余长度相加

eg. u=1 and v=2

distance(0,001) = 2
distance(0,11) = 3
distance(000,001) = 2
distance(000,11) = 5 

min = 2

注意:我认为第二步可以提高效率,但需要做更多的研究

答案 1 :(得分:1)

您可以通过计算LCA(x1,LCA(x2,LCA(x3 ...)来计算一组节点的LCA,并且该组中的所有节点都将低于此LCA。如果比较两个LCA一组节点和一个不直接在另一个之下,那么不同组中任何两个节点之间的最小距离将至少是LCA之间的距离。如果一个LCA高于另一个,则最小距离可以为零。

这允许一种分支定界方法。到目前为止,每个点都有一个最佳的最小距离(初始化为无穷大)。给定两组节点,使用它们的LCA计算出它们最小距离的下限并丢弃它们,如果这并不比目前为止的最佳答案好。如果没有丢弃,则根据集合中的每个节点是在LCA的左侧,LCA的右侧,还是LCA,将每个集合分成两个加上一个可能的单个集合。递归检查(最多九对)拆分集中的最小距离。如果一对中的两个分裂都低于某个最小大小,那么只需计算出LCA以及两组中每对节点的最小距离 - 此时可能会发现您有一个新的最佳答案,并且可以更新最佳答案,远。

查看问题顶部的示例,2s的LCA是树的根,1s的LCA是最高1.因此这两组之间的最小距离可能接近于零。现在将每组分成两部分。左手2距离两个1都是距离2。右手2的LCA本身位于树的右侧分支上,两个1中的每一个的LCA位于树的左手分支上。因此,两者之间的距离至少为2,即使我们在现有右手2的位置下方有大量的2,并且在左手子树的任何位置都有大量的1,我们也可以告诉它。

答案 2 :(得分:0)

树的pre-order traversal(或任何遍历应该有效)。

在此过程中,只需跟踪最近的1和2,并在找到2时更新距离,最近的1距离目前的最近距离更近,反之亦然。

代码(C ++,未经测试的初稿):(为简单起见,硬编码12

int getLeastDistance(Node *n, int *distTo1, int *distTo2)
{
  if (n == NULL)
    return;

  int dist = LARGE_VALUE;

  // process current node
  if (n->data == 1)
  {
    dist = *distTo2;
    *distTo1 = 0;
  }
  else if (n->data == 2)
  {
    dist = *distTo1;
    *distTo2 = 0;
  }

  // go left
  int newDistTo1 = *distTo1 + 1,
      newDistTo2 = *distTo2 + 1;

  dist = min(dist, getLeastDistance(n->left, &newDistTo1, &newDistTo2));

  // update distances
  *distTo1 = min(*distTo1, newDistTo1 + 1);
  *distTo2 = min(*distTo2, newDistTo2 + 1);

  // go right
  newDistTo1 = *distTo1 + 1;
  newDistTo2 = *distTo2 + 1;

  dist = min(dist, getLeastDistance(n->right, &newDistTo1, &newDistTo2));
}

呼叫者:

Node root = ...;
int distTo1 = LARGE_VALUE, distTo2 = LARGE_VALUE;
int dist = getLeastDistance(&root, &distTo1, &distTo2);

请确保LARGE_VALUE远离int的最大值,以便在递增时不会溢出(-1可能更安全,但需要更复杂的代码)。