树与二叉树

树分为有序树和无序树。
无序树是树中任意节点的子节点没有顺序关系,也成为自由树。基本无应用价值。
有序树是树中任意结点之间有顺序关系,成为有序树。分为二叉树 、霍夫曼树、B树。
二叉树:每个节点最多含有两个子树的树成为二叉树。二叉树是有序树。
完全二叉树,对于一颗二叉树,假设其深度为d(d>1),
除了第d层外,其余各层节点数目均已达到最大值。且第d层所有结点从左到右连续的紧密排雷,这样的二叉树称为完全数。
满二叉树,所有叶结点都在最底层的二叉树。
平衡二叉树(AVL树):当且仅当任何结点的两颗子树的高度不大于1的二叉树。
排序二叉树(BST):又称二叉查找树,有序二叉树,二叉搜索树
B树:一种对读写操作进行优化的二叉查找树,能够保持数据有序,拥有多余两个子树。
输的存储有顺序存储和链式存储。
顺序存储在遍历速度有优势。但是不能应对残缺树,因此一般采用链式存储。

二叉树的性质:
1.在二叉树上第i层最多有2^(i-1)个结点
2.深度为k的二叉树上最多有2^k-1个结点
3.对于任意1颗二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0 = N2 +1
4.具有n个结点的完全二叉树的深度必维log(n+1)
5. 对于完全二叉树,若从上至下,从左至右编号,则编号为i的结点,其左孩子的编号为2i,右孩子结点编号为2i+1,其双亲的编号必为i/2

树的遍历:
分为广度遍历和深度遍历
深度遍历分为先序遍历,中序遍历和后续遍历。
二叉树的广度遍历代码如下,主要利用队列作为数据存储,先进先出的原则:

def breadth_travel(self):#s广度遍历
            
        if self.root is None:   
            return
       
        queue =[self.root]
        while queue:
            cur_node = queue.pop(0)
            print(cur_node.elem, end = " ")
            if cur_node.lchild is not None:
                queue.append(cur_node.lchild)
            
            if cur_node.rchild is not None:
                queue.append(cur_node.rchild)

二叉树先序遍历,访问顺序为根-左-右,代码如下:

 def non_recu_preorder(self):
        #采用非递归方法前序遍历。其主要思想是从根节点开始不断寻找左节点并打印,同时将打印节点入栈,
        #直到左节点为空。再弹出栈内元素并判断其是否有右结点,若有,继续第一层循环。
        if self.root == None:
        #如果树是空的,直接返回空值。
            return
        
        stack = []
        # 用列表模拟栈

        cur_node = self.root
        
        while stack  or cur_node is not None:
            #注意此处不能采用stack == None 作为终止条件。外层循环控制,当栈里没有元素而且当前节点是空时,循环终止。
            #只要栈内有元素或者当前节点不为None,循环继续。
            
            while cur_node is not None:
                #当前节点不是空值,直接打印节点。并继续寻找左子节点。直至左子节点为空。
                print(cur_node.elem, end = " ")           
                stack.insert(0, cur_node)
                cur_node = cur_node.lchild
            while stack  and cur_node is None:
                #当前节点位于左子节点为空处,栈内弹出上一个节点并判断是否有右结点,若有,跳出本层循环并进入上一层循环,进入左子节点查找环节。
                cur_node = stack.pop(0).rchild

二叉树中序遍历,遍历顺序为左-根-右,其实现方法与前序相似,不同的是其访问语句位置放在弹栈之后,其代码实现如下:

def non_recu_midorder(self):
        #采用非递归方法前序遍历。其主要思想是从根节点开始不断寻找左节点并打印,同时将打印节点入栈,
        #直到左节点为空。再弹出栈内元素并判断其是否有右结点,若有,继续第一层循环。
        if self.root == None:
        #如果树是空的,直接返回空值。
            return
        stack = []
        # 用列表模拟栈
        cur_node = self.root
        while stack  or cur_node is not None:
            while cur_node is not None:
                #当前节点不是空值,继续寻找左子节点。直至左子节点为空。
                stack.insert(0, cur_node)
                cur_node = cur_node.lchild
            while stack  and cur_node is None:
                #当前节点位于左子节点为空处,栈内弹出上一个节点并判断是否有右结点,若有,跳出循环并再次进入左子节点查找。
                cur_node = stack.pop(0)
                #同前序遍历相比,移动了节点访问的位置,每次打印出栈的元素,实际上每次出栈的元素就是父节点。
                print(cur_node.elem, end = " ") 
                cur_node = cur_node.rchild

