Golang中的递归数据结构解析与应用

Golang中的递归数据结构解析与应用 我正在尝试弄清楚如何从扁平结构构建树形结构。 我有以下代码:

package main
import "fmt"

//DataFlat 节点
type DataFlat struct {
    IDGeoRegion int
    IDParent    int
    Description string
}

func main() {

    dFlatlist := []DataFlat{}

    dFlat := DataFlat{IDGeoRegion: 1, IDParent: 0, Description: "Organisation"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 2, IDParent: 1, Description: "Office"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 3, IDParent: 2, Description: "Office Zürich"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 4, IDParent: 2, Description: "Office Brazil"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 5, IDParent: 1, Description: "Storage"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 6, IDParent: 5, Description: "Storge Zürich"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 7, IDParent: 5, Description: "Storge Brazil"}
    dFlatlist = append(dFlatlist, dFlat)

    fmt.Println(dFlatlist)
    //结果: [{1 0 Organisation} {2 1 Office} {3 2 Office Zürich} {4 2 Office Brazil} {5 1 Storage} {6 5 Storge Zürich} {7 5 Storge Brazil}]
}

现在我需要一个递归函数来处理 dFlatList 并将其构建为具有以下格式的树形结构:

type GeoTree struct {
    IDGeoRegion int
    IDParent    int
    Description string
    Children    []*GeoTree
}

任何帮助或建议都将不胜感激。

谢谢


更多关于Golang中的递归数据结构解析与应用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

你好 Shubh,

你的解决方案完美地解决了问题,这正是我一直在寻找的。

非常感谢!

更多关于Golang中的递归数据结构解析与应用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我在if条件处添加了一条注释。你可以创建一个队列,用于存储那些父节点不在映射中的平面节点,然后在for循环之后遍历该队列,并再次执行相同的操作。

分两步构建 GeoTree。第一步,扫描 dataFlat 并通过将 GeoTree 元素以其 ID 作为键存储在映射中来实例化所有元素。在此步骤中,忽略 parentID。第二步,重新扫描 dataFlat,在映射中找到对应的 GeoTree 元素,并将其添加为对应父元素的子元素。

我按照Shubh的建议,为那些父节点不在映射中的节点创建了一个队列,然后对这个队列进行循环处理。

这个方法可行,我可以这样使用它。再次感谢您的帮助!

我很喜欢Christoph的建议,那听起来是个好方法。我需要研究一下,看看能否自己解决这个问题。

我尝试了使用基于映射的方法来创建一棵树。如果我的理解正确,对于子节点来说,IDParent 将是其父节点的 IDGeoRegion。

func (g GeoTree) print() {

	var list []GeoTree

	list = append(list, g)

	i := 0
	for {
		element := list[i]
		fmt.Println(element)
		for _, value := range element.Children {
			list = append(list, *value)
		}
		i++
		if i == len(list) {
			break
		}
	}

}

func addNode(d DataFlat) (g GeoTree) {
	g.IDGeoRegion = d.IDGeoRegion
	g.IDParent = d.IDParent
	g.Description = d.Description
	return
}

在主函数内部添加以下代码:

root := addNode(dFlatlist[0])

m := map[int]*GeoTree{}

m[root.IDGeoRegion] = &root

for i := 1; i < len(dFlatlist); i++ {

	if m[dFlatlist[i].IDParent] == nil {
		fmt.Println("parent does not exist", dFlatlist[i].IDGeoRegion) // 我们可以创建一个队列来存储这些扁平数据,而不是直接打印,然后在此循环之后,重新遍历这个队列并再次尝试添加。
	} else {
		node := addNode(dFlatlist[i])
		m[dFlatlist[i].IDParent].Children = append(m[dFlatlist[i].IDParent].Children, &node)
		m[dFlatlist[i].IDGeoRegion] = &node
	}
}

root.print()

起初,这个方法看起来对我有效,但我遇到了一个问题。上述解决方案似乎要求扁平数据列表的顺序是正确的,但在我的情况下,我不能确定这一点。

如果我构建的扁平列表是无序的,例如,在“Storage”之前插入了“Storage Zürich and Storage Brazil”,那么该解决方案将无法工作。

当唯一能保证的是列表中的第一个条目是根元素,但之后的条目未排序时,是否仍有办法实现这个功能?

以下是由于 dFlatlist 顺序问题而无法正常工作的代码:

package main

import "fmt"

//DataFlat node
type DataFlat struct {
	IDGeoRegion int    `json:"idgeoregion" db:"IdGeoRegion"`
	IDParent    int    `json:"idparent" db:"IdParent"`
	Description string `json:"description" db:"Description"`
}

//GeoTree tree
type GeoTree struct {
	IDGeoRegion int    `json:"idgeoregion" db:"IdGeoRegion"`
	IDParent    int    `json:"idparent" db:"IdParent"`
	Description string `json:"description" db:"Description"`
	Children    []*GeoTree
}

