Leetcode 二叉树相关问题总结

1. 二叉树的递归写法和非递归写法

递归写法比较简单,非递归写法三种遍历不太一样比较难记,但是可以采用染色法来统一较好记。非递归前序和后序比较简单,考虑一下顺序即可,后序要逆转。中序先压栈左子树回到根部再右子树。

前序(leetcode 144)根左右

递归:

中序和后序类似写法,调整一下顺序即可。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right) if root else []

非递归:

使用stack来实现。从top到bottom,从左到右。先将根节点压如栈内,弹出当前顶端的节点打印值,之后将当前节点的右孩子压入栈,左孩子压入栈,重复以上过程直到栈为空。

算法复杂度

时间复杂度:访问每个节点恰好一次,时间复杂度为 O(N),其中 N 是节点的个数,也就是树的大小。
空间复杂度:取决于树的结构,最坏情况存储整棵树,因此空间复杂度是 O(N)。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root: return []
        ans = []
        stack = []
        stack.append(root)
        while len(stack)!=0:
            top = stack.pop()
            if top.right:
                stack.append(top.right)
            if top.left:
                stack.append(top.left)
            ans.append(top.val)
        return ans

染色法:

https://leetcode-cn.com/problems/binary-tree-inorder-traversal/solution/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/

中序和后序类似,调整一下顺序即可。主要是分为已经访问的和还没有访问的节点。每个节点用一个元祖来记录,含其访问状态。先加入(根节点,未访问)到栈中。当栈不为空的时候,先弹出栈顶的节点,如果栈顶节点为空(叶子节点的左孩子或者右孩子),则继续循环。如果当前节点lu的访问状态为未访问,则按照要求的顺序倒序压入节点和访问状态;否则打印当前节点的值。

class Solution(object):
    def preorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root: return []
        ans = []
        stack = []
        unvisited, visited = 0, 1
        stack.append((root, unvisited))
        while stack:
            (top, flag) = stack.pop()
            if not top: continue  # 处理叶子节点的左孩子或者右孩子
            if flag == unvisited:
                stack.append((top.right, unvisited))
                stack.append((top.left, unvisited))
                stack.append((top, visited))
            else:
                ans.append(top.val)
        return ans

中序  94

递归略,参照前序。

染色法略,参照前序。

非递归:

一直压入当前节点,找到树的最左边的孩子,直到当前节点为空。回退到上一层节点即弹出栈顶元素并且打印,并更新当前节点为其右孩子。符合中序根左右的顺序。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root: return []
        stack = []
        ans = []
        while stack or root:
            while root:
                stack.append(root)
                root = root.left
            root = stack.pop()
            ans.append(root.val)
            root = root.right
        return ans

 

后序 145

非递归:

从根节点开始依次迭代,弹出栈顶元素输出到输出列表中,然后依次压入它的所有孩子节点,按照从上到下、从左至右的顺序依次压入栈中。

因为深度优先搜索后序遍历的顺序是从下到上、从左至右,所以需要将输出列表逆序输出。栈根左右,输出根右左,逆序左右根。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def postorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        if not root: return []
        ans = []
        stack = []
        stack.append(root)
        while(stack):
            top = stack.pop()
            if top.left:
                stack.append(top.left)
            if top.right:
                stack.append(top.right)
            ans.append(top.val)
        return ans[::-1]

层序遍历  102

采用队列的结构。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if not root: return []
        ans = []
        q = [(root,0)]
        while q:
            curr, level = q[0]
            if len(ans) == level:
                ans.append([])
            ans[level].append(curr.val)
            q = q[1:] # dequeue
            if curr.left:
                q.append((curr.left, level+1))
            if curr.right:
                q.append((curr.right, level+1))
        return ans

拓展:

104 二叉树的最大深度。

递归的思想,先考虑空的情况和根节点是叶子节点的情况。然后递归求左子树和右子树的最大深度,即可求得最大深度。

class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if not root: return 0
        if not root.left and not root.right: return 1
        return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1

101 判断二叉树是不是对称的。

递归:思考对称的时候的条件!p, q两个节点是对称的,则其值相等,p左子树与q右子树是对称的。

