使用递归计算树类结构

时间:2015-08-17 11:44:22

标签: javascript algorithm

我有一个如下所示的数据结构,基本上data是一个包含节点列表的数组,每个节点都可以与parent节点连接,创建一个类似树的结构。

每个节点都有rotation_relative属性,这是它的相对旋转。

由于节点可以嵌套在任何级别,我需要根据其父节点为每个节点计算属性rotation_absolute(我已经为下面树中的每个节点添加了最终结果)。

基本上,叶子的rotation_absolute等于其父母在正确路径中的总和。

考虑到:

  • data数组中的节点可以按任意顺序放置。
  • 可能存在无限数量的嵌套(节点内的节点)。
  • 数据可能包含数百个节点

你能建议任何算法来解决这个问题吗?

A
|_B
| |_F (rotation_absolute = -20)
|
|_C
  |_D (rotation_absolute = 20)
    |_E (rotation_absolute = 40)

X (rotation_absolute = 20)
|_J (rotation_absolute = 0)
|_Y (rotation_absolute = 40)

Code example

 <!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Test</title>
    <script>
        window.app = {
            data: [
                {
                    id: 'j',
                    parent: 'x',
                    rotation_relative: 20,
                    rotation_absolute: null
                },
                {
                    id: 'y',
                    parent: 'x',
                    rotation_relative: 20,
                    rotation_absolute: null
                },
                {
                    id: 'a',
                    parent: '',
                    rotation_relative: 0,
                    rotation_absolute: null
                },
                {
                    id: 'f',
                    parent: 'b',
                    rotation_relative: -20,
                    rotation_absolute: null
                },
                {
                    id: 'b',
                    parent: 'a',
                    rotation_relative: 0,
                    rotation_absolute: null
                },
                {
                    id: 'e',
                    parent: 'd',
                    rotation_relative: 20,
                    rotation_absolute: null
                },
                {
                    id: 'x',
                    parent: '',
                    rotation_relative: 20,
                    rotation_absolute: null
                },
                {
                    id: 'c',
                    parent: 'a',
                    rotation_relative: 0,
                    rotation_absolute: null
                },
                {
                    id: 'd',
                    parent: 'c',
                    rotation_relative: 20,
                    rotation_absolute: null
                },
            ],
            start: function () {
                // get root
                var get1Level = function (parent) {
                    var nodes1L = this.data.filter(function (item) {
                        if (item.parent === parent) {
                            return true;
                        } else {
                            return false;
                        }
                    }, this);
                    return nodes1L;
                }.bind(this);

                var recursion = function (id) {
                    var nodes = get1Level(id);
                    nodes.forEach(function (item) {
                        console.log(item);
                        recursion.call(this, item.id);
                        console.log('--');
                    }, this);

                }.bind(this);


                var roots = recursion.call(this, '');
            }
        };
    </script>
</head>

注意:

我意识到标题可能真的具有描述性,请随意向我推荐一个更好的标题。谢谢大家。

这是我正在处理的递归示例,但我在添加值部分时遇到了一些问题。

https://jsbin.com/qexeqasema/1/edit?html,console,output

3 个答案:

答案 0 :(得分:1)

如果你不介意使用方法,你可以这样做

function rotation_absolute() {
    return this.rotation_relative + (this.parentNode ? this.parentNode.rotation_absolute() : 0);
}

var result = {};
var address = {};
window.app.data.forEach(function (e) {
    address[e.id] = e;
    e.rotation_absolute = rotation_absolute.bind(e);

    // see if we have a set of (preceding) nodes with this one as parent
    if (result[e.id] instanceof Array) {
        result[e.id].forEach(function (c) {
            c.parentNode = e;
            e[c.id] = c;
        });
        delete result[e.id];
    }

    // top level nodes
    if (e.parent === "") {
        result[e.id] = e;
    }
    else
    {
        var parent = address[e.parent]
        if (parent !== undefined) {
            // add to parent
            parent[e.id] = e;
            e.parentNode = parent;
        }
        else {
            // add to top level temporary array
            result[e.parent] = result[e.parent] || [];
            result[e.parent].push(e);
        }
    }

    // don't actually need to do this - I just added it to remove the clutter in the console
    delete e.rotation_rotation;
    delete e.parent;
})

console.log(result)
console.log(result.a.c.d.e.rotation_absolute())

或者您可以通过构建的层次结构进行递归并使用该方法设置值。

答案 1 :(得分:0)

如果可以限制树的维度,可以尝试嵌套for循环。然后树的每个级别都是一个for循环。树的无限维度并不容易。也许你可以尝试一种递归算法。

答案 2 :(得分:0)

我能够使用递归加上一些额外的逻辑来处理兄弟元素的旋转。

