从展平的 DFS 结构创建递归结构

Posted

技术标签:

【中文标题】从展平的 DFS 结构创建递归结构【英文标题】:Create recursive structure from flatten DFS structure 【发布时间】:2014-11-24 10:35:31 【问题描述】:

问题

我有以下树:

      2
     / \
    3   5
   /   / \ 
  6   4   1

以下列方式和顺序表示:

id    parent
------------
2     null
3     2
6     3
5     2
4     5
1     5

目的:

O(n)中将这个扁平化的树存储在递归结构中(O(n*log(n))是可以接受的,但不是很好)(我知道如何在O(n^2),但我以 DFS 顺序存储数据以便能够以更有效的方式“解析”它)。例如:

class R 
    int id;
    List<R> children;

在 JSON 格式中看起来像这样:


    id: 2,
    children: [
            
                id: 3,
                children:  ...              
            ,
            
                id: 5,
                children:  ... 
            
    ]

如何我可以这样做?编程语言并不重要,因为我可以用 Java 翻译它。


Java 代码:

R r = new R();
Map<Long, Line> map = createMap2();
List<Line> vals = new ArrayList<Line>(map.values());
r.id = vals.get(0).id;
vals.remove(0);
r.children = createResource(vals, r.id);
...
private static List<R> createResource(List<Line> l, Long pid) 
    List<R> lr = new ArrayList<R>();
    if ( l.size() > 0 )            
        Long id = l.get(0).id;
        Long p = l.get(0).pid;
        l.remove(0);
        if ( pid.equals(p) ) 
            R r = new R();
            r.id = id;
            r.children = createResource(l, id);
            lr.add(r);
        
        else 
            return createResource(l, pid);   // of course, this is not ok
        
    
    return lr;

上面代码中的问题是递归结构(R类)中只有236存储。我想将整个扁平树结构(许多 Line 对象)存储在那个 递归结构(R 对象)中,而不仅仅是一些节点。

P.S.:问题被简化了。我对特定的解决方案不感兴趣,因为涉及的领域很多,条目数以千计。我也对在最坏情况下(不同种类的树)工作良好的解决方案感兴趣,因为这是用户的保证。

【问题讨论】:

究竟有什么问题,您想将 id/parent 列表转换为有子节点的节点吗?此外,您的数据是否主要涉及平衡树? 【参考方案1】:

这样的事情呢?在第一遍中,将父母哈希为他们孩子的数组并识别根;在第二个中,从根开始,并为它的每个子项插入一个新对象,以及它自己的子项,依此类推:

以你为例,第一遍会生成

parent_hash = 2:[3,5], 3:[6], 5:[4,1]
root = 2

第二遍会是这样的:

object 2 -> object 3 -> object 6
         -> object 5 -> object 4
                     -> object 1
done

【讨论】:

感谢您的回复。现在我正在尝试另一种方法(我编辑了帖子),但如果我做不到,我会尝试根据您的回答创建其他对象。【参考方案2】:

您的代码的问题在于,一旦条目不满足p == pid 条件,它就会永远丢失。您应该打破循环并立即返回,而不是丢失条目。违规条目也应由R上游的适当实例返回和处理。

【讨论】:

感谢您的回复,但我将帖子调整为使用数组的简化版本。我会努力解决的……【参考方案3】:

您可以轻松地在数组中表示整棵树,因为树的每个节点都可以由数组中的索引表示。对于二叉树,索引 i 的子节点将位于索引 2*i+1 和索引 2*i+2 处。然后将数组转换为任何其他表示形式会很简单。数组本身对于平衡树来说是一种节省空间的表示,但对于非常不平衡的树会浪费大量空间。 (除非您要处理大量数据,否则这应该无关紧要。)

如果您需要一种高效存储大型不平衡树的方法,那么使用树的标准节点表示是有意义的。要从您的列表进行转换,您可以使用 HashMap 作为 גלעד ברקן 建议的。但是,如果节点的 id 大部分是连续的(例如它们从 1 到 6 的示例),您也可以只使用一个数组,其中数组 i 的每个索引都用于存储一个 ID 为 i 的节点。这将让您轻松找到父节点并在创建子节点时为其分配子节点。

(参见我的Trees tutorial,将树存储为数组。)

【讨论】:

【参考方案4】:

我找到了一个基于“DFS”顺序的简单解决方案。

即使我使用“线”对象列表或地图,这种方法也有效。

private static List<R> createResource(List<Line> l, Long pid) 
    List<R> lr = new ArrayList<R>();
    for ( Line line : l )  
        if ( line is a children ) 
            R r = new R();
            r.id = id;
            l.remove(0);
            r.children = createResource(l, line.id);                
            lr.add(r);
        
    
    return lr;

它似乎在 O(n^2) 中,因为所有元素都有一个 for 循环+递归,但它在 O(n) 中。由于 DFS 顺序,调用 createResource 的下一个元素位于第一个位置( 0 -> O(1) )。因为递归需要每个元素 => 复杂性是 O(n)

但如果订单不是 DFS 订单(可能涉及到不是 LinkedHashMapMap),我推荐包含父母数组的解决方案。 (根据 גלעד ברקן )

【讨论】:

以上是关于从展平的 DFS 结构创建递归结构的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Hive/Pig/MapReduce 展平递归层次结构

Java数据结构——二叉树的递归与非递归遍历(DFS)

Hive - 将层次结构表展平为级别

数据结构与算法图遍历算法 ( 深度优先搜索 DFS | 深度优先搜索和广度优先搜索 | 深度优先搜索基本思想 | 深度优先搜索算法步骤 | 深度优先搜索理论示例 )

[数据结构]Graph之拓扑排序BFS&DFS实现

递归遍历未知结构的NSDictionary