数据结构与算法-树(二):剑指offer-树的遍历

树的遍历

一、树的结构

在这里插入图片描述

  • 实现树的结构
'''
值:val
两个指针:left,right
二叉树中叶子结点的左右节点都为None
'''
'''
值:val
两个指针:left,right
二叉树中叶子结点的左右节点都为None
'''
class TreeNode(object):  # x为值
    def __init__(self,x):
        self.val = x
        self.left = None
        self.right = None
    
    
    
# 取出数据在大类上有两种方式:深度优先与广度优先   
# 深度优先:先找到最深,纵着来
# 广度优先:从第一行开始找第二行,第三行,横着来

if __name__ == '__main__':
    t1 = TreeNode(1)
    t2 = TreeNode(2)
    t3 = TreeNode(3)
    t4 = TreeNode(4)
    t5 = TreeNode(5)
    t6 = TreeNode(6)
    t7 = TreeNode(7)
    t8 = TreeNode(8)
    
    t1.left = t2
    t1.right = t3
    t2.left = t4
    t2.right = t5
    t3.left = t6
    t3.right = t7
    t6.right = t8
    

二、深度优先与广度优先遍历

实现完数据结构后,我们需要查找,查找的方式有两大类:深度优先广度优先
深度优先又分为前序遍历中序遍历后序遍历
先序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后续遍历:左子树->右子树->根节点
例如:
在这里插入图片描述
先序遍历:ABDHIECFGJK
中序遍历:HIDBEAFCJKG
后序遍历:HIDEBFKJGCA

1、先序中序后序遍历的递归实现

  • 先序遍历
# 先序遍历
def preOrderRecursive(root):
    if root == None:
        return None
    print(root.val)
    # 递归
    preOrderRecursive(root.left)
    preOrderRecursive(root.right)
  • 中序遍历
# 中序遍历
def midOrderRecursive(root):
    if root == None:
        return None
    midOrderRecursive(root.left)
    print(root.val)
    midOrderRecursive(root.right)
  • 后序遍历
# 后序遍历
def latOrderRecursive(root):
    if root == None:
        return None
    latOrderRecursive(root.left)
    latOrderRecursive(root.right)
    print(root.val)

看一下效果:

# 先序遍历
def preOrderRecursive(root):
    if root == None:
        return None
    print(root.val)
    # 递归
    preOrderRecursive(root.left)
    preOrderRecursive(root.right)

# 中序遍历
def midOrderRecursive(root):
    if root == None:
        return None
    midOrderRecursive(root.left)
    print(root.val)
    midOrderRecursive(root.right)

# 后序遍历
def latOrderRecursive(root):
    if root == None:
        return None
    latOrderRecursive(root.left)
    latOrderRecursive(root.right)
    print(root.val)

'''
值:val
两个指针:left,right
二叉树中叶子结点的左右节点都为None
'''
class TreeNode(object):  # x为值
    def __init__(self,x):
        self.val = x
        self.left = None
        self.right = None
    
    
    
# 取出数据在大类上有两种方式:深度优先与广度优先   
# 深度优先:先找到最深,纵着来
# 广度优先:从第一行开始找第二行,第三行,横着来

if __name__ == '__main__':
    t1 = TreeNode(1)
    t2 = TreeNode(2)
    t3 = TreeNode(3)
    t4 = TreeNode(4)
    t5 = TreeNode(5)
    t6 = TreeNode(6)
    t7 = TreeNode(7)
    t8 = TreeNode(8)
    
    t1.left = t2
    t1.right = t3
    t2.left = t4
    t2.right = t5
    t3.left = t6
    t3.right = t7
    t6.right = t8
    
    print('先序遍历')
    preOrderRecursive(t1)
    print('中序遍历')
    midOrderRecursive(t1)
    print('后序遍历')
    latOrderRecursive(t1)
先序遍历 12453687
中序遍历 42516837
后序遍历 45286731

2、非递归方式实现遍历

