从列表中构造树(非二进制,不平衡)的最低时间复杂度?

Posted

技术标签:

【中文标题】从列表中构造树(非二进制,不平衡)的最低时间复杂度?【英文标题】:Lowest time complexity to construct tree (nonbinary, unbalanced) from a list? 【发布时间】:2021-04-29 23:31:22 【问题描述】:

假设我有一个像这样的列表/数组:

[
     id: 1, parent: null ,
     id: 2, parent: 1 ,
     id: 4, parent: 2 ,
     id: 5, parent: 3 ,
     id: 6, parent: 2 ,
     id: 3, parent: 4 
]

我想把它转换成这样的树:

 
    id: null,
    children: [
         
            id: 1,
            children: [
                 
                    id: 2,
                    children: [
                         
                            id: 4,
                            children: [
                                
                                    id: 3,
                                    children: [
                                        
                                            id: 5,
                                            children: []
                                        
                                    ]
                                
                            ]
                        ,
                        
                            id: 6,
                            children: [
                                
                            ]
                        
                    ]
                
            ]
        
    ]

我可以使用以下(伪代码)函数轻松做到这一点:

function GetChildren(rootNode, inputList) 
    for (item in inputList) 
        if (item.parent == rootNode.id) 
            childNode = 
                id: item.id,
                children: []
            
            GetChildren(childNode, inputList)
            rootNode.children.append(childNode)
        
    

我相信这将以 O(n²) 的时间复杂度运行。有没有一种算法可以更快地做到这一点?我见过一些类似的 BST 问题,但这不是 BST。

注意以下几点:

一个节点可能有无限的子节点 树可以无限深 列表中的项目可以按任意顺序出现(子项可能出现在父项之前

我曾想过尝试只传递列表的一部分而不传递父级,因此当您重复时,您会遍历一个逐渐变小的列表。我不知道这是否真的可以节省时间:

function GetChildren(rootNode, inputList) 
    for (item in inputList) 
        listWithoutParent = inputList.remove(rootNode) // O(?)
        if (item.parent == rootNode.id) 
            childNode = 
                id: item.id,
                children: []
            
            GetChildren(childNode, listWithoutParent)
            rootNode.children.append(childNode)
        
    

【问题讨论】:

您可以通过使用地图/查找表更快地实现这一目标。看看这个问题:***.com/questions/444296/… 【参考方案1】:

这个想法是维护一个哈希表,以 id 为键,其中的值是要创建的树的对象。因此,例如,在该哈希表中,您将有这样的条目:

key: 1
value:  id: 1, children: [] 

在迭代输入数据时,查找当前 id 和当前父级的值。如果其中任何一个尚未出现在哈希表中,则在此时插入它。这为您提供了两个具有idchildren 属性的对象。然后将子节点对象追加到父对象的children数组中。

这是它在 javascript 中的工作方式,对于哈希表,我使用本机 Map

function getOrCreateNode(map, id) 
    let node = map.get(id);
    if (node == undefined)  // Node not yet created
        node =  id, children: [] ; // Create a new node
        map.set(id, node); // Add that new node to the map by its id
    
    return node;


function createTree(data) 
    let map = new Map; // Keys are id, values are nodes of the tree 
    for (let item of data)  // Iterate all objects in the input array
        // Get parent and child object, and add child object to parent's children array:
        getOrCreateNode(map, item.parent).children.push(getOrCreateNode(map, item.id));
    
    return map.get(null); // Return the root node


// Sample input data:
let data = [
     id: 1, parent: null ,
     id: 2, parent: 1 ,
     id: 4, parent: 2 ,
     id: 5, parent: 3 ,
     id: 6, parent: 2 ,
     id: 3, parent: 4 
];

// Output the result
console.log(createTree(data));

由于哈希表通常提供具有average amortised constant time complexity 的插入和查找实现,因此该算法将以线性时间复杂度运行(就输入数组中的条目数而言)。

我们应该添加免责声明,即哈希表上的插入和查找操作具有与其大小成线性关系的理论上的最差时间复杂度。有时您可能对id 值了解更多,这样您就可以使用完美的散列算法。例如,id 值都可以是小的无符号整数(如您的示例中)。在这种情况下,您可以将数组用作哈希表,并将 id 值用作数组索引。那么插入或查找操作的最坏情况时间复杂度仍然是 O(1)。

【讨论】:

以上是关于从列表中构造树(非二进制,不平衡)的最低时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章

第八章 查找——动态表查找之平衡二叉树

数据结构与算法简记--红黑树

各种查找算法的选用分析(顺序查找二分查找二叉平衡树B树红黑树B+树)

深入理解平衡二叉树(AVL)

平衡二叉树的构建

树总结(二)平衡二叉树