前中后序遍历 + 层次遍历+迭代器遍历

  前序遍历:

        result = []
        st = [root]
        while st:
            node = st.pop(-1)
            if node.right:
                st.append(node.right)
            if node.left:
                st.append(node.left)
            result.append(node.val)
        return result

  入门代码不多解释了,这种做法就是列表后面取出一个节点,然后访问根节点(这一步在前面所以叫前序),之后将右侧节点先放入列表内,在将左侧节点放入列表内(因为你每次都是拿后面的,所以后放的先访问)。

  拒绝递归写法,这样写更容易进行扩展。

  之后就可以很方便的补上中序和后序。

  中序做法:

  中序访问是左根右,所以先从左侧一直访问到底,然后将这些节点保存在一个list中,从后面开始逐一拿出来找右侧是否有节点,如果有的话,从这个节点继续左侧访问到底。

  访问到底的做法就是在访问左,从后面拿出来每个节点就是在访问根,查找这些节点是否有右节点是访问右。

            res = []
            stack = []
            while stack or root:
                # 不断往左子树方向走,每走一次就将当前节点保存到栈中
                # 这是模拟递归的调用
                if root:
                    stack.append(root)
                    root = root.left
                else:
                    tmp = stack.pop()
                    res.append(tmp.val)
                    root = tmp.right
            return res        

  后续做法:

  很好理解,后续访问是左右根。增加的时候先加入左再加入右,如123这个树,增加的时候bag中是[2,3],访问的时候pop先出来的是3所以加入的是3的左右孩子。直到把3这棵树都遍历完才会处理2.所以结果是[1,3,3的孩子们先右后左,2,2的孩子们先右后左] 。最后我们取反,那就是[2的孩子们先左后右,2,3的孩子们先左后右,3,1],就是我们想要的左右根的顺序了。

        if not root:
            return []
        bag = [root]
        result = []
        while bag:
            node = bag.pop()
            result.append(node.val)
            if node.left:
                bag.append(node.left)
            if node.right:
                bag.append(node.right)
        return result[::-1]

   层次遍历:

  每一层循环用一个列表保存这些节点,在最后的时候加到一起。

  

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        queue =[]
        cur = [root]
        while cur:
            que_val = []
            next_node = []
            for node in cur:
                if node:
                    next_node.extend([node.left,node.right])
                    que_val.append(node.val)
            cur = next_node
            if que_val:
                queue.append(que_val)
        return queue

  把上一层的节点都拿出来,把它的左右孩子都放在容器中,都放好后,把这个节点指向这个容器的第一个节点。怎么写都可。

class Solution:
    def levelOrder(self, root: TreeNode) -> List[List[int]]:
        if not root:
            return []
        bag = [root]
        res= []
        while bag:
            tmp = []
            val = []
            for node in bag:
                if node.left:
                    tmp.extend([node.left])
                if node.right:
                    tmp.extend([node.right])
                val.append(node.val)
            bag = tmp
            res.append(val)
        return res

  使用迭代器来遍历也是一种非常可以的方法。

  

 

   通过这道题来理解一下迭代器yeild和yeild from在树结构中的应用。首先这个yeild和yeild from我的理解是这样的:

  对于yeild来说,它可以使当前函数变成一个迭代器,每次执行到yeild a代表停止,并等待时机返回a值。所以如果你使用一个迭代器.send(None)也就是老版本的next,就会获得一次的值。所以在你for循环中不断用 i 获取这个迭代器的值的时候,每一轮都是执行到yeild a返回了一个a然后等待下一轮,这样来的。

  对于yeild from来说是这样的,如果一个函数里套一个函数,里面都有yeild那么来来回回传递非常复杂,有时候你外层函数并不需要这样传递,你只需要和你内层保持一致就好了,内层yeild什么你就yeild什么所以使用了yeild from。

  看这段代码:

class Solution:
    def findMode(self, root: TreeNode) -> List[int]:
        #迭代器版本
        def inorder(node):
            if node:
                yield from inorder(node.left)
                yield node.val      #每次刀yeild的时候就返回一个迭代值
                yield from inorder(node.right)
      for v in inorder(root):

  这样一段代码已经很明显了,通过yield完成一个中序遍历的递归,上下的yield from代表我返回的东西在inorder里面找到yield为止,这样就完成了一个中序遍历了。后面通过一个for就可以逐个拿到每个节点,这样非常的方便。

  那么对于这道题来说,拿到每个节点后还有一些操作顺便讲完,因为题中要求了空间的大小,所以这样做:设置  ans = []  cnt,max_cnt,last = 0,0,None

        for v in inorder(root):
            if v == last:  #如果v和上面的数字相同
                cnt += 1    #重复元素计数+1
            else:
                cnt = 1      #不同的话重新技术
            if cnt > max_cnt:  #如果cnt计数比最大值要大
                ans = [v]      #最多的元素v放在ans里 
                max_cnt = cnt   #cnt更新一下最大值
            elif cnt == max_cnt:  #如果cnt和最大的值相等
                ans.append(v)     #说明这个元素也是最多的元素之一 加入到ans中
            last = v
        return ans

  cnt代表当前值得节点已经连续出现多少个了,第一个if else代表如果出现重复了就+1个,如果没重复那肯定是新节点了重新计数变成1个。

  max_cnt代表最大的个数是多少,因为求的是众数,所以统计最大值。第二个if else则是如果当前的比最大值要大了,那更新一下最大值变成cnt,在把当前这个节点值放入ans中,它可是第一个众数节点的值。如果cnt的数量和max cnt相等代表了这个新的节点的值出现 次数和最大的一样多,同样是众数,那么也需要加入到ans中。

  每次循环过后,last节点要标记这个数字,用来和下一个数字比较是否相同。

  

posted @ 2021-06-28 10:13  灰人  阅读(158)  评论(0)    收藏  举报