递归树映射

发布于 2024-10-27 10:57:32 字数 679 浏览 1 评论 0原文

我最近一直在研究树的实现以及我们如何表示和理解树。我的重点是将数学表达式转换为二叉树,我设置了以线性形式(例如字符串或数组)表示树的问题,同时仍然保留有关树及其子树的重要信息。

因此,我为二进制表达式树开发了一种非常简单的编码,就是这样做的。然而,我在递归庄园中有效地实现它时遇到了一些问题,这似乎是这个概念背后的一个失败的方面。

如果节点作为左子节点,则编码很简单,如果它作为右子节点,则给出 1 的映射。这种简单的编码允许我对整个平衡和不平衡树进行编码,如下所示:

           ##                      ##
          /  \                    /  \
         1    0         OR       1    0
        / \  / \                     / \
       11 10 01 00                  01  00 

Etc to trees深度 N

有没有人对如何创建一个递归函数有任何建议,该函数将创建表示此类映射的前缀字符串(例如 ## 1 11 10 0 01 00)。

有人告诉我,这很困难/不可能,因为必须跟踪 1 和 0 之间的交替,同时保留并连接到父级的值。

我想知道是否有人对如何使用 C# 做到这一点有任何见解或想法?

I've been working a lot with tree implementations lately and how we represent and understand trees. My focus has been on turning mathematical expressions into binary trees, I set the problem of representing a tree in a linear form say a string or an array, while still retaining important information about the tree and its sub trees.

As such I have developed a really simple encoding for binary expression trees does just this. However I am having some issues with implementing it effectively in a recursive manor, it seems to be the one failing aspect behind the concept.

The encoding is simple if the node resides as a left child it is given a map of 1 if it resides as a right child it is given a 0. This simple encoding allows me to encode entire balanced and unbalanced trees like this:

           ##                      ##
          /  \                    /  \
         1    0         OR       1    0
        / \  / \                     / \
       11 10 01 00                  01  00 

Etc to trees of depth N

Does anyone have any suggestions as to how to create a recursive function that would create the prefix string representing a mapping of this sort (for example ## 1 11 10 0 01 00).

I was told this would be difficult/impossible due to having to keep track of alternating between 1 and 0 while retaining and concatenating to the value of the parent.

I wondered if anyone had any insight or ideas into how to do this with C# ??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

萌辣 2024-11-03 10:57:32

我不确定我是否理解你的问题,但这里有一些可能有帮助的东西。一种解决方案可能是在图上实现图遍历例程(请记住树是专门的图),其中访问发生在您第一次遇到节点/顶点时。当您要求使用 C# 时,我为发布 Java 代码表示歉意,但我碰巧知道 Java...

public void depthFirstSearch(Graph graph, Vertex start){
    Set<Vertex> visited = new HashSet<Vertex>(); // could use vertex.isVisited()...
    Deque<Vertex> stack = new ArrayDeque<Vertex>(); // stack implies depth first

    // first visit the root element, then add it to the stack so
    // we will visit it's children in a depth first order
    visit(start);
    visited.add(start);
    stack.push(start);   

    while(stack.isEmpty() == false){
        List<Edge> edges = graph.getEdges(stack.peekFirst());
        Vertex nextUnvisited = null;
        for(Edge edge : edges){
            if(visited.contains(edge.getEndVertex)) == false){
               nextUnvisited = edge.getEndVertex();
               break; // break for loop
            }
        }
        if(nextUnvisited == null){
            // check the next item in the stack
            Vertex popped = stack.pop();
        } else {
            // visit adjacent unvisited vertex
            visit(nextUnvisited);
            visited.add(nextUnvisited);
            stack.push(nextUnvisited); // visit it's "children"
        }
    }
}

public void visit(Vertex vertex){
    // your own visit logic (string append, etc)
}

您可以通过使用 Deque 作为队列而不是堆栈来轻松地将其修改为广度优先搜索,如下所示:

stack.pop()  >>>>  queue.removeFirst()
stack.push() >>>>  queue.addLast()

请注意,为此目的,图和 Edge 类支持以下操作:

public interface Graph {
    ...
    // get edges originating from Vertex v
    public List<Edge> getEdges(Vertex v);
    ...
}

public interface Edge {
    ...
    // get the vertex at the start of the edge
    // not used here but kind of implied by the getEndVertex()...
    public Vertex getStartVertex();
    // get the vertex at the end of the edge
    public Vertex getEndVertex();
    ...
}

希望这能给您一些想法。

I'm not sure I understand you problem but here is something that might help. One solution might be implementing graph traversal routine on a Graph (remember a Tree is a specialized Graph), where the visit occurs the first time you encounter a node/vertex. I apologize for posting Java code when you asked for C# but I happen know Java...

public void depthFirstSearch(Graph graph, Vertex start){
    Set<Vertex> visited = new HashSet<Vertex>(); // could use vertex.isVisited()...
    Deque<Vertex> stack = new ArrayDeque<Vertex>(); // stack implies depth first

    // first visit the root element, then add it to the stack so
    // we will visit it's children in a depth first order
    visit(start);
    visited.add(start);
    stack.push(start);   

    while(stack.isEmpty() == false){
        List<Edge> edges = graph.getEdges(stack.peekFirst());
        Vertex nextUnvisited = null;
        for(Edge edge : edges){
            if(visited.contains(edge.getEndVertex)) == false){
               nextUnvisited = edge.getEndVertex();
               break; // break for loop
            }
        }
        if(nextUnvisited == null){
            // check the next item in the stack
            Vertex popped = stack.pop();
        } else {
            // visit adjacent unvisited vertex
            visit(nextUnvisited);
            visited.add(nextUnvisited);
            stack.push(nextUnvisited); // visit it's "children"
        }
    }
}

