Як ефективно побудувати дерево з плоскої конструкції?


153

У мене купа предметів у плоскій структурі. Ці об'єкти мають IDі ParentIDвластивість, тому їх можна розташувати по деревах. Вони не в конкретному порядку. Кожна ParentIDвластивість не обов'язково збігається з IDструктурою. Тому їх може бути декілька дерев, що виникають із цих об'єктів.

Як би ви обробили ці об’єкти, щоб створити отримані дерева?

Я не так далеко від рішення, але впевнений, що це далеко не оптимальне ...

Мені потрібно створити ці дерева, щоб потім вставити Дані в базу даних, у належному порядку.

Циркулярних посилань немає. Вузол - це RootNode, коли ParentID == null або коли ParentID не можна знайти в інших об'єктах


Що ви маєте на увазі під «творити»? Візуалізувати в інтерфейсі? Зберігати ієрархічно в XML чи базі даних?
RedFilter

Як визначити вузол без батьківського (тобто кореневий вузол). ParentID недійсний? ParentID = 0? Я припускаю, що кругових посилань немає правильно?
Джейсон Пеньон

5
Я вважаю це питання досить крутим.
nes1983

1
ознайомтесь із цією статтею: scip.be/index.php?Page=ArticlesNET23&Lang=NL
ebram khalil

Відповіді:


120

Зберігайте ідентифікатори об’єктів у хеш-таблиці, що відображає конкретний об'єкт. Перерахуйте всі об'єкти та знайдіть їхнього батьківського, якщо він існує, та оновіть його батьківський покажчик відповідно.

class MyObject
{ // The actual object
    public int ParentID { get; set; }
    public int ID { get; set; }
}

class Node
{
    public List<Node> Children = new List<Node>();
    public Node Parent { get; set; }
    public MyObject AssociatedObject { get; set; }
}

IEnumerable<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects)
{
    Dictionary<int, Node> lookup = new Dictionary<int, Node>();
    actualObjects.ForEach(x => lookup.Add(x.ID, new Node { AssociatedObject = x }));
    foreach (var item in lookup.Values) {
        Node proposedParent;
        if (lookup.TryGetValue(item.AssociatedObject.ParentID, out proposedParent)) {
            item.Parent = proposedParent;
            proposedParent.Children.Add(item);
        }
    }
    return lookup.Values.Where(x => x.Parent == null);
}

5
яка мова це? (Я беру це C #)
Jason S

3
Цей альго є (у неофіційній нотації) O (3N), де рішення O (1N) легко досяжне шляхом інстанцізації часткових Вузлів для батьків, які не переїжджають, АБО шляхом збереження вторинних таблиць огляду для дітей, які не мають інстанції батьків. Ймовірно, це не має значення для більшості застосувань у реальному світі, але це може бути значним для великих наборів даних.
Ендрю Ханлон

15
@AndrewHanlon може бути , ви повинні розміщувати золю для 0 (1N)
Ced

1
@Ced Мартин Шмідт відповідь нижче дуже близький до того, як я б це здійснив. Як видно, він використовує один цикл, а решта - це хеш-операції.
Ендрю Ханлон

26
O (3N) - це просто O (N);)
Джейк Уілсон801

34

Виходячи з відповіді Мехрдада Афшарі та коментаря Ендрю Хенлона про пришвидшення, ось моя думка.

Важлива відмінність від початкового завдання: кореневий вузол має ID == parentID.

class MyObject
{   // The actual object
    public int ParentID { get; set; }
    public int ID { get; set; }
}

class Node
{
    public List<Node> Children = new List<Node>();
    public Node Parent { get; set; }
    public MyObject Source { get; set; }
}

List<Node> BuildTreeAndGetRoots(List<MyObject> actualObjects)
{
    var lookup = new Dictionary<int, Node>();
    var rootNodes = new List<Node>();

    foreach (var item in actualObjects)
    {
        // add us to lookup
        Node ourNode;
        if (lookup.TryGetValue(item.ID, out ourNode))
        {   // was already found as a parent - register the actual object
            ourNode.Source = item;
        }
        else
        {
            ourNode = new Node() { Source = item };
            lookup.Add(item.ID, ourNode);
        }

        // hook into parent
        if (item.ParentID == item.ID)
        {   // is a root node
            rootNodes.Add(ourNode);
        }
        else
        {   // is a child row - so we have a parent
            Node parentNode;
            if (!lookup.TryGetValue(item.ParentID, out parentNode))
            {   // unknown parent, construct preliminary parent
                parentNode = new Node();
                lookup.Add(item.ParentID, parentNode);
            }
            parentNode.Children.Add(ourNode);
            ourNode.Parent = parentNode;
        }
    }

    return rootNodes;
}

