关于几种递归函数的使用总结

import copy

class Dept:

    def __init__(self, id, name, parentid) -> None:
        self.id = id
        self.name = name
        self.parentid = parentid
        self.children = []

    def __eq__(self, other):
        # if(self.id == other.id and self.name == other.name and self.parentid == other.parentid
        #    and self.children == other.children):
        if(self.__dict__ == other.__dict__):
            return True
        else:
            return False

  以上是一个组织的结构,现有一个list如下,里面存放的是这个组织的信息。求:将此list转换为树状结构

d1 = Dept('120', 'zhangsan', '12')
d2 = Dept('1200', 'lisi', '120')
d3 = Dept('1201', 'wangwu', '120')
d4 = Dept('12010', 'zhangsan', '1201')
l1 = [d4, d1, d3, d2 ]

  以下是两个方法,均使用了递归函数。第一个方法是比较组织树dept和组织d1,如果dept是d1的子节点,则将dept加在d1下,并返回d1;如果d1是dept的子节点,则将d1加在dept下,返回dept;如果dept和d1关联不上,则返回dept。

  第二个方法是dept是与list1里结构一样的组织对象,将list1中的组织对象组合到dept中成为一个树状组织。

  第三,还新建了一个判断两个组织是否相等的方法,是重写了Dept的__eq__方法。

def traverse_dept(dept, d1):
    tmp = copy.deepcopy(d1)
    if(dept.id == d1.parentid):
        dept.children.append(d1)
    elif(d1.id == dept.parentid):
        d1.children.append(dept)
    else:
        for dept_tmp in dept.children:
            traverse_dept(dept_tmp, d1)
    return d1 if(tmp != d1) else dept

def convert_list_to_tree(dept, list1):
    if(len(list1)==0):
        return dept
    for i in list1:
        tmp = copy.deepcopy(dept)
        dept = traverse_dept(dept, i)
        if(tmp != dept):
            list1.remove(i)
            dept = convert_list_to_tree(dept, list1)
            return dept
    # return dept

depts = convert_list_to_tree(l1[0], l1[1:]) 
for i in depts.children: 
  print(i.__dict__)  

  经过解决这个问题,对于递归函数有更多认识,现总结如下:

1、第一种常见的递归,就是函数处理一直是同一个对象,这种函数不需要有返回值,调用完函数之后,对象本身已经经过了处理。比如,下面,将一个list中的重复元素删除。

def del_repeat_element(list1):
    for i in list1:
        if(list1.count(i)):
            list1.remove(i)
            del_repeat_element(list1)
            break

  这种函数注意,可以在调用自身函数之后要加一个break语句,中断后面的遍历(有时候不写break语句,可能逻辑会出问题,因为一旦调用了自身,当前层函数的处理就完成了,如果不中断for循环,将会做多余的处理,结果就错了。在这个函数中加不加break语句结果相同,只是因此删除重复元素是一个幂等操作,即便多处理即便也不会出错)。  

2、第二种是返回的结果是一个列表或者是一系列类似的值,比如求一个正整数的所有质因数。这种使用生成器比较方便,就是每产生一个数值就yield返回出去,剩下的继续往下执行。

def resolve_prime(pri_num):
    for i in range(2,pri_num+1):
        if(pri_num%i == 0):
            yield i
            pri_num = int(pri_num/i)
            for j in resolve_prime(pri_num):
                yield j
            break    

  生成器类型的递归函数,再调用自己时,注意要用for来遍历(因为它本身是一个可迭代对象)。这种一旦调用了自己,当前层的循环就必须break掉。不然等所有的递归函数执行完之后,返回到最外层的函数时,还是会将未循环完的for继续执行下去。结果就是错的了。

3、第三种递归是如同开篇中的示例那样,入参为2个或者多个,返回的是其中的一个,这种参数为同一类型的对象,具有相同的结构。由于需要的结果不确定是处理之后的哪一个,因此需要有返回值

def traverse_dept(dept, d1):
    tmp = copy.deepcopy(d1)
    if(dept.id == d1.parentid):
        dept.children.append(d1)
    elif(d1.id == dept.parentid):
        d1.children.append(dept)
    else:
        for dept_tmp in dept.children:
            traverse_dept(dept_tmp, d1)
    return d1 if(tmp != d1) else dept

  dept组织树,d1是单个的组织,此函数是查找两者是否有上下级关系,如果有,则返回合并之后的组织,如果没有则返回dept。合并也有两种情况,dept合并到d1下,或者d1合并到dept下。在判断了dept和d1没有直接的关系之后,再查看d1与dept的子级有没有关系,在后面的循环调用中,d1就只可能是dept某一子级的的子级了,相当于d1只会合并到dept中,因此后面调用自己的过程中,返回的是只可能是dept,不可能是d1,就不需要接受函数的返回值了。最后返回最外层函数处理的结果就行了。

def convert_list_to_tree(dept, list1):
    if(len(list1)==0):
        return dept
    for i in list1:
        tmp = copy.deepcopy(dept)
        dept = traverse_dept(dept, i)
        if(tmp != dept):
            list1.remove(i)
            dept = convert_list_to_tree(dept, list1)
            return dept
    # return dept

  上面这个函数是list1中的对象转换成一个树形结构,其中dept是list1中的一员。注意,此函数虽然返回的都是dept,但是dept经过函数traverse_dept(dept, i)处理之后,可能不是同一个对象了,因此不能在for循环之后返回dept,写在最外层,则最终返回的最外层的dept值,而递归里面处理之后的没有返回出来。所以,要将返回语句放在循环里面,递归之后,这样dept就会一层一层返回出来,最终返回的才是经过层层处理之后的dept。

原文地址:https://www.cnblogs.com/yahutiaotiao/p/12716238.html