其实递归和循环(利用栈进行循环)是互相转换的,递归是一定要事先找到一个出口的,f否则一直递归下去什么时候是个头,比如我们的程序if root == None: return None所以用递归第一件事就是要找到一个出口看到这里一定要将上面递归的过程从脑海里过一遍!
在这里插入图片描述
递归与栈其实是可以互相转换。回想一下上面的递归过程,以前序为例:preOrderRecursive(root),然后root不为空,print(root.val)继续向下走:

  • 此时碰上了自己递归自己,preOrderRecursive(root.left=2),此时重复的又进入了函数内部,判断是否为空,不为空,打印2,继续往下走;
    – 此时又碰上了自己递归自己,preOrderRecursive(root.left=4),此时又重复的进入了函数内部,判断是否为空,不为空,打印4,继续往下走;
    — 此时又碰上了自己d递归自己,preOrderRecursive(root.left=none),此时又重复的进入了函数内部,为空,返回none,这个preOrderRecursive(root.left=none)结束;
    此时并代表整个函数结束,而是接着preOrderRecursive(root.left=4),这个函数第一个递归已经结束,进入第二个递归,preOrderRecursive(root.right=none),返回none;preOrderRecursive(root.left=4)结束,接着继续preOrderRecursive(root.left=2),进入preOrderRecursive(root.right=5),非空,打印5,进入5的左节点,空,返回,进入5的右节点,空返回,preOrderRecursive(root.left=2)结束;进入根节点的右边子树…

这个过程很像是一个栈结构,首先给一个栈stack=[],stack.append(1) stack = [1];stack,append(2), stack = [1,2];stack,append(4), stack = [1,2,4];然后碰到none了,开始蹦出,stack,pop(4), stack = [1,2];stack,append(5), stack = [1,2,5];碰到None,开始蹦出,stack,pop(5), stack = [1,2];stack,pop(2), stack = [1];stack,append(3), stack = [1,3]…

注意以上栈中的数字并不是节点的val,而是递归的函数中的参数,记录的是递归的函数,pop就是这个函数结束了,append就是开始进入这个函数递归。很明显1在栈中的时间最长,什么时候1pop了,证明这个函数已经完全结束!

  • 先序遍历
def preOrder(root):
    if root == None:
        return None
    
    # 列表表示栈
    stack = []
    tmpNode = root  # tmNode可以理解成指针,也可以理解成当前循环到那个节点了
    '''
    循环结束的条件并不仅仅是tmpNode为空,比如当你一直tmpNode = left的时候迟早为空
    但是节点还没有遍历完呢!这个时候应该怎么做呢?想象一下递归,当递归到叶子结点时,
    这个左函数结束啦,是不是该右函数了,因此此时该pop了。所以循环到栈为空为止
    '''
    while tmpNode or stack:  
        # 先找所有的左子树加入栈,循环所有的子节点,此循环主要是更新所有的左子节点
        while tmpNode:
            # 先序打印跟
            print(tmpNode.val)
            stack.append(tmpNode)
            tmpNode = tmpNode.left # 结束前中国left为空,并没有进入栈
            
        # 左子树完了,碰到了None,开始pop
        node = stack.pop()
        # 更新节点
        tmpNode = node.right           
  • 中序遍历
    中序遍历与前序遍历的循环结构一样,只不过打印的位置变为pop那了
def midOrder(root):
    if root == None:
        return None
    
    # 列表表示栈
    stack = []
    tmpNode = root  # tmNode可以理解成指针,也可以理解成当前循环到那个节点了
    '''
    循环结束的条件并不仅仅是tmpNode为空,比如当你一直tmpNode = left的时候迟早为空
    但是节点还没有遍历完呢!这个时候应该怎么做呢?想象一下递归,当递归到叶子结点时,
    这个左函数结束啦,是不是该右函数了,因此此时该pop了。所以循环到栈为空为止
    '''
    while tmpNode or stack:  
        # 先找所有的左子树加入栈,循环所有的子节点,此循环主要是更新所有的左子节点
        while tmpNode:
            # 先序打印跟
#             print(tmpNode.val)
            stack.append(tmpNode)
            tmpNode = tmpNode.left # 结束前中国left为空,并没有进入栈
            
        # 左子树完了,碰到了None,开始pop
        node = stack.pop()
        # 后序打印
        print(node.val)
        # 更新节点
        tmpNode = node.right
            
def latOrder(root):
    if root == None:
        return None
    
    # 列表表示栈
    stack = []
    tmpNode = root  # tmNode可以理解成指针,也可以理解成当前循环到那个节点了
    while tmpNode or stack:  
        # 先找所有的左子树加入栈,循环所有的子节点,此循环主要是更新所有的左子节点
        while tmpNode:
            # 先序打印跟
#             print(tmpNode.val)
            stack.append(tmpNode)
            tmpNode = tmpNode.left # 结束前中国left为空,并没有进入栈
            
#         node = stack.pop()  因为先打印右子树后打印根节点,所以先不能pop
        node = stack[-1]
        # 后序打印
        # 更新节点
        tmpNode = node.right
        # 因为是先打印左节点后再打印右节点,现在左节点为空,那来判断右节点是不是为空
        if node.right == None:
            print(node.val)
            node = stack.pop()
            while stack and node == stack[-1].right:
                node = stack.pop()
                print(node.val)
            
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值