每个级别的链接树节点

时间:2010-02-01 22:15:52

标签: algorithm binary-tree

给定二叉树,如何从左到右加入每个级别的节点。 假设第3级有5个节点,从左到右连接所有节点。

我不需要任何人为此编写代码..但只是一种有效的算法。

由于

9 个答案:

答案 0 :(得分:5)

想法是:
1.用BFS遍历树。
2.当您进行遍历时,您将链接下一级别的节点 - 如果节点具有左侧和右侧节点,则您将从左向右链接。如果节点有下一个节点,则将当前节点的最右边的子节点链接到下一节点的最左边的子节点。

    public void BreadthFirstSearch(Action<Node> currentNodeAction)
    {
        Queue<Node> q = new Queue<Node>();
        q.Enqueue(root);

        while (q.Count != 0)
        {
            Node current = q.Dequeue();

            if (currentNodeAction != null)
                currentNodeAction(current);

            if (current.left != null) q.Enqueue(current.left);
            if (current.right != null) q.Enqueue(current.right);
        }
    }

    private void Linker(Node node)
    {
        Link(node.left, node.right);

        if (node.next != null)
            Link(node.right ?? node.left, node.next.left ?? node.next.right);
    }

    private void Link(Node node1, Node node2)
    {
        if (node1 != null && node2 != null)
            node1.next = node2;
    }

    public void LinkSameLevel()
    {
        BreadthFirstSearch(Linker);
    }

答案 1 :(得分:4)

创建链接列表的向量。 DFS会跟踪您的级别,并且对于您找到的每个节点,将其添加到级别的链接列表中。 这将在O(n)中运行,这是最佳的。

这是你想要做的吗?

答案 2 :(得分:2)

这不是问题的直接答案,可能不适用于您的情况。但是,如果您可以控制二叉树的创建和维护,那么在构建/更新树时维护链接可能会更有效。

如果你在每个级别都保留了左右两个指针,那么维护它们就会“简单”(总是很容易在其他人做这项工作时说出这个词)。在给定级别插入新节点时,您可以从父节点信息中知道它的直接兄弟节点。您可以调整所涉及的三个节点的左右指针(假设不在树的边缘)。同样,在删除节点时,只需更新要删除的节点的兄弟节点的左右指针。改变它们以指向彼此。

答案 3 :(得分:1)

如果你想同时制作所有的行列表,我同意Thomas Ahle的回答。您似乎只对列出一个特定行的列表感兴趣。

假设您有一棵巨树,但您只想链接第5行。访问第5行下面的任何节点显然没有意义。所以只需做一个早期终止的DFS。不幸的是,您仍然需要遍历列表中每个节点的所有祖先

但这是个好消息。如果你有一个完美的二叉树(每个节点除最后一行之外都会分两次),那么第一行将有1个,第二个2,第三个4,第四个8和第五个16。因此还有更多最后一行(16)上的节点比之前的所有节点(1 + 2 + 4 + 8 = 15),所以搜索所有的祖先仍然只是O(n),其中n是节点中的节点数行。

另一方面,最坏的情况是让第五行由单个节点组成,其上面有一个完整的二叉树。然后你仍然需要搜索所有15个祖先只是为了将那个节点放在列表中。

因此,虽然在不修改数据结构的情况下,此算法确实是您唯一的选择,但其效率完全依赖于将行与较高行进行比较的方式。

答案 4 :(得分:0)

#include <queue>

struct Node {
    Node *left;
    Node *right;
    Node *next;
};

/** Link all nodes of the same level in a binary tree. */
void link_level_nodes(Node *pRoot)
{
    queue<Node*> q;
    Node *prev;     // Pointer to the revious node of the current level
    Node *node;
    int cnt;        // Count of the nodes in the current level
    int cntnext;    // Count of the nodes in the next level

    if(NULL == pRoot)
        return;

    q.push(pRoot);
    cnt = 1;
    cntnext = 0;
    prev = NULL;

    while (!q.empty()) {
        node = q.front();
        q.pop();

        /* Add the left and the right nodes of the current node to the queue
           and increment the counter of nodes at the next level. 
        */
        if (node->left){
            q.push(node->left);
            cntnext++;
        }
        if (node->right){
            q.push(node->right);
            cntnext++;
        }

        /* Link the previous node of the current level to this node */
        if (prev)
            prev->next = node;

        /* Se the previous node to the current */
        prev = node;

        cnt--;
        if (0 == cnt) { // if this is the last node of the current level
            cnt = cntnext;
            cntnext = 0;
            prev = NULL;
        }
    }
}

答案 5 :(得分:0)

我通常做的解决这个问题的方法是我做一个简单的inorder遍历。

我使用构造函数初始化我的树,该构造函数为每个节点提供级别或列值。因此我的头脑处于0级。

public Node(int d)
{
head=this;
data=d;
left=null;
right=null;
level=0;
}

现在,如果在遍历中,我左转或右转,我只需使用水平指示器进行遍历。对于每个级别标识符,我可以在节点向量中创建链接列表。

答案 6 :(得分:0)

可以使用不同的方法来解决此问题。其中一些想到的是 -

1)使用级别顺序遍历或BFS  我们可以修改队列条目以包含节点级别。因此队列节点将包含指向树节点和整数级别的指针。当我们deque一个节点时,我们可以检查出列节点的级别是否相同我们可以设置右指针指向它。
该方法的时间复杂度为O(n)。

2)如果我们有完整的二叉树,我们可以扩展Pre-Order遍历。在这个方法中,我们将在子项之前设置父项的右指针 该方法的时间复杂度为O(n)。

3)在不完整的二叉树的情况下,我们可以通过遍历第一个根然后右指针来修改方法(2)然后向左,这样我们就可以确保第i级的所有节点都有正确的指针集,在i +级之前1个节点。 该方法的时间复杂度为O(n ^ 2)。

答案 7 :(得分:0)

    private class Node
    {
        public readonly Node Left;
        public readonly Node Right;
        public Node Link { get; private set; }

        public void Run()
        {
            LinkNext = null;
        }

        private Node LinkNext
        {
            get
            {
                return Link == null ? null : (Link.Left ?? Link.Right ?? Link.LinkNext);
            }
            set
            {
                Link = value;
                if (Right != null)
                    Right.LinkNext = LinkNext;
                if (Left != null)
                    Left.LinkNext = Right ?? LinkNext;
            }
        }
    }

答案 8 :(得分:0)

在广度优先搜索中保留深度数组。

    vector<forward_list<index_t>> level_link(MAX_NODES);
    index_t fringe_depth = 0;
    static index_t depth[MAX_NODES];
    memset(depth,0,sizeof(depth));
    depth[0] = 0;

现在,当出队时深度发生变化时,您将全部链接!

    explored[0] = true;
    static deque<index_t> fringe;
    fringe.clear();
    fringe.push_back(0); // start bfs from node 0
    while(!fringe.empty()) {
        index_t xindex = fringe.front();
        fringe.pop_front();
        if(fringe_depth < depth[xindex]) {
            // play with prev-level-data
            fringe_depth = depth[xindex];
        }

现在我们具有边缘深度,因此我们可以进行水平链接。

        level_link[fringe_depth].push_front(xindex);

        for(auto yindex : nodes[xindex].connected) {
            if(explored[yindex])
                continue;
            explored[yindex] = true;
            depth[yindex] = depth[xindex] + 1;
            fringe.push_back(yindex);
        }
    }