class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        return self.isMirror(root, root)
    
    def isMirror(self, p, q):
        if not p and not q:
            return True
        if not p or not q:
            return False
        return (p.val==q.val) and self.isMirror(p.left, q.right) and self.isMirror(p.right, q.left)

假设树上一共 n 个节点。

时间复杂度:这里遍历了这棵树,渐进时间复杂度为O(n)。
空间复杂度:这里的空间复杂度和递归使用的栈空间有关,这里递归层数不超过 n,故渐进空间复杂度为 O(n)

迭代:一般递归改迭代会用到栈或者队列。这里用队列。队列压入u,v两个需要比较的节点,每次弹出两个节点并进行比较,如果值不等则不对称,否则继续压入u的左子树和v的右子树,u的右子树和v的左子树。直至队列为空。

class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        if not root: return True
        if not root.left and not root.right: return True
        q = [root.left, root.right]
        while q:
            u = q[0]
            v = q[1]
            q = q[2:]
            if not u and not v: continue
            if not u or not v or u.val!=v.val: return False
            q.append(u.left)
            q.append(v.right)
            q.append(u.right)
            q.append(v.left)
        return True

时间复杂度:O(n),同「方法一」。
空间复杂度:这里需要用一个队列来维护节点,每个节点最多进队一次,出队一次,队列中最多不会超过 n 个点,故渐进空间复杂度为 O(n)。

112  路径总和

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

递归:基本情况,如果叶子节点直接判断。否则递归判断左子树和右子树是否等于sum-root.val。

class Solution(object):
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        # level traverse and record the sum!
        if not root: return False
        q = [(root, root.val)]
        while q:
            curr, curr_sum = q[0]
            q = q[1:]
            if not curr.left and not curr.right: # leaf node
                if curr_sum == sum: return True
                continue
            if curr.left: # update left tree node
                q.append((curr.left, curr_sum+curr.left.val))
            if curr.right: # update right tree node
                q.append((curr.right, curr_sum+curr.right.val))
        
        return False

迭代:使用队列。层次遍历一遍树,并记录每个节点的路径和!

class Solution(object):
    def hasPathSum(self, root, sum):
        """
        :type root: TreeNode
        :type sum: int
        :rtype: bool
        """
        # level traverse and record the sum!
        if not root: return False
        q = [(root, root.val)]
        while q:
            curr, curr_sum = q[0]
            q = q[1:]
            if not curr.left and not curr.right: # leaf node
                if curr_sum == sum: return True
                continue
            if curr.left: # update left tree node
                q.append((curr.left, curr_sum+curr.left.val))
            if curr.right: # update right tree node
                q.append((curr.right, curr_sum+curr.right.val))
        
        return False

 

N叉树的前序、中序和后序遍历。

105 从二叉树的前序和中序构造树:和106类似。

106 从二叉树的后序和中序构造树:确定根,递归调用

class Solution(object):
    def buildTree(self, inorder, postorder):
        """
        :type inorder: List[int]
        :type postorder: List[int]
        :rtype: TreeNode
        """
        if inorder == [] and postorder == []: return None
        root_index = inorder.index(postorder[-1])
        l_in = inorder[:root_index]
        l_post = postorder[:root_index]
        r_in = inorder[root_index+1:]
        r_post = postorder[root_index:-1]

        root = TreeNode(postorder[-1])
        root.left = self.buildTree(l_in, l_post)
        root.right = self.buildTree(r_in, r_post)
        return root

116 填充每个节点的下一个右侧节点指针

题解:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/solution/tian-chong-mei-ge-jie-dian-de-xia-yi-ge-you-ce-j-3/

思路一:层序遍历的另一种写法!

不采用(node,level)或者Null记录层次的方法,采用队列每层获取当成的size。时间和空间复杂度都是O(N)。