func main() {

	dFlatlist := []DataFlat{}

	dFlat := DataFlat{IDGeoRegion: 1, IDParent: 0, Description: "Organisation"}
	dFlatlist = append(dFlatlist, dFlat)

	dFlat = DataFlat{IDGeoRegion: 6, IDParent: 5, Description: "Storge Zürich"}
	dFlatlist = append(dFlatlist, dFlat)

	dFlat = DataFlat{IDGeoRegion: 7, IDParent: 5, Description: "Storge Brazil"}
	dFlatlist = append(dFlatlist, dFlat)

	dFlat = DataFlat{IDGeoRegion: 2, IDParent: 1, Description: "Office"}
	dFlatlist = append(dFlatlist, dFlat)

	dFlat = DataFlat{IDGeoRegion: 3, IDParent: 2, Description: "Office Zürich"}
	dFlatlist = append(dFlatlist, dFlat)

	dFlat = DataFlat{IDGeoRegion: 4, IDParent: 2, Description: "Office Brazil"}
	dFlatlist = append(dFlatlist, dFlat)

	dFlat = DataFlat{IDGeoRegion: 5, IDParent: 1, Description: "Storage"}
	dFlatlist = append(dFlatlist, dFlat)

	//fmt.Println(dFlatlist)
	//Result: [{1 0 Organisation} {2 1 Office} {3 2 Office Zürich} {4 2 Office Brazil} {5 1 Storage} {6 5 Storge Zürich} {7 5 Storge Brazil}]
	root := addNode(dFlatlist[0])

	m := map[int]*GeoTree{}

	m[root.IDGeoRegion] = &root

	for i := 1; i < len(dFlatlist); i++ {

		if m[dFlatlist[i].IDParent] == nil {
			fmt.Println("parent does not exist", dFlatlist[i].IDGeoRegion)
		} else {
			node := addNode(dFlatlist[i])
			m[dFlatlist[i].IDParent].Children = append(m[dFlatlist[i].IDParent].Children, &node)
			m[dFlatlist[i].IDGeoRegion] = &node
		}
	}

	root.print()
}

func (g GeoTree) print() {
	var list []GeoTree
	list = append(list, g)
	i := 0
	for {
		element := list[i]
		fmt.Println(element)
		for _, value := range element.Children {
			list = append(list, *value)
		}
		i++
		if i == len(list) {
			break
		}
	}
}
func addNode(d DataFlat) (g GeoTree) {
	g.IDGeoRegion = d.IDGeoRegion
	g.IDParent = d.IDParent
	g.Description = d.Description
	return
}

以下是一个从扁平结构构建树形结构的递归实现:

package main

import "fmt"

// DataFlat 扁平节点
type DataFlat struct {
    IDGeoRegion int
    IDParent    int
    Description string
}

// GeoTree 树形节点
type GeoTree struct {
    IDGeoRegion int
    IDParent    int
    Description string
    Children    []*GeoTree
}

// BuildTree 构建树形结构的递归函数
func BuildTree(flatData []DataFlat, parentID int) []*GeoTree {
    var tree []*GeoTree
    
    for _, item := range flatData {
        if item.IDParent == parentID {
            node := &GeoTree{
                IDGeoRegion: item.IDGeoRegion,
                IDParent:    item.IDParent,
                Description: item.Description,
                Children:    BuildTree(flatData, item.IDGeoRegion),
            }
            tree = append(tree, node)
        }
    }
    
    return tree
}

// PrintTree 递归打印树形结构
func PrintTree(tree []*GeoTree, level int) {
    for _, node := range tree {
        indent := ""
        for i := 0; i < level; i++ {
            indent += "  "
        }
        fmt.Printf("%s%d: %s\n", indent, node.IDGeoRegion, node.Description)
        if len(node.Children) > 0 {
            PrintTree(node.Children, level+1)
        }
    }
}

func main() {
    dFlatlist := []DataFlat{}

    dFlat := DataFlat{IDGeoRegion: 1, IDParent: 0, Description: "Organisation"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 2, IDParent: 1, Description: "Office"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 3, IDParent: 2, Description: "Office Zürich"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 4, IDParent: 2, Description: "Office Brazil"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 5, IDParent: 1, Description: "Storage"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 6, IDParent: 5, Description: "Storge Zürich"}
    dFlatlist = append(dFlatlist, dFlat)

    dFlat = DataFlat{IDGeoRegion: 7, IDParent: 5, Description: "Storge Brazil"}
    dFlatlist = append(dFlatlist, dFlat)

    // 构建树形结构
    tree := BuildTree(dFlatlist, 0)
    
    // 打印树形结构
    fmt.Println("树形结构:")
    PrintTree(tree, 0)
    
    // 访问特定节点
    fmt.Println("\n根节点及其子节点:")
    for _, root := range tree {
        fmt.Printf("根节点: %s (ID: %d)\n", root.Description, root.IDGeoRegion)
        for _, child := range root.Children {
            fmt.Printf("  子节点: %s (ID: %d)\n", child.Description, child.IDGeoRegion)
        }
    }
}

输出结果:

树形结构:
1: Organisation
  2: Office
    3: Office Zürich
    4: Office Brazil
  5: Storage
    6: Storge Zürich
    7: Storge Brazil

根节点及其子节点:
根节点: Organisation (ID: 1)
  子节点: Office (ID: 2)
  子节点: Storage (ID: 5)

如果需要更高效的实现,可以使用映射来减少递归搜索:

// BuildTreeEfficient 使用映射构建树形结构
func BuildTreeEfficient(flatData []DataFlat) []*GeoTree {
    // 创建映射:ID -> 节点
    nodeMap := make(map[int]*GeoTree)
    
    // 创建所有节点
    for _, item := range flatData {
        nodeMap[item.IDGeoRegion] = &GeoTree{
            IDGeoRegion: item.IDGeoRegion,
            IDParent:    item.IDParent,
            Description: item.Description,
            Children:    []*GeoTree{},
        }
    }
    
    // 构建树形结构
    var roots []*GeoTree
    for _, node := range nodeMap {
        if node.IDParent == 0 {
            roots = append(roots, node)
        } else {
            if parent, exists := nodeMap[node.IDParent]; exists {
                parent.Children = append(parent.Children, node)
            }
        }
    }
    
    return roots
}

这个实现的时间复杂度为O(n),比纯递归实现更高效。

回到顶部