1
Приємно, це в основному підхід, на який я натякав. Однак я б просто скористався псевдокореневим вузлом (з ID = 0 і нульовим батьківським) і видалив вимогу самопосилання.
Ендрю Ханлон

Єдине, чого бракує в цьому прикладі, - це призначення батьківського поля кожному дитячому вузлу. Для цього нам потрібно встановити поле Батько лише після додавання дітей до його батьківської колекції. Приблизно так: parentNode.Children.Add (ourNode); ourNode.Parent = parentNode;
плауріола

@plauriola Щоправда, дякую, я додав це. Альтернативою було б просто видалити властивість Parent, для алгоритму core не потрібно.
Мартін Шмідт

4
Оскільки я не зміг знайти модуль npm, який реалізує рішення O (n), я створив наступний (перевірений блок, 100% покриття коду, розміром лише 0,5 кб і включає типінги. Можливо, хтось допомагає: npmjs.com/package / виконавець-масив до дерева
Філіп Станіслав

32

Ось простий алгоритм JavaScript для розбору плоскої таблиці в структурі батьківського / дочірнього дерева, яка працює за N часу:

var table = [
    {parent_id: 0, id: 1, children: []},
    {parent_id: 0, id: 2, children: []},
    {parent_id: 0, id: 3, children: []},
    {parent_id: 1, id: 4, children: []},
    {parent_id: 1, id: 5, children: []},
    {parent_id: 1, id: 6, children: []},
    {parent_id: 2, id: 7, children: []},
    {parent_id: 7, id: 8, children: []},
    {parent_id: 8, id: 9, children: []},
    {parent_id: 3, id: 10, children: []}
];

var root = {id:0, parent_id: null, children: []};
var node_list = { 0 : root};

for (var i = 0; i < table.length; i++) {
    node_list[table[i].id] = table[i];
    node_list[table[i].parent_id].children.push(node_list[table[i].id]);
}

console.log(root);

намагаючись перетворити такий підхід на C #.
хакан

зрозумів, що якщо id починається з чогось великого, як 1001, то ми отримуємо індекс поза обмеженим винятком ...
hakan

2
Порада: використовуйте console.log(JSON.stringify(root, null, 2));для гарного друку результатів.
aloisdg переходить на codidact.com

14

Рішення Python

def subtree(node, relationships):
    return {
        v: subtree(v, relationships) 
        for v in [x[0] for x in relationships if x[1] == node]
    }

Наприклад:

# (child, parent) pairs where -1 means no parent    
flat_tree = [
     (1, -1),
     (4, 1),
     (10, 4),
     (11, 4),
     (16, 11),
     (17, 11),
     (24, 17),
     (25, 17),
     (5, 1),
     (8, 5),
     (9, 5),
     (7, 9),
     (12, 9),
     (22, 12),
     (23, 12),
     (2, 23),
     (26, 23),
     (27, 23),
     (20, 9),
     (21, 9)
    ]

subtree(-1, flat_tree)

Виробляє:

{
    "1": {
        "4": {
            "10": {}, 
            "11": {
                "16": {}, 
                "17": {
                    "24": {}, 
                    "25": {}
                }
            }
        }, 
        "5": {
            "8": {}, 
            "9": {
                "20": {}, 
                "12": {
                    "22": {}, 
                    "23": {
                        "2": {}, 
                        "27": {}, 
                        "26": {}
                    }
                }, 
                "21": {}, 
                "7": {}
            }
        }
    }
}

Привіт. Як додати ще один атрибут у висновок? тобто. ім'я, parent_id
простий хлопець

на сьогоднішній день найелегантніший!
ccpizza

@simpleguy: розуміння списку можна розгорнути, якщо вам потрібно більше контролю, наприклад:def recurse(id, pages): for row in rows: if row['id'] == id: print(f'''{row['id']}:{row['parent_id']} {row['path']} {row['title']}''') recurse(row['id'], rows)
ccpizza

8

Версія JS, яка повертає один корінь або масив коренів, кожен з яких матиме властивість «Дочірній масив», що містить відповідні дочірні програми. Не залежить від впорядкованого вводу, пристойно компактний і не використовує рекурсії. Насолоджуйтесь!

// creates a tree from a flat set of hierarchically related data
var MiracleGrow = function(treeData, key, parentKey)
{
    var keys = [];
    treeData.map(function(x){
        x.Children = [];
        keys.push(x[key]);
    });
    var roots = treeData.filter(function(x){return keys.indexOf(x[parentKey])==-1});
    var nodes = [];
    roots.map(function(x){nodes.push(x)});
    while(nodes.length > 0)
    {

        var node = nodes.pop();
        var children =  treeData.filter(function(x){return x[parentKey] == node[key]});
        children.map(function(x){
            node.Children.push(x);
            nodes.push(x)
        });
    }
    if (roots.length==1) return roots[0];
    return roots;
}


// demo/test data
var treeData = [

    {id:9, name:'Led Zep', parent:null},
    {id:10, name:'Jimmy', parent:9},
    {id:11, name:'Robert', parent:9},
    {id:12, name:'John', parent:9},

    {id:8, name:'Elec Gtr Strings', parent:5},
    {id:1, name:'Rush', parent:null},
    {id:2, name:'Alex', parent:1},
    {id:3, name:'Geddy', parent:1},
    {id:4, name:'Neil', parent:1},
    {id:5, name:'Gibson Les Paul', parent:2},
    {id:6, name:'Pearl Kit', parent:4},
    {id:7, name:'Rickenbacker', parent:3},

    {id:100, name:'Santa', parent:99},
    {id:101, name:'Elf', parent:100},

];
var root = MiracleGrow(treeData, "id", "parent")
console.log(root)

2
Це запитання 7 років і вже має проголосовану та прийняту відповідь. Якщо ви вважаєте, що у вас є краще рішення, було б чудово додати трохи пояснень у свій код.
Джорді Небот

Цей підхід добре працює для цього не упорядкованого типу даних.
Cody C

4

Тут знайдено дивовижну версію JavaScript: http://oskarhane.com/create-a-nested-array-recursively-in-javascript/

Скажімо, у вас такий масив:

const models = [
    {id: 1, title: 'hello', parent: 0},
    {id: 2, title: 'hello', parent: 0},
    {id: 3, title: 'hello', parent: 1},
    {id: 4, title: 'hello', parent: 3},
    {id: 5, title: 'hello', parent: 4},
    {id: 6, title: 'hello', parent: 4},
    {id: 7, title: 'hello', parent: 3},
    {id: 8, title: 'hello', parent: 2}
];

І ви хочете, щоб об’єкти вклалися так:

const nestedStructure = [
    {
        id: 1, title: 'hello', parent: 0, children: [
            {
                id: 3, title: 'hello', parent: 1, children: [
                    {
                        id: 4, title: 'hello', parent: 3, children: [
                            {id: 5, title: 'hello', parent: 4},
                            {id: 6, title: 'hello', parent: 4}
                        ]
                    },
                    {id: 7, title: 'hello', parent: 3}
                ]
            }
        ]
    },
    {
        id: 2, title: 'hello', parent: 0, children: [
            {id: 8, title: 'hello', parent: 2}
        ]
    }
];

Ось рекурсивна функція, яка змушує її відбуватися.

function getNestedChildren(models, parentId) {
    const nestedTreeStructure = [];
    const length = models.length;

    for (let i = 0; i < length; i++) { // for-loop for perf reasons, huge difference in ie11
        const model = models[i];

        if (model.parent == parentId) {
            const children = getNestedChildren(models, model.id);

            if (children.length > 0) {
                model.children = children;
            }

            nestedTreeStructure.push(model);
        }
    }

    return nestedTreeStructure;
}

Використання:

const models = [
    {id: 1, title: 'hello', parent: 0},
    {id: 2, title: 'hello', parent: 0},
    {id: 3, title: 'hello', parent: 1},
    {id: 4, title: 'hello', parent: 3},
    {id: 5, title: 'hello', parent: 4},
    {id: 6, title: 'hello', parent: 4},
    {id: 7, title: 'hello', parent: 3},
    {id: 8, title: 'hello', parent: 2}
];
const nestedStructure = getNestedChildren(models, 0);

Для кожного з батьків, якщо це циклічно ціла модель - це не O (N ^ 2)?
Ед Рендалл

4

Для всіх, хто цікавиться C # версією рішення Євгена, зауважте, що node_list доступ до є картою, тому використовуйте Словник замість цього.

Майте на увазі, що це рішення працює лише в тому випадку, якщо таблиця відсортована за родиною_id .

var table = new[]
{
    new Node { parent_id = 0, id = 1 },
    new Node { parent_id = 0, id = 2 },
    new Node { parent_id = 0, id = 3 },
    new Node { parent_id = 1, id = 4 },
    new Node { parent_id = 1, id = 5 },
    new Node { parent_id = 1, id = 6 },
    new Node { parent_id = 2, id = 7 },
    new Node { parent_id = 7, id = 8 },
    new Node { parent_id = 8, id = 9 },
    new Node { parent_id = 3, id = 10 },
};

var root = new Node { id = 0 };
var node_list = new Dictionary<int, Node>{
    { 0, root }
};

foreach (var item in table)
{
    node_list.Add(item.id, item);
    node_list[item.parent_id].children.Add(node_list[item.id]);
}

Вузол визначається наступним чином.

class Node
{
    public int id { get; set; }
    public int parent_id { get; set; }
    public List<Node> children = new List<Node>();
}

1
Він занадто старий, але пункт 8 списку new Node { parent_id = 7, id = 9 },перешкоджає node_list.Add(item.id, item);завершенню, оскільки ключ не може повторитися; це друкарська помилка; тож замість id = 9 введіть id = 8
Marcelo Scofano

Виправлено. Дякую @MarceloScofano!
Джоел Малоун

3

Я написав загальне рішення в C # на основі відповіді @Mehrdad Afshari:

void Example(List<MyObject> actualObjects)
{
  List<TreeNode<MyObject>> treeRoots = actualObjects.BuildTree(obj => obj.ID, obj => obj.ParentID, -1);
}

public class TreeNode<T>
{
  public TreeNode(T value)
  {
    Value = value;
    Children = new List<TreeNode<T>>();
  }

  public T Value { get; private set; }
  public List<TreeNode<T>> Children { get; private set; }
}

public static class TreeExtensions
{
  public static List<TreeNode<TValue>> BuildTree<TKey, TValue>(this IEnumerable<TValue> objects, Func<TValue, TKey> keySelector, Func<TValue, TKey> parentKeySelector, TKey defaultKey = default(TKey))
  {
    var roots = new List<TreeNode<TValue>>();
    var allNodes = objects.Select(overrideValue => new TreeNode<TValue>(overrideValue)).ToArray();
    var nodesByRowId = allNodes.ToDictionary(node => keySelector(node.Value));

    foreach (var currentNode in allNodes)
    {
      TKey parentKey = parentKeySelector(currentNode.Value);
      if (Equals(parentKey, defaultKey))
      {
        roots.Add(currentNode);
      }
      else
      {
        nodesByRowId[parentKey].Children.Add(currentNode);
      }
    }

    return roots;
  }
}

Вибачте, будь ласка, прокоментуйте. Я буду радий дізнатися, що я зробив не так.
HuBeZa

2

Ось Java рішення відповіді Мехрдада Афшарі.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class Tree {

    Iterator<Node> buildTreeAndGetRoots(List<MyObject> actualObjects) {
        Map<Integer, Node> lookup = new HashMap<>();
        actualObjects.forEach(x -> lookup.put(x.id, new Node(x)));
        //foreach (var item in lookup.Values)
        lookup.values().forEach(item ->
                {
                    Node proposedParent;
                    if (lookup.containsKey(item.associatedObject.parentId)) {
                        proposedParent = lookup.get(item.associatedObject.parentId);
                        item.parent = proposedParent;
                        proposedParent.children.add(item);
                    }
                }
        );
        //return lookup.values.Where(x =>x.Parent ==null);
        return lookup.values().stream().filter(x -> x.parent == null).iterator();
    }

}

class MyObject { // The actual object
    public int parentId;
    public int id;
}

class Node {
    public List<Node> children = new ArrayList<Node>();
    public Node parent;
    public MyObject associatedObject;

    public Node(MyObject associatedObject) {
        this.associatedObject = associatedObject;
    }
}

Ви повинні трохи пояснити, яка ваша ідея за кодом.
Зіад Акікі

Це просто переклад Java на попередню відповідь
Vimal Bhatt

1

Неясне, як мені здається питання, я, мабуть, створив би карту від ідентифікатора до фактичного об'єкта. У pseudo-java (я не перевіряв, чи працює / компілюється), це може бути щось на кшталт:

Map<ID, FlatObject> flatObjectMap = new HashMap<ID, FlatObject>();

for (FlatObject object: flatStructure) {
    flatObjectMap.put(object.ID, object);
}

І шукати кожного з батьків:

private FlatObject getParent(FlatObject object) {
    getRealObject(object.ParentID);
}

private FlatObject getRealObject(ID objectID) {
    flatObjectMap.get(objectID);
}

Використовуючи повторно getRealObject(ID)та роблячи карту від об’єкта до колекції об’єктів (або їх ідентифікаторів), ви також отримуєте батьківську -> дитячу карту.


1

Я можу це зробити в 4 рядках коду та часу O (n log n), припускаючи, що словник - це щось на зразок TreeMap.

dict := Dictionary new.
ary do: [:each | dict at: each id put: each].
ary do: [:each | (dict at: each parent) addChild: each].
root := dict at: nil.

EDIT : Гаразд, і тепер я прочитав, що деякі батьківські ідентифікатори підроблені, тому забудьте вище, і зробіть це:

dict := Dictionary new.
dict at: nil put: OrderedCollection new.
ary do: [:each | dict at: each id put: each].
ary do: [:each | 
    (dict at: each parent ifAbsent: [dict at: nil]) 
          add: each].
roots := dict at: nil.

1

Більшість відповідей припускають, що ви хочете зробити це поза межами бази даних. Якщо ваші дерева відносно статичні за своєю природою і вам просто потрібно якимось чином зіставити дерева в базу даних, ви можете розглянути можливість використання вкладених представлень наборів на стороні бази даних. Ознайомтеся з книгами Джо Селко (або тут для огляду Челко).

Якщо ви все-таки прив’язані до db Oracle, перевірте їх CONNECT BY для прямих підходів SQL.

При будь-якому підході ви зможете повністю пропустити картографування дерев, перш ніж завантажувати дані в базу даних. Я просто подумав, що я запропоную це як альтернативу, це може бути абсолютно невідповідно вашим конкретним потребам. Вся частина "належного замовлення" оригінального питання дещо означає, що вам потрібно, щоб замовлення було "правильним" в db чомусь? Це також може підштовхнути мене до обробки дерев.


1

Це зовсім не те саме, що шукав запитувач, але мені було складно обернути голову навколо неоднозначно викладених тут відповідей, і я все ще думаю, що ця відповідь підходить під назвою.

Моя відповідь полягає у відображенні плоскої структури на дереві прямо на об'єкті, де все, що у вас є, є ParentIDна кожному об'єкті. ParentIDє nullабо 0якщо це корінь. Навпроти запитувача, я припускаю, що всі дійсні ParentIDточки вказують на щось інше у списку:

var rootNodes = new List<DTIntranetMenuItem>();
var dictIntranetMenuItems = new Dictionary<long, DTIntranetMenuItem>();

//Convert the flat database items to the DTO's,
//that has a list of children instead of a ParentID.
foreach (var efIntranetMenuItem in flatIntranetMenuItems) //List<tblIntranetMenuItem>
{
    //Automapper (nuget)
    DTIntranetMenuItem intranetMenuItem =
                                   Mapper.Map<DTIntranetMenuItem>(efIntranetMenuItem);
    intranetMenuItem.Children = new List<DTIntranetMenuItem>();
    dictIntranetMenuItems.Add(efIntranetMenuItem.ID, intranetMenuItem);
}

foreach (var efIntranetMenuItem in flatIntranetMenuItems)
{
    //Getting the equivalent object of the converted ones
    DTIntranetMenuItem intranetMenuItem = dictIntranetMenuItems[efIntranetMenuItem.ID];

    if (efIntranetMenuItem.ParentID == null || efIntranetMenuItem.ParentID <= 0)
    {
        rootNodes.Add(intranetMenuItem);
    }
    else
    {
        var parent = dictIntranetMenuItems[efIntranetMenuItem.ParentID.Value];
        parent.Children.Add(intranetMenuItem);
        //intranetMenuItem.Parent = parent;
    }
}
return rootNodes;

1

ось реалізація рубіну:

Він буде каталогізувати ім'я атрибута або результат виклику методу.

CatalogGenerator = ->(depth) do
  if depth != 0
    ->(hash, key) do
      hash[key] = Hash.new(&CatalogGenerator[depth - 1])
    end
  else
    ->(hash, key) do
      hash[key] = []
    end
  end
end

def catalog(collection, root_name: :root, by:)
  method_names = [*by]
  log = Hash.new(&CatalogGenerator[method_names.length])
  tree = collection.each_with_object(log) do |item, catalog|
    path = method_names.map { |method_name| item.public_send(method_name)}.unshift(root_name.to_sym)
  catalog.dig(*path) << item
  end
  tree.with_indifferent_access
end

 students = [#<Student:0x007f891d0b4818 id: 33999, status: "on_hold", tenant_id: 95>,
 #<Student:0x007f891d0b4570 id: 7635, status: "on_hold", tenant_id: 6>,
 #<Student:0x007f891d0b42c8 id: 37220, status: "on_hold", tenant_id: 6>,
 #<Student:0x007f891d0b4020 id: 3444, status: "ready_for_match", tenant_id: 15>,
 #<Student:0x007f8931d5ab58 id: 25166, status: "in_partnership", tenant_id: 10>]

catalog students, by: [:tenant_id, :status]

# this would out put the following
{"root"=>
  {95=>
    {"on_hold"=>
      [#<Student:0x007f891d0b4818
        id: 33999,
        status: "on_hold",
        tenant_id: 95>]},
   6=>
    {"on_hold"=>
      [#<Student:0x007f891d0b4570 id: 7635, status: "on_hold", tenant_id: 6>,
       #<Student:0x007f891d0b42c8
        id: 37220,
        status: "on_hold",
        tenant_id: 6>]},
   15=>
    {"ready_for_match"=>
      [#<Student:0x007f891d0b4020
        id: 3444,
        status: "ready_for_match",
        tenant_id: 15>]},
   10=>
    {"in_partnership"=>
      [#<Student:0x007f8931d5ab58
        id: 25166,
        status: "in_partnership",
        tenant_id: 10>]}}}

1

Прийнята відповідь виглядає для мене занадто складною, тому я додаю її версії Ruby та NodeJS

Припустимо, що список плоских вузлів має таку структуру:

nodes = [
  { id: 7, parent_id: 1 },
  ...
] # ruby

nodes = [
  { id: 7, parentId: 1 },
  ...
] # nodeJS

Функції, які перетворять структуру плоского списку вище в дерево, виглядають наступним чином

для Рубі:

def to_tree(nodes)

  nodes.each do |node|

    parent = nodes.find { |another| another[:id] == node[:parent_id] }
    next unless parent

    node[:parent] = parent
    parent[:children] ||= []
    parent[:children] << node

  end

  nodes.select { |node| node[:parent].nil? }

end

для NodeJS:

const toTree = (nodes) => {

  nodes.forEach((node) => {

    const parent = nodes.find((another) => another.id == node.parentId)
    if(parent == null) return;

    node.parent = parent;
    parent.children = parent.children || [];
    parent.children = parent.children.concat(node);

  });

  return nodes.filter((node) => node.parent == null)

};

Я вважаю, чек на nullпотреби має бутиundefined
Ullauri

@Ullauri null == undefined => truein NodeJS
Hirurg103

1

Один елегантний спосіб зробити це - представити елементи в списку як рядки, що містять розділений крапкою список батьків, і, нарешті, значення:

server.port=90
server.hostname=localhost
client.serverport=90
client.database.port=1234
client.database.host=localhost

Збираючи дерево, ви закінчите щось подібне:

server:
  port: 90
  hostname: localhost
client:
  serverport=1234
  database:
    port: 1234
    host: localhost

У мене є бібліотека конфігурацій, яка реалізує цю конфігурацію заміщення (дерево) з аргументів командного рядка (списку). Алгоритм додавання одного елемента до списку є тут .


0

Ви застрягли, використовуючи лише ці атрибути? Якщо ні, то, можливо, буде непогано створити масив дочірніх вузлів, де ви зможете переглядати всі ці об’єкти один раз, щоб створити такі атрибути. Звідти виберіть вузол з дітьми, але немає батьків і ітеративно будуйте своє дерево зверху вниз.


0

версія java

// node
@Data
public class Node {
    private Long id;
    private Long parentId;
    private String name;
    private List<Node> children = new ArrayList<>();
}

// flat list to tree
List<Node> nodes = new ArrayList();// load nodes from db or network
Map<Long, Node> nodeMap = new HashMap();
nodes.forEach(node -> {
  if (!nodeMap.containsKey(node.getId)) nodeMap.put(node.getId, node);
  if (nodeMap.containsKey(node.getParentId)) {
    Node parent = nodeMap.get(node.getParentId);
    node.setParentId(parent.getId());
    parent.getChildren().add(node);
  }
});

// tree node
List<Node> treeNode = nodeMap .values().stream().filter(n -> n.getParentId() == null).collect(Collectors.toList());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.