在线性时间内为树中的每个节点查找最左边的子节点?

时间:2013-11-08 18:10:45

标签: algorithm data-structures tree theory graph-theory

A paper我正在阅读声明

  

很容易看出有一个线性时间算法来计算函数l()

其中l()给出最左边的子节点(输入和输出都在树的后序遍历中)。但是,我只能想到一个天真的O(n^2)实现,其中n是树中的节点数。

例如,请考虑以下树:

  a
 / \
c   b

在后序遍历中,树为c b a。相应的函数l()应该为c b c

以下是O(n^2)时间内的实施情况。

public Object[] computeFunctionL(){
    ArrayList<String> l= new ArrayList<String>();
    l= l(this, l);
    return l.toArray();
}

private ArrayList<String> l(Node currentRoot, ArrayList<String> l){
    for (int i= 0; i < currentRoot.children.size(); i++){
        l= l(currentRoot.children.get(i), l);
    }
    while(currentRoot.children.size() != 0){
        currentRoot= currentRoot.children.get(0);
    }
    l.add(currentRoot.label);
    return l;
}

树被制作为:

public class Node {
private String label;
private ArrayList<Node> children= new ArrayList<Node>();
...

3 个答案:

答案 0 :(得分:1)

您可以在不到O(n ^ 2)的时间内找到整个树的l()。我们的想法是按顺序遍历树,维护您在遍历左侧分支时访问过的一堆节点。当你到达一个叶子时,这是整个分支的最左边的节点。

以下是一个例子:

class BTreeNode
{
    public readonly int Value;
    public BTreeNode LeftChild { get; private set; }
    public BTreeNode RightChild { get; private set; }
}

void ShowLeftmost(BTreeNode node, Stack<int> stack)
{
    if (node.LeftChild == null)
    {
        // this is the leftmost node of every node on the stack
        while (stack.Count > 0)
        {
            var v = stack.Pop();
            Console.WriteLine("Leftmost node of {0} is {1}", v, node.Value);
        }
    }
    else
    {
        // push this value onto the stack so that
        // we can add its leftmost node when we find it.
        stack.Push(node.Value);
        ShowLeftmost(node.LeftChild, stack);
    }
    if (node.RightChild != null)
        ShowLeftmost(node.RightChild, stack);
}

复杂性显然不是O(n ^ 2)。相反,它是O(n)。

遍历树需要O(n)。没有节点多次放在堆栈上。该算法的最坏情况是包含所有左节点的树。在这种情况下,遍历树的O(n)和O(n)枚举堆栈。最好的情况是包含所有权限节点的树,在这种情况下,永远不会有任何堆栈进行枚举。

所以O(n)时间复杂度,O(n)最坏情况下堆栈的额外空间。

答案 1 :(得分:1)

您可以使用一种简单的递归算法,可以在每个节点的O(1)时间内计算此信息。由于总共有n个节点,因此这将在O(n)总时间内运行。

基本思想是以下递归洞察:

  • 对于没有左子节点的任何节点n,l(n)= n。
  • 否则,如果n已离开子L,则l(n)= l(L)。

这就产生了这种递归算法,它用l值注释每个节点:

function computeL(node n) {
   if n is null, return.

   computeL(n.left)
   computeL(n.right)

   if n has no left child:
      set n.l = n
   else
      set n.l = n.left.l

希望这有帮助!

答案 2 :(得分:0)

看看3.1节:

  

3.1。符号。根据从左到右,让T [i]成为树中的第i个节点   后序编号,l(i)是子树最左边的叶子后代的编号   扎根于T [i]。

鉴于关于符号的句子,我假设函数l()指的是在线性时间内找到一个节点。

可能有更优雅(优于O(n ^ 2))的方式为整个树找到l(),但我认为它指的是单个节点。