public void visit(Vertex vertex){
    // your own visit logic (string append, etc)
}

You can easily modify this to be a breadth first search by using the Deque as a queue instead of stack as follows:

stack.pop()  >>>>  queue.removeFirst()
stack.push() >>>>  queue.addLast()

Note that for this purpose the Graph and Edge classes support the following operations :

public interface Graph {
    ...
    // get edges originating from Vertex v
    public List<Edge> getEdges(Vertex v);
    ...
}

public interface Edge {
    ...
    // get the vertex at the start of the edge
    // not used here but kind of implied by the getEndVertex()...
    public Vertex getStartVertex();
    // get the vertex at the end of the edge
    public Vertex getEndVertex();
    ...
}

Hopefully that gives you some ideas.

失眠症患者 2024-11-03 10:57:32

好吧,我不知道我是否完全明白你的问题,但似乎你想要对树进行预序遍历。我不知道 c# 的语法,但我认为伪代码如下:

preorder_traversal(node)
    if(node != NULL)
        print(node)
        preorder_traversal(left_sub_child)
        preorder_traversal(right_sub_child)
    else
        return

Well i don't know if i completely get your question but it seems you want a preorder traversal of the tree. I don't know c#'s syntax but the pseudocode i think will be as follows:

preorder_traversal(node)
    if(node != NULL)
        print(node)
        preorder_traversal(left_sub_child)
        preorder_traversal(right_sub_child)
    else
        return
⒈起吃苦の倖褔 2024-11-03 10:57:32

即使对于经验丰富的程序员来说,递归地构建一棵树也是一个艰巨的挑战。我意识到我在这个问题上有点晚了,考虑到它最初是在 2011 年 3 月发布的。迟到总比不做好?

创建树的一个重要因素是确保数据集的格式正确。您只需要一种将父级与子级关联起来的方法。一旦明确定义了关联,您就可以开始编写解决方案。我选择使用像这样的简单格式:

ParentId ChildId
1        2
1        3
2        4
3        5

等等。

一旦建立了这种关系,我开发了一种递归方法来迭代数据集来构建树。

首先,我识别所有父节点并将它们存储在一个集合中,使用父 ID 和子 ID 的组合为每个节点提供唯一标识符:

 private void IdentifyParentNodes()
{
    SortedList<string, MyTreeNode> newParentNodes = new SortedList<string,MyTreeNode>();
    Dictionary<string, string> parents = new Dictionary<string, string>();
    foreach (MyTreeNode oParent in MyTreeDataSource.Values)
    {
        if (!parents.ContainsValue(oParent.ParentId))
        {
            parents.Add(oParent.ParentId + "." + oParent.ChildId, oParent.ParentId);

            newParentNodes.Add(oParent.ParentId + "." + oParent.ChildId, oParent);
        }
    }

    this._parentNodes = newParentNodes;
}