Working example

  <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8">
        <title>Test</title>
        <script>
            window.app = {
                data: [
                    {
                        id: 'a',
                        parent: 'area',
                        rotation_relative: 0,
                        rotation_absolute: null
                    },
                        {
                            id: 'b',
                            parent: 'a',
                            rotation_relative: 0,
                            rotation_absolute: null
                        },
                            {
                                id: 'c',
                                parent: 'b',
                                rotation_relative: -10,
                                rotation_absolute: null
                            },
                        {
                            id: 'd',
                            parent: 'a',
                            rotation_relative: 0,
                            rotation_absolute: null
                        },
                            {
                                id: 'e',
                                parent: 'd',
                                rotation_relative: 0,
                                rotation_absolute: null
                            },
                                {
                                    id: 'f',
                                    parent: 'e',
                                    rotation_relative: 10,
                                    rotation_absolute: null
                                },
                                    {
                                        id: 'g',
                                        parent: 'f',
                                        rotation_relative: 10,
                                        rotation_absolute: null
                                    },
            {
                id: 'area',
                parent: '',
                rotation_relative: 0,
                rotation_absolute: null
            },
                {
                    id: 'h',
                    parent: 'area',
                    rotation_relative: 10,
                    rotation_absolute: null
                },
                    {
                        id: 'i',
                        parent: 'h',
                        rotation_relative: -10,
                        rotation_absolute: null
                    },
                    {
                        id: 'l',
                        parent: 'h',
                        rotation_relative: 10,
                        rotation_absolute: null
                    },
            {
                id: 'x',
                parent: 'area',
                rotation_relative: 0,
                rotation_absolute: null
            },
                {
                    id: 'y',
                    parent: 'x',
                    rotation_relative: -10,
                    rotation_absolute: null
                },
                    {
                        id: 'q',
                        parent: 'y',
                        rotation_relative: 10,
                        rotation_absolute: null
                    },
                        {
                            id: 'o',
                            parent: 'q',
                            rotation_relative: -10,
                            rotation_absolute: null
                        },

            {
                id: 'w',
                parent: 'area',
                rotation_relative: 10,
                rotation_absolute: null
            },

                {
                    id: 'z',
                    parent: 'w',
                    rotation_relative: -10,
                    rotation_absolute: null
                },
                {
                    id: 'j',
                    parent: 'w',
                    rotation_relative: -10,
                    rotation_absolute: null
                },

            {
                id: 's',
                parent: 'area',
                rotation_relative: 10,
                rotation_absolute: null
            },
                {
                    id: 't',
                    parent: 's',
                    rotation_relative: -10,
                    rotation_absolute: null
                },
                {
                    id: 'v',
                    parent: 's',
                    rotation_relative: 10,
                    rotation_absolute: null
                },

            {
                id: 'm',
                parent: 'area',
                rotation_relative: -10,
                rotation_absolute: null
            },
                {
                    id: 'n',
                    parent: 'm',
                    rotation_relative: 10,
                    rotation_absolute: null
                },
                {
                    id: 'i',
                    parent: 'm',
                    rotation_relative: 10,
                    rotation_absolute: null
                },
                ],
                getById: function (id) {
                    var result = null;
                    for (var i = 0, len = this.data.length; i < len; i++) {
                        var item = this.data[i];
                        if (item.id === id) {
                            result = item;
                        }
                    }
                    return result;
                },
                start: function () {
                    // get root
                    var get1Level = function (parent) {
                        var nodes1L = this.data.filter(function (item) {
                            if (item.parent === parent) {
                                return true;
                            } else {
                                return false;
                            }
                        }, this);
                        return nodes1L;
                    }.bind(this);

                    var increment = 0;
                    var parent = null;

                    var recursion = function (id) {
                        var nodes = get1Level(id);
                        nodes.forEach(function (item) {
                            if (!parent) {
                                parent = item.parent;
                            }
                            if (parent && parent === item.parent) {
                                var targetParent = this.getById(parent);
                                increment = targetParent.rotation_relative;
                                increment += item.rotation_relative;
                                item.rotation_absolute = increment;

                            }
                            if (parent && parent !== item.parent) {
                                parent = item.parent;
                                var targetParent = this.getById(parent);
                                increment = targetParent.rotation_absolute ? targetParent.rotation_absolute : 0;
                                increment += item.rotation_relative;
                                item.rotation_absolute = increment;
                            }



                            console.log(item);
                            recursion.call(this, item.id);
                            console.log('---');
                        }, this);

                        //increment = 0;
                        //parent = null;

                    }.bind(this);


                    recursion.call(this, 'area');



                }
            };
        </script>
    </head>
    <body onload="window.app.start();">
    </body>
    </html>