python 实现多叉树重复值合并

原始应用场景:

1. 扫描系统中的image,在release的前夕确保系统的image tag是可release的版本。
2. 追溯上游image(base),保证上游base image版本同样达到release要求,且可控。

那么如何做到溯源,要求docker file中From 或者Label字段上记录该image的上游。如果能保证按照这条规则执行,那么我们会得到类似于这样的信息:

    centos-base:1.0.0-->openjdk:1.8.0-->product:2.0.0
    ubuntu-base:1.0.1-->tomcat:1.3.0-->product1:2.0.1
    centos-base:1.0.0-->openjdk:1.8.1-->product:2.0.1
    ...

继续思考:观察数据会发现好多image会有同样的base image,那么针对这些image,我们需要将他们归并到一条分支上。或者说要找出他们的祖先,将他们归并道对应的祖先分支上。那么思想和树很像了,决定用树形结构来处理。

简化抽象需求,画出简图如下:

jaymz
├── abc
│   ├── abcd
│   └── shg
│       └── du
├── ef
│   ├── abcd
│   │   ├── mn
│   │   │   └── df
│   │   ├── xyz
│   │   └── xyz(重复节点)
│   └── gh
├── abc(重复节点)
│   └── abcd(重复节点)
│       └── sh
└── abc(重复节点)

设计:

使用python treelib模块构造多叉树:

tree = Tree()
tree.create_node('root','root',data = NodeX('jaymz'))
tree.create_node("child1","child1",parent="root",data=NodeX("abc"))
tree.create_node("child11","child11",parent="root",data=NodeX("ef"))
tree.create_node("child111","child111",parent="root",data=NodeX("abc"))
tree.create_node("child1111","child1111",parent="root",data=NodeX("abc"))
tree.create_node("child2","child2",parent="child1",data=NodeX("abcd"))
tree.create_node("child22","child22",parent="child1",data=NodeX("shg"))
tree.create_node("child222","child222",parent="child11",data=NodeX("abcd"))
tree.create_node("child2222","child2222",parent="child11",data=NodeX("gh"))
tree.create_node("child22222","child22222",parent="child111",data=NodeX("abcd"))
tree.create_node("child3","child3",parent="child22",data=NodeX("du"))
tree.create_node("child33","child33",parent="child222",data=NodeX("mn"))
tree.create_node("child333","child333",parent="child222",data=NodeX("xyz"))
tree.create_node("child3333","child3333",parent="child222",data=NodeX("xyz"))
tree.create_node("child33333","child33333",parent="child22222",data=NodeX("sh"))
tree.create_node("child4","child4",parent="child33",data=NodeX("df"))

将每一层的树节点的id,编为child1,child11...child2,child22...同一层后缀数字一样,个数不一样,不同层数字不一样,以此类推。这样的好处就是我能根据节点ID,就知道它属于第几层。

合并思路:

同层节点比较,将第一个节点的数值作为base value,通过tree.children(parentNodeTag),获取同层的子节点,如果发现有相同的数值,将其ID记录到变量中,等处理完后统一从tree中remove。当然在remove之前需要将其子树移动到相同值的节点上。

流程图如下:

WeChat Screenshot_20190513175033.png

代码部分:

#!/usr/bin/env python
import treelib
from treelib import Tree, Node

class NodeX(object):
    def __init__(self,num):
        self.num = num

def moveNode(tree,source,dentinate):
    if len(source)==1:
        tree.move_node(''.join(source),dentinate)
    else:
        for var in source:
            tree.move_node(var,dentinate)

def getChildrenNodeTag(tree,nodeTag):
    childrenNodeTag = tree.children(nodeTag)
    tags = []
    for i in childrenNodeTag:
        tags.append(i.tag)
    return tags

def compareSameFloorAndRemoveDupliNode(tree,nodeTag):
    dupltiNodeTag = []
    firstNodeTag = ""
    if tree.children(nodeTag):
        nodeCount = len(tree.children(nodeTag))
        tempValue = []
        for m in range(nodeCount):
            # print("tempValue",tempValue)
            nodeValue = tree.children(nodeTag)[m]
            if len(tempValue)==0:
                tempValue.append(nodeValue.data.num)
                firstNodeTag = nodeValue.tag
            else:
                if nodeValue.data.num in tempValue:
                    dupltiNodeTag.append(nodeValue.tag)
                    # print("dupltiNodeTag",dupltiNodeTag)
                    if hasMultiLeafs(tree,nodeValue.tag):
                        adjustNode = getChildrenNodeTag(tree,nodeValue.tag)
                        moveNode(tree,adjustNode,firstNodeTag)
                else:
                    tempValue.append(nodeValue.data.num)
    # print(dupltiNodeTag)
    return dupltiNodeTag

def hasMultiLeafs(tree,nodeTag):
    count = len(tree.children(nodeTag))
    if count>=1:
        return True
    else:
        return False

def main():
    createTree()
    # tree.show()
    print("---------------refactor below-------------------")

    removeNode = []
    treeStruct =tree.to_json()
    for floor in range(tree.depth()):
        if floor == 0:
            nodeTag = "root"
        else:
            nodeTag = "child"+str(floor)
            levelCount = treeStruct.count(nodeTag)
            horizontalTempNodeTag = "child"
            for horizontal in range(levelCount):
                horizontalTempNodeTag = horizontalTempNodeTag+str(floor)
                if tree.contains(horizontalTempNodeTag) and hasMultiLeafs(tree,horizontalTempNodeTag):
                    removeNode.extend(compareSameFloorAndRemoveDupliNode(tree,horizontalTempNodeTag))
                else:
                    continue

        removeNode.extend(compareSameFloorAndRemoveDupliNode(tree,nodeTag))

    for i in set(removeNode):
        # print(set(removeNode))
        tree.remove_node(i)
    return tree.show(data_property="num")


if __name__=="__main__":
    main()

效果如下:

WeChat Screenshot_20190513175613.png

PS:代码还有优化空间,嵌套比较多,性能方面考虑不是很多。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 关于数据结构中树结构的相关分享 本文参考: 树结构参考文献 一、传统的数据结构中的树结构 树结构是一种非线性存储结...
    ai_chen2050阅读 9,259评论 0 3
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 11,311评论 0 13
  • http://python.jobbole.com/85231/ 关于专业技能写完项目接着写写一名3年工作经验的J...
    燕京博士阅读 12,221评论 1 118
  • Python语言特性 1 Python的函数参数传递 看两个如下例子,分析运行结果: 代码一: a = 1 def...
    伊森H阅读 8,189评论 0 15
  • 日更3|我为健康向艾求 20181117 星期六 晴 心情指数 2.2 早间醒来,闭着眼,感受着昨晚睡觉之前左眼的...
    Anyanna阅读 3,150评论 0 1