【问题标题】:Fastest non-recursive implementation of LCA?LCA 最快的非递归实现?
【发布时间】:2017-01-02 14:33:24
【问题描述】:

这是我提出的非递归查找二叉树中两个节点的最低共同祖先的算法。以下是基本策略:

  1. 使用字典/哈希表来存储树。每个键值对代表一个节点及其父节点。
  2. 从两个节点中的每一个开始,通过将表示每个节点的值的变量设置为其父节点的值,将遍历的值存储在哈希集中(两个节点中的每个节点一个),从而向上遍历树。
  3. 当满足以下任一条件时,搜索完成: (a) 两个节点的值相等;或 (b) 当两条路径相互交叉时(即节点 1 的遍历值的哈希集包含节点 2 的当前值,反之亦然);或 (c) 传入的节点在树中不存在(在这种情况下,算法终止并返回 -1)。

我的理解是,我的算法的最坏情况时间和空间复杂度是 O(log(n)),因为我们永远不需要进行超过 2 * 高度遍历或在哈希集中存储超过 2 * 高度值(并且由于哈希集和树字典的查找时间为 O(1))。

以下是我的代码 (C#)。请告知我的分析是否正确,或者是否有更有效(非递归)的方法来做到这一点:

int LowestCommonAncestor(int value1, int value2, Dictionary<int, int> tree)
{
    var value1Visited = new HashSet<int>();
    var value2Visited = new HashSet<int>();
    while (true)
    {
        if (value1 == value2) return value1;
        if (value1Visited.Contains(value2)) return value2;
        if (value2Visited.Contains(value1)) return value1;

        int nextValue1;
        int nextValue2;

        if (tree.TryGetValue(value1, out nextValue1))
        {
            //Walk node 1 up the tree:
            value1 = nextValue1;
            value1Visited.Add(value1);
        }
        else
        {
            //Node doesn't exist in tree:
            return -1;
        }

        if (tree.TryGetValue(value2, out nextValue2))
        {
            //Walk node 2 up the tree:
            value2 = nextValue2;
            value2Visited.Add(value2);
        }
        else
        {
            //Node doesn't exist in tree:
            return -1;
        }
    }
}

【问题讨论】:

  • 所提出的算法是否会导致一些错误或意外结果?
  • 除非树是平衡的,否则它不是O(log N)。是O(N)(例如,竹子是二叉树)。

标签: algorithm dictionary tree lowest-common-ancestor


【解决方案1】:
  1. 从每个节点向上到根测量其深度

  2. 从较深的节点向上移动路径,直到到达与较浅节点相同的深度。

  3. 从两个节点向上移动路径(即,保持两条路径的相同深度)直到它们相遇。

【讨论】:

  • 我喜欢这个,因为它不需要额外的哈希集空间。
【解决方案2】:

您不需要两个哈希集。

  1. 向上并在单个哈希集中收集一个节点的祖先
  2. 从第二个节点向上,在它的每个祖先,检查在步骤 1 收集的路径是否包含第二个的当前祖先。停在第一个常见的地方。

D 是树的最大深度,复杂度是 O(D) 最坏情况复杂度。

最坏情况复杂度 N - 节点数 - 当树在一个列表中退化时,一个节点是这个列表的头部,另一个是尾部。

如果树是平衡的,则 D=log(N) - log 的基数是节点的后代数(二进制 - log2、三进制 - log3 等)。

【讨论】:

    【解决方案3】:

    那么,这是我修改后的算法:

    int LCA(int value1, int value2, Dictionary<int, int> tree)
    {
        if (!tree.ContainsKey(value1) || !(tree.ContainsKey(value2))) return -1;
    
        int depth1 = 0;
        int depth2 = 0;
        int tmpVal1 = value1;
        int tmpVal2 = value2;
    
        while (tmpVal1 != -1)
        {
            tmpVal1 = tree[tmpVal1];
            depth1++;
        }
    
        while (tmpVal2 != -1)
        {
            tmpVal2 = tree[tmpVal2];
            depth2++;
        }
    
        if (depth1 > depth2)
        {
            while (depth1 > depth2)
            {
                value1 = tree[value1];
                depth1--;
            }
        }
    
        else if (depth2 > depth1)
        {
            while (depth2 > depth1)
            {
                value2 = tree[value2];
                depth2--;
            }
        }
        while (value1 != value2)
        {
            value1 = tree[value1];
            value2 = tree[value2];
        }
    
        return value1;
    }
    

    【讨论】:

    • 我的下一个挑战是在给定一个只包含子指针(没有父指针)的树时这样做,也没有递归并且(如果可能的话)除了树本身之外不使用其他集合。
    猜你喜欢
    • 2016-11-05
    • 2011-08-27
    • 2014-08-21
    • 2013-10-08
    • 2016-09-27
    • 1970-01-01
    • 2020-04-22
    • 2019-11-16
    • 1970-01-01
    相关资源
    最近更新 更多