"""
# Definition for a Node.
class Node(object):
    def __init__(self, val=0, left=None, right=None, next=None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""

class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root: return root
        # level traverse
        q = collections.deque()
        q.append(root)
        while q:
            sz = len(q)
            for i in range(sz):
                curr = q.popleft()
                if i < sz-1:
                    curr.next = q[0]
                else:
                    curr.next = None
                if curr.left:
                    q.append(curr.left)
                if curr.right:
                    q.append(curr.right)
        return root

思路二:分析指针之间的关系。分情况讨论:1.同一个子树的孩子。2.不同子树之间的孩子。只需要找到最左边的节点,知道父节点的next指针,可以以此更新其子节点的next指针。时间复杂度O(N),空间复杂度O(1)。

class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root: return root
        leftmost = root
        while leftmost.left: 
            head = leftmost  # node at layer N
            while head:
                if head.left: # connection 1 at layer N+1
                    head.left.next = head.right 
                if head.next: # connection 2 at layer N+1,这里不是.right是因为最右边的节点和完美二叉树肯定会有右孩子。
                    head.right.next = head.next.left
                head = head.next # move the node at layer N
            leftmost = leftmost.left # move to layer N+1
        return root

117  不是完全二叉树的情况。普通情况。

层序遍历一样的写法。

迭代要分情况考虑。我自己写的效率比较低。

class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root: return root
        leftmost = root
        while leftmost:
            head = leftmost
            while head:
                # 4 conditions
                if head.left and head.right:
                    head.left.next = head.right
                    head.right.next = self.getNextLeftChild(head)
                    head = head.next
                    continue
                if head.left: # no right child
                    head.left.next = self.getNextLeftChild(head)
                    head = head.next
                    continue
                if head.right: # no left child
                    head.right.next = self.getNextLeftChild(head)
                    head = head.next
                    continue
                # no child
                head = head.next
            leftmost = self.getLeftMost(leftmost)
        return root
    
    def getLeftMost(self, curr):
        if curr.left: return curr.left
        if curr.right: return curr.right
        leftmost = self.getNextLeftChild(curr) # 要return...
        return leftmost

    def getNextLeftChild(self, curr):
        while curr.next:
            if curr.next.left:
                return curr.next.left
            if curr.next.right:
                return curr.next.right
            curr = curr.next
        return None
class Solution(object):
    def connect(self, root):
        """
        :type root: Node
        :rtype: Node
        """
        if not root: return root
        leftmost = root
        while leftmost:
            dummy = Node(0)  # 增加虚节点来记录下一层
            prev = dummy # 下一层的指针
            head = leftmost # 记录当前层的节点的指针 
            while head:
                if head.left:
                    prev.next = head.left
                    prev = prev.next
                if head.right:
                    prev.next = head.right
                    prev = prev.next
                head = head.next
            leftmost = dummy.next
            # while dummy:  # dummy就是下一层的整个链表
            #     print('ww')
            #     print(dummy.val)
            #     dummy = dummy.next
            # while prev: # prev指针现在指向下一层最后一个节点
            #     print('yy')
            #     print(prev.val)
            #     prev = prev.next
        return root

236 二叉树的最近公共祖先

题解

题解2: lowestCommonAncestor这个函数不要理解为找公共祖先,而就理解为帮两个节点找祖先 传入的值是root, p, q,帮p和q找到一个祖先就行,找到两个就更好了,如果找不到就返回NULL 在root->left里面找一次,root->right里面再找一次,如果某一边返回值是NULL, 那么说明两个值都在另一边 由于找的时候,一定是找的最近的祖先返回,所以这里直接返回前面的返回值就行了,可以保证是最近的公共祖先 如果左右的返回值都不是NULL,那说明p和q分别在两边,则当前节点就是最近公共祖先 左右都找不到就直接返回NULL

递归法:分析基础状况,分析左子树和右子树。

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if not root: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if left and right: return root # 分割左右两个子树里面
        if root == p or root == q: return root # 同在一侧的子树里面且其中一个结点就是root
        if left: return left # 同在一侧的子树里面
        if right: return right
        return None

存储父结点(其实还是递归,但是换一种思路。)

先遍历获取父结点,从p开始记录哪些结点访问过,从q开始访问如果遇到访问过的即为共同祖先。

class Solution(object):
    def __init__(self):
        self.fa = dict()
        self.visited = dict()

    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        self.fa[root.val] = None
        self.dfs(root) # traverse and save father nodes
        while p:
            self.visited[p.val] = True
            p = self.fa[p.val] # move upward
        while q:
            if self.visited.has_key(q.val): return q
            q = self.fa[q.val]
        return None

    def dfs(self, root):
        if root.left:
            self.fa[root.left.val] = root
            self.dfs(root.left)

        if root.right:
            self.fa[root.right.val] = root
            self.dfs(root.right)

tbc.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值