然后根调用方法将循环遍历父节点并调用递归方法来构建树:

// Build the rest of the tree
foreach (MyTreeNode node in ParentNodes.Values)
{
    RecursivelyBuildTree(node);
}

递归方法:

private void RecursivelyBuildTree(MyTreeNode node)
{
    int nodePosition = 0;

    _renderedTree.Append(FormatNode(MyTreeNodeType.Parent, node, 0));
    _renderedTree.Append(NodeContainer("open", node.ParentId));

    foreach (MyTreeNode child in GetChildren(node.ParentId).Values)
    {
        nodePosition++;
        if (IsParent(child.ChildId))
        {
            RecursivelyBuildTree(child);
        }
        else
        {
            _renderedTree.Append(FormatNode(MyTreeNodeType.Leaf, child, nodePosition));
        }
    }
    _renderedTree.Append(NodeContainer("close", node.ParentId));
}

用于获取父级的子级的方法:

private SortedList<string, MyTreeNode> GetChildren(string parentId)
{
    SortedList<string, MyTreeNode> childNodes = new SortedList<string, MyTreeNode>();
    foreach (MyTreeNode node in this.MyTreeDataSource.Values)
    {
        if (node.ParentId == parentId)
        {
            childNodes.Add(node.ParentId + node.ChildId, node);
        }
    }
    return childNodes;
}

不是那么复杂或优雅,但它完成了工作。这是在 2007 年编写的,所以它是旧代码,但它仍然有效。 :-) 希望这有帮助。

Building a tree recursively is a difficult challenge even for a seasoned programmer. I realize I'm a bit late to the party on this question considering it was originally posted in March of 2011. Better late than never?

One important factor in creating a tree is just making sure your dataset is formatted correctly. You simply need a way to associate a parent to a child. Once the association is clearly defined, then you can begin to code the solution. I chose to use a simple format like this:

ParentId ChildId
1        2
1        3
2        4
3        5

Etc.

Once that relationship is established, I developed a recursive method to iterate through the dataset to build the tree.

First I identify all the parent nodes and store them in a collection giving them each a unique identifier using a combination of the parent ID and child ID:

 private void IdentifyParentNodes()
{
    SortedList<string, MyTreeNode> newParentNodes = new SortedList<string,MyTreeNode>();
    Dictionary<string, string> parents = new Dictionary<string, string>();
    foreach (MyTreeNode oParent in MyTreeDataSource.Values)
    {
        if (!parents.ContainsValue(oParent.ParentId))
        {
            parents.Add(oParent.ParentId + "." + oParent.ChildId, oParent.ParentId);

            newParentNodes.Add(oParent.ParentId + "." + oParent.ChildId, oParent);
        }
    }

    this._parentNodes = newParentNodes;
}

Then the root calling method would loop through the parents and call the recursive method to build the tree:

// Build the rest of the tree
foreach (MyTreeNode node in ParentNodes.Values)
{
    RecursivelyBuildTree(node);
}

Recursive method:

private void RecursivelyBuildTree(MyTreeNode node)
{
    int nodePosition = 0;

    _renderedTree.Append(FormatNode(MyTreeNodeType.Parent, node, 0));
    _renderedTree.Append(NodeContainer("open", node.ParentId));

    foreach (MyTreeNode child in GetChildren(node.ParentId).Values)
    {
        nodePosition++;
        if (IsParent(child.ChildId))
        {
            RecursivelyBuildTree(child);
        }
        else
        {
            _renderedTree.Append(FormatNode(MyTreeNodeType.Leaf, child, nodePosition));
        }
    }
    _renderedTree.Append(NodeContainer("close", node.ParentId));
}

Method used to get children of a parent:

private SortedList<string, MyTreeNode> GetChildren(string parentId)
{
    SortedList<string, MyTreeNode> childNodes = new SortedList<string, MyTreeNode>();
    foreach (MyTreeNode node in this.MyTreeDataSource.Values)
    {
        if (node.ParentId == parentId)
        {
            childNodes.Add(node.ParentId + node.ChildId, node);
        }
    }
    return childNodes;
}

Not that complex or elegant, but it got the job done. This was written in 2007 time frame, so it's old code, but it still works. :-) Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文