二叉树后序遍历方法1,遍历顺序为左-右-根,打印顺序与访问顺序有差别,因此其控制流程比较复杂,一个投机做法是,就是如果知道前序遍历,其次序为根-左-右,很容易写出根-右-左次序的遍历方式,最后对数据倒序输出,遍历顺序就是左-右-根,这种方法只是权宜之计,并没有触及后序遍历的精髓,其代码如下:

    def non_recu_postorder1(self):
       
        if self.root == None:
        #如果树是空的,直接返回空值。
            return
        
        stack = []
        res = []
        # 用列表模拟栈

        cur_node = self.root
        
        while stack  or cur_node is not None:
            #注意此处不能采用stack == None 作为终止条件。外层循环控制,当栈里没有元素而且当前节点是空时,循环终止。
            #只要栈内有元素或者当前节点不为None,循环继续。
            
            while cur_node is not None:
                #当前节点不是空值,继续寻找左子节点。直至左子节点为空。
                
                res.append(cur_node.elem)        
                stack.insert(0, cur_node)
                cur_node = cur_node.rchild
                
            while stack  and cur_node is None:
                #当前节点位于左子节点为空处,栈内弹出上一个节点并判断是否有右结点,若有,跳出循环并再次进入左子节点查找。
                cur_node = stack.pop(0).lchild
        #倒序打印即可    
        while res:
            print(res.pop(), end = " ")

二叉树后序遍历方法2,采用单栈存储,加以流程控制,首先外层循环条件是栈内元素不为空,或者当前指针不为空,接下来如果指针元素不为空,则通过内层循环找到左子节点的终点。接着判断此时栈元素不为空,弹出栈内元素,如果该元素的右子节点不存在,或者右子节点存在,但上一次访问的节点恰好是右子节点,说明该出栈元素可以打印。否则,指针指向该元素的右子节点,同时将该元素压回栈内。等下次弹出打印。其代码如下:

def non_recu_postorder2(self):
        
        if self.root == None:
        #如果树是空的,直接返回空值。
            return
        stack = []
        # 用列表模拟栈
        #import pdb
        #pdb.set_trace()
        cur_node = self.root
        pre_node = None
        while stack  or cur_node is not None:
            #注意此处不能采用stack == None 作为终止条件。外层循环控制,当栈里没有元素而且当前节点是空时,循环终止。
            #只要栈内有元素或者当前节点不为None,循环继续。
            
            while cur_node is not None:
                #当前节点不是空值,直接打印节点。并继续寻找左子节点。直至左子节点为空。
              
                stack.insert(0, cur_node)
                cur_node = cur_node.lchild
                
            if stack:#保证栈内有元素,若无,说明已经遍历完毕
                cur_node = stack.pop(0)
                if cur_node.rchild == None or pre_node == cur_node.rchild:
                #如果该元素的右子节点不存在,或者右子节点存在,但上一次访问的节点恰好是右子节点,说明该出栈元素可以打印。
                    print(cur_node.elem, end = " ")
                    pre_node = cur_node
                    cur_node = None  #此处十分重要,否则无法跳出内层的while循环,一直执行cur_node = cur_node.lchild。置空后直接
                    #接收下一个栈的弹出值,从而进入下一次循环。
                else:
                #该元素右子节点存在,但上一次访问的节点恰好不是右子节点,更新指针,并将弹出的元素再压回栈内,等待下次打印。
                    stack.insert(0, cur_node)
                    cur_node = cur_node.rchild
       

二叉树后序遍历方法3,采用单栈存储,但是将栈内元素节点贴上标签,记录出栈次数,当第一次出栈时不打印并压回栈内,第二次出栈时打印,其代码如下:

def non_recu_postorder3(self):
       #构造一个栈列表,栈元素为元组,元组包括节点地址和访问标记,首次入栈的元组,其访问标记均为False。逐个弹出栈元素,当访问标记为Flase,
    #不打印并将访问标记修改为True,再压回栈内,以便下次访问时打印。
        #if self.root == None:
        #如果树是空的,直接返回空值。
           # return
        stack = []
        stack.append((self.root, False))
        
        while stack:
            cur, visited = stack.pop()
            if visited:
                print(cur.elem, end = " ")
            else:
                stack.append((cur,True))
                if cur.rchild:
                    stack.append((cur.rchild, False))
                if cur.lchild:
                    stack.append((cur.lchild, False))
        return
        
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值