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>();
...
答案 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)总时间内运行。
基本思想是以下递归洞察:
这就产生了这种递归算法,它用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(),但我认为它指的是单个节点。