【算法框架套路】构造无限级树型结构,附上python/golang/php/js实现

前言

框架思维非常重要,和语言无关,这是一种非常重要的抽象能力,吹得厉害一点,就是要有高屋建瓴,统筹全局的能力。
无限级树型结构的创建也是有套路的,下面由伟大的诗人chenqionghe给出套路框架,再分别套上相应的编程语言实现。
最终达到看到框架就能实现对应编程语言的代码,或者也可以直接copy实现拿去用~

迭代实现参考了这篇文章的思路列表list转树形结构:(python/golang/js/php),童鞋们可以看看这篇文章

需求

给定一级列表,构造出指定父ID的无限级树型结果

数据

[
  {'id': 1, 'parent_id': 0, 'name': "A"},
  {'id': 2, 'parent_id': 1, 'name': "AA"},
  {'id': 3, 'parent_id': 1, 'name': "AB"},
  {'id': 4, 'parent_id': 3, 'name': "ABA"},
  {'id': 5, 'parent_id': 3, 'name': "ABB"},
  {'id': 6, 'parent_id': 3, 'name': "ABC"},
  {'id': 7, 'parent_id': 1, 'name': "AC"},
  {'id': 8, 'parent_id': 7, 'name': "ACA"},
  {'id': 9, 'parent_id': 8, 'name': "ACAA"},
  {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

结果

最后构造成一棵根据父ID连接起来的树结构

{
    "id": 1,
    "parent_id": 0,
    "name": "A",
    "children": [
        {
            "id": 2,
            "parent_id": 1,
            "name": "AA",
            "children": []
        },
        {
            "id": 3,
            "parent_id": 1,
            "name": "AB",
            "children": [
                {
                    "id": 4,
                    "parent_id": 3,
                    "name": "ABA",
                    "children": []
                },
                {
                    "id": 5,
                    "parent_id": 3,
                    "name": "ABB",
                    "children": []
                },
                {
                    "id": 6,
                    "parent_id": 3,
                    "name": "ABC",
                    "children": []
                }
            ]
        },
        {
            "id": 7,
            "parent_id": 1,
            "name": "AC",
            "children": [
                {
                    "id": 8,
                    "parent_id": 7,
                    "name": "ACA",
                    "children": [
                        {
                            "id": 9,
                            "parent_id": 8,
                            "name": "ACAA",
                            "children": []
                        },
                        {
                            "id": 10,
                            "parent_id": 8,
                            "name": "ACAB",
                            "children": []
                        }
                    ]
                }
            ]
        }
    ]
}

框架

递归框架

递归是一门艺术,用接近人类的语言来表达了程序,优点是代码较少,缺点是性能较差。

获取树(列表,父ID)
   res = []
   for 节点 in 列表:
        if 节点的parent_id 等于 父ID
            节点.children = 获取树(列表, 节点ID)
            res.add(节点)
   return res

迭代框架

迭代实现本质是创建一条引用链,将所有的节点串起来

获取树(列表,父ID)
   memo = {}
   for 节点 in 列表:
        //构造memo给节点的父ID查找追加节点用
        if 节点ID in memo:
            节点.children = memo[节点ID].children //之前构造的children数组覆盖当前节点的children
            memo[节点ID] = 节点
        else
            节点.children = []
            memo[节点ID] = 节点
        
        //给像父对象的children追加
        if 节点父ID in memo:
            memo[节点父ID].children.add(memo[节点ID]) //追加当前构造的ID节点
        else:
            memo[节点父ID] = {'children':[memo[节点ID]]} //初始化父对象再追加
            
   return memo[父ID].children

递归框架实现

python

def get_tree_iterative(list_data, parent_id=0):
    memo = {}
    for v in list_data:
        item_id = v['id']
        item_paren_id = v['parent_id']
        if item_id in memo:
            v['children'] = memo[item_id]['children']
            memo[item_id] = v
        else:
            v['children'] = []
            memo[item_id] = v

        if item_paren_id in memo:
            memo[item_paren_id]['children'].append(memo[item_id])
        else:
            memo[item_paren_id] = {'children': memo[item_id]}
    return memo[parent_id]['children']


list_data = [
    {'id': 1, 'parent_id': 0, 'name': "A"},
    {'id': 2, 'parent_id': 1, 'name': "AA"},
    {'id': 3, 'parent_id': 1, 'name': "AB"},
    {'id': 4, 'parent_id': 3, 'name': "ABA"},
    {'id': 5, 'parent_id': 3, 'name': "ABB"},
    {'id': 6, 'parent_id': 3, 'name': "ABC"},
    {'id': 7, 'parent_id': 1, 'name': "AC"},
    {'id': 8, 'parent_id': 7, 'name': "ACA"},
    {'id': 9, 'parent_id': 8, 'name': "ACAA"},
    {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

res = get_tree_iterative(list_data)

import json

print(json.dumps(res, indent=4))

运行如下

golang

package main

import (
    "encoding/json"
    "fmt"
)

type Node struct {
    Id       int     `json:"id"`
    ParentId int     `json:"parent_id"`
    Name     string  `json:"name"`
    Children []*Node `json:"children"`
}

func getTreeRecursive(list []*Node, parentId int) []*Node {
    res := make([]*Node, 0)
    for _, v := range list {
        if v.ParentId == parentId {
            v.Children = getTreeRecursive(list, v.Id)
            res = append(res, v)
        }
    }
    return res
}

func main() {
    list := []*Node{
        {4, 3, "ABA", nil},
        {3, 1, "AB", nil},
        {1, 0, "A", nil},
        {2, 1, "AA", nil},
        {5, 3, "ABB", nil},
        {6, 3, "ABC", nil},
        {7, 1, "AC", nil},
        {8, 7, "ACA", nil},
        {9, 8, "ACAA", nil},
        {10, 8, "ACAB", nil},
    }
    res := getTreeRecursive(list, 0)
    bytes, _ := json.MarshalIndent(res, "", "    ")
    fmt.Printf("%s\n", bytes)
}

运行如下

php

function getTreeRecursive(&$list, $parentId = 0)
{
    $res = [];
    foreach ($list as $k => $v) {
        if ($v['parent_id'] == $parentId) {
            $v['children'] = getTreeRecursive($list, $v['id']);
            $res[] = $v;
        }
    }
    return $res;
}
$list = [
    ['id' => 4, 'parent_id' => 3, 'name' => "ABA"],
    ['id' => 3, 'parent_id' => 1, 'name' => "AB"],
    ['id' => 1, 'parent_id' => 0, 'name' => "A"],
    ['id' => 2, 'parent_id' => 1, 'name' => "AA"],
    ['id' => 5, 'parent_id' => 3, 'name' => "ABB"],
    ['id' => 6, 'parent_id' => 3, 'name' => "ABC"],
    ['id' => 7, 'parent_id' => 1, 'name' => "AC"],
    ['id' => 8, 'parent_id' => 7, 'name' => "ACA"],
    ['id' => 9, 'parent_id' => 8, 'name' => "ACAA"],
    ['id' => 10, 'parent_id' => 8, 'name' => "ACAB"],
];
$res = getTreeRecursive($list);
echo json_encode($res, JSON_PRETTY_PRINT);

运行结果如下

js

function getTreeRecursive(listData, parentId = 0) {
  let res = []
  listData.forEach((v, k) => {
    if (v.parent_id == parentId) {
      v.children = getTreeRecursive(listData, v.id)
      res.push(v)
    }
  })
  return res
}


dataList = [
  {'id': 1, 'parent_id': 0, 'name': "A"},
  {'id': 2, 'parent_id': 1, 'name': "AA"},
  {'id': 3, 'parent_id': 1, 'name': "AB"},
  {'id': 4, 'parent_id': 3, 'name': "ABA"},
  {'id': 5, 'parent_id': 3, 'name': "ABB"},
  {'id': 6, 'parent_id': 3, 'name': "ABC"},
  {'id': 7, 'parent_id': 1, 'name': "AC"},
  {'id': 8, 'parent_id': 7, 'name': "ACA"},
  {'id': 9, 'parent_id': 8, 'name': "ACAA"},
  {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

let res = getTreeRecursive(dataList);
console.log((JSON.stringify(res, null, 4)))

运行如下

迭代框架实现

python

def get_tree_iterative(list_data, parent_id=0):
    memo = {}
    for v in list_data:
        item_id = v['id']
        item_paren_id = v['parent_id']
        if item_id in memo:
            v['children'] = memo[item_id]['children']
            memo[item_id] = v
        else:
            v['children'] = []
            memo[item_id] = v

        if item_paren_id in memo:
            memo[item_paren_id]['children'].append(memo[item_id])
        else:
            memo[item_paren_id] = {'children': [memo[item_id]]}
    return memo[parent_id]['children']

list_data = [
    {'id': 1, 'parent_id': 0, 'name': "A"},
    {'id': 2, 'parent_id': 1, 'name': "AA"},
    {'id': 3, 'parent_id': 1, 'name': "AB"},
    {'id': 4, 'parent_id': 3, 'name': "ABA"},
    {'id': 5, 'parent_id': 3, 'name': "ABB"},
    {'id': 6, 'parent_id': 3, 'name': "ABC"},
    {'id': 7, 'parent_id': 1, 'name': "AC"},
    {'id': 8, 'parent_id': 7, 'name': "ACA"},
    {'id': 9, 'parent_id': 8, 'name': "ACAA"},
    {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

res = get_tree_iterative(list_data)

import json

print(json.dumps(res, indent=4))

运行如下

golang

package main

import (
    "encoding/json"
    "fmt"
)

type Node struct {
    Id       int     `json:"id"`
    ParentId int     `json:"parent_id"`
    Name     string  `json:"name"`
    Children []*Node `json:"children"`
}

func getTreeIterative(list []*Node, parentId int) []*Node {
    memo := make(map[int]*Node)
    for _, v := range list {
        if _, ok := memo[v.Id]; ok {
            v.Children = memo[v.Id].Children
            memo[v.Id] = v
        } else {
            v.Children = make([]*Node, 0)
            memo[v.Id] = v
        }
        if _, ok := memo[v.ParentId]; ok {
            memo[v.ParentId].Children = append(memo[v.ParentId].Children, memo[v.Id])
        } else {
            memo[v.ParentId] = &Node{Children: []*Node{memo[v.Id]}}
        }
    }
    return memo[parentId].Children

}

func main() {
    list := []*Node{
        {4, 3, "ABA", nil},
        {3, 1, "AB", nil},
        {1, 0, "A", nil},
        {2, 1, "AA", nil},
        {5, 3, "ABB", nil},
        {6, 3, "ABC", nil},
        {7, 1, "AC", nil},
        {8, 7, "ACA", nil},
        {9, 8, "ACAA", nil},
        {10, 8, "ACAB", nil},
    }
    res := getTreeIterative(list, 0)
    bytes, _ := json.MarshalIndent(res, "", "    ")
    fmt.Printf("%s\n", bytes)
}

运行如下

php

function getTreeIterative($list, $parentId = 0)
{
    $memo = [];
    foreach ($list as &$v) {
        $id = $v['id'];
        $itemParentId = $v['parent_id'];
        if (isset($memo[$id])) {
            $v['children'] = &$memo[$id]['children'];
            $memo[$id] = $v;
        } else {
            $v['children'] = [];
            $memo[$id] = $v;
        }
        if (isset($memo[$itemParentId])) {
            $memo[$itemParentId]['children'][] = &$memo[$id];
        } else {
            $memo[$itemParentId] = ['children' => [&$memo[$id]]];
        }
    }
    return $memo[$parentId]['children'];
}


$list = [
    ['id' => 4, 'parent_id' => 3, 'name' => "ABA"],
    ['id' => 3, 'parent_id' => 1, 'name' => "AB"],
    ['id' => 1, 'parent_id' => 0, 'name' => "A"],
    ['id' => 2, 'parent_id' => 1, 'name' => "AA"],
    ['id' => 5, 'parent_id' => 3, 'name' => "ABB"],
    ['id' => 6, 'parent_id' => 3, 'name' => "ABC"],
    ['id' => 7, 'parent_id' => 1, 'name' => "AC"],
    ['id' => 8, 'parent_id' => 7, 'name' => "ACA"],
    ['id' => 9, 'parent_id' => 8, 'name' => "ACAA"],
    ['id' => 10, 'parent_id' => 8, 'name' => "ACAB"],
];
$res = getTreeIterative($list);
echo json_encode($res, JSON_PRETTY_PRINT);

运行结果如下

js

function getTreeIterative(listData, parentId = 0) {
  let memo = {};
  listData.forEach((v, k) => {
    let id = v.id
    let itemParentId = v.parent_id

    if (memo[id]) {
      v.children = memo[id].children
      memo[id] = v
    } else {
      v.children = []
      memo[id] = v;
    }

    if (memo[itemParentId]) {
      memo[itemParentId].children.push(memo[id]);
    } else {
      memo[itemParentId] = {children: [memo[id]]};
    }
  })

  return memo[parentId].children
}


dataList = [
  {'id': 1, 'parent_id': 0, 'name': "A"},
  {'id': 2, 'parent_id': 1, 'name': "AA"},
  {'id': 3, 'parent_id': 1, 'name': "AB"},
  {'id': 4, 'parent_id': 3, 'name': "ABA"},
  {'id': 5, 'parent_id': 3, 'name': "ABB"},
  {'id': 6, 'parent_id': 3, 'name': "ABC"},
  {'id': 7, 'parent_id': 1, 'name': "AC"},
  {'id': 8, 'parent_id': 7, 'name': "ACA"},
  {'id': 9, 'parent_id': 8, 'name': "ACAA"},
  {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

let res = getTreeIterative(dataList);
console.log((JSON.stringify(res, null, 4)))

运行如下

扩展

添加level

如果给节点添加一个处于的层级属性怎么办,其实用递归很简单,只要增加一个参数就行了,下面给出python代码

  • 递归实现添加level
def get_tree_recursive(list_data, parent_id=0, level=0):
    res = []
    for v in list_data:
        if v['parent_id'] == parent_id:
            v['level'] = level
            v['children'] = get_tree_recursive(list_data, v['id'], level + 1)
            res.append(v)
    return res


list_data = [
    {'id': 1, 'parent_id': 0, 'name': "A"},
    {'id': 2, 'parent_id': 1, 'name': "AA"},
    {'id': 3, 'parent_id': 1, 'name': "AB"},
    {'id': 4, 'parent_id': 3, 'name': "ABA"},
    {'id': 5, 'parent_id': 3, 'name': "ABB"},
    {'id': 6, 'parent_id': 3, 'name': "ABC"},
    {'id': 7, 'parent_id': 1, 'name': "AC"},
    {'id': 8, 'parent_id': 7, 'name': "ACA"},
    {'id': 9, 'parent_id': 8, 'name': "ACAA"},
    {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

res = get_tree_recursive(list_data)

import json

print(json.dumps(res, indent=4))

运行输出

  • 迭代实现添加level
    这里就尴尬了,虽然是迭代,添加level还是得递归一下,这里加了一个add_tree_level方法
def get_tree_recursive(list_data, parent_id=0, level=0):
    res = []
    for v in list_data:
        if v['parent_id'] == parent_id:
            v['level'] = level
            v['children'] = get_tree_recursive(list_data, v['id'], level + 1)
            res.append(v)
    return res


def get_tree_iterative(list_data, parent_id=0):
    memo = {}
    for v in list_data:
        item_id = v['id']
        item_paren_id = v['parent_id']
        if item_id in memo:
            v['children'] = memo[item_id]['children']
            memo[item_id] = v
        else:
            v['children'] = []
            memo[item_id] = v

        if item_paren_id in memo:
            memo[item_paren_id]['children'].append(memo[item_id])
        else:
            memo[item_paren_id] = {'children': [memo[item_id]]}

    res = memo[parent_id]['children']
    return add_tree_level(res)


def add_tree_level(children, level=0):
    for v in children:
        v['level'] = level
        if len(v['children']) > 0:
            add_tree_level(v['children'], level + 1)
    return children


list_data = [
    {'id': 1, 'parent_id': 0, 'name': "A"},
    {'id': 2, 'parent_id': 1, 'name': "AA"},
    {'id': 3, 'parent_id': 1, 'name': "AB"},
    {'id': 4, 'parent_id': 3, 'name': "ABA"},
    {'id': 5, 'parent_id': 3, 'name': "ABB"},
    {'id': 6, 'parent_id': 3, 'name': "ABC"},
    {'id': 7, 'parent_id': 1, 'name': "AC"},
    {'id': 8, 'parent_id': 7, 'name': "ACA"},
    {'id': 9, 'parent_id': 8, 'name': "ACAA"},
    {'id': 10, 'parent_id': 8, 'name': "ACAB"},
]

res = get_tree_iterative(list_data)

import json

print(json.dumps(res, indent=4))

运行输出

以后如果想用直接来拷贝代码就行,就是这么简单~

posted @ 2021-06-10 14:08  雪山飞猪  阅读(668)  评论(0编辑  收藏  举报