知识储备--基础算法篇-二叉树

1.二叉树理论

1.1二叉树的种类

  1. 满二叉树:所有节点的数目等于2**k-1
  2. 完全二叉树:除了底层之外其他层都是的,最底层的节点是从左到右连续的。
  3. 二叉搜索树:左边子树的所有节点都小于中间节点,右边子树的所有节点都大于中间节点。并且左子树也是满足这个规律的。搜索数的时间复杂度为logN。
  4. 平衡二叉搜索树:左子树和右子树的深度差不能超过1。且左子树也符合这个规则。

1.2存储方式

  1. 链式存储:每个节点包括val和左右指针指向左右节点。
  2. 顺序存储:用一个数组来存储,数组中的值是二叉树的节点值,索引是从上到下,从左到右的顺序。比如:给你一个索引i,下标为i的节点的左子节点下标是2*i+1,右子节点下标是2*i+2

1.3遍历方式

  1. 深度优先搜索:深度优先搜索(Depth-First Search,DFS)是一种用于遍历或搜索树和图数据结构的算法。它从起始节点开始,沿着一条路径尽可能深入地探索,直到无法继续为止,然后回溯并尝试其他路径。DFS通常使用递归或栈数据结构来实现。前中后序遍历都是深度优先搜索。一般是递归法迭代法
  2. 广度优先搜索:广度优先搜索(Breadth-First Search,BFS)是一种用于遍历或搜索树和图数据结构的算法。与深度优先搜索不同,BFS从起始节点开始,首先探索其所有相邻节点,然后再逐层向外扩展。这意味着它会先访问离起始节点最近的节点,然后依次访问距离更远的节点。层序遍历是广度优先搜索。一般是迭代法。用队列(先进先出)来进行。

前序遍历的顺序是:中左右。

中序遍历是:左中右。

后序遍历是:左右中。 

其实表示的就是中在其中所处的位置。

2.算法题

2.1第94题-二叉树的中序遍历

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        # 中序遍历是左中右
        # 这是深度优先搜索dfs
        # 一般是递归法和迭代法
        def dfs(cur, value):
            if cur == None:
                return
            
            dfs(cur.left, value)
            value.append(cur.val)
            dfs(cur.right, value)
        
        value = []
        dfs(root, value)
        return value

递归比较简单,主要还是要掌握迭代的做法。 

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def inorderTraversal(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        # 中序遍历是左中右
        # 这是深度优先搜索dfs
        # 一般是递归法和迭代法
        # 用迭代法
        # 栈,用来储存节点地址
        stack = []
        # 用来储存结果
        res = []
        while root or stack:
            # 先把左节点一直走到底
            if root:
                stack.append(root)
                root = root.left
            # 如果左节点都放入栈中了
            else:
                # 弹出栈中最上面的节点
                temp = stack.pop()
                # 储存该节点的值
                res.append(temp.val)
                # 将该节点的右子节点作为根节点遍历,如果没有右节点这个就是左,再次弹出最上面的节点,储存值,这个值是中,再以该节点的右子节点作为根节点,这个值是右。
                root = temp.right
            
        return res

2.2第104题-二叉树的最大深度

心得:不知道为什么使max_deepth为数值的时候,输出的总是初始值,明明在函数里变化了的。最后只好改成了数组,输出数组最后的值。 

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        # 遍历二叉树,用深度优先搜索的方式
        # 就用前序遍历吧,顺序是中左右
        # 先用递归法,后用迭代法
        
        def dfs(root, deepth, max_deepth):
            if not root:
                return
            deepth += 1
            if max_deepth[-1] < deepth:
                max_deepth.append(deepth)
            dfs(root.left, deepth, max_deepth)
            dfs(root.right, deepth, max_deepth)
            deepth -= 1

        deepth = 0
        max_deepth = [0]
        dfs(root, deepth, max_deepth)

        return max_deepth[-1]

心得:二叉树是一个天然的递归,算最大深度只需要算每个小树的深度,也就是左树深度+右树深度+1,不断递归得到。 

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def maxDepth(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        # 遍历二叉树,用深度优先搜索的方式
        # 就用前序遍历吧,顺序是中左右
        # 先用递归法,后用迭代法
        # 用栈
        if root == None:
            return 0
        left = self.maxDepth(root.left)
        right = self.maxDepth(root.right)
        deepth = max(left,right)

        return deepth+1

2.3第226题-翻转二叉树

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def invertTree(self, root):
        """
        :type root: TreeNode
        :rtype: TreeNode
        """
        steam = root
        def invert(root):
            if root == None:
                return
            # 翻转大树,小树也要翻转,一眼递归
            temp = root.left
            root.left = root.right
            root.right = temp
            invert(root.left)
            invert(root.right)

        invert(root)
        
        return steam

2.4第101题-对称二叉树

给你一个二叉树的根节点 root , 检查它是否轴对称。

示例 1:

心得:自己能想到怎么做,但是代码不知道怎么写,解析是定义了一个新函数来判断。应该思考到如果轴对称需要满足几个条件,两个指针分别指向左右子树,两个指针的val值相等,并且左指针的左子树等于右指针的右子树。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 递归,左右同时开始,结构一样就继续往下,不一样就返回false
        # 层遍历
        # 或者分成左右两个树,每一步都同时往下深度优先搜索
        return self.check(root, root)

    def check(self, p, q):
        if p==None and q==None:
            return True
        if p==None or q==None:
            return False
        return p.val==q.val and self.check(p.left, q.right) and self.check(p.right, q.left)

下面的方法更容易理解 

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 递归,左右同时开始,结构一样就继续往下,不一样就返回false
        # 层遍历
        # 或者分成左右两个树,每一步都同时往下深度优先搜索
        if not root:
			return True
        def dfs(left,right):
            # 递归的终止条件是两个节点都为空
			if not (left or right):
				return True
            # 或者两个节点中有一个为空
			if not (left and right):
				return False
            # 或者两个节点的值不相等
			if left.val!=right.val:
				return False
			return dfs(left.left,right.right) and dfs(left.right,right.left)
		# 用递归函数,比较左节点,右节点
        return dfs(root.left,root.right)

用队列做

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def isSymmetric(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 队列实现
        queue = [root.left, root.right]
        while queue:
            # pop(0)是从头弹出,pop()是从尾弹出
            left = queue.pop(0)
            right = queue.pop(0)
            # 如果都是None,继续循环
            if not (left or right):
                continue
            # 如果其中一个是None
            if not (left and right):
                return False
            if left.val != right.val:
                return False
            queue.append(left.left)
            queue.append(right.right)
            queue.append(left.right)
            queue.append(right.left)
        return True

2.5第543题-二叉树的直径

给你一棵二叉树的根节点,返回该树的 直径 。

二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。

两节点之间路径的 长度 由它们之间边数表示。

示例 1:

输入:root = [1,2,3,4,5]
输出:3
解释:3 ,取路径 [4,2,1,3] 或 [5,2,1,3] 的长度。

心得:想到了怎么来求,但就是不会写。 

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def diameterOfBinaryTree(self, root):
        """
        :type root: TreeNode
        :rtype: int
        """
        if root.left==None and root.right==None:
            return 0
        # 左右两边可以分开算
        # 直径等于左子树直径加右子树直径
        # 选出最大的
        # 父节点的直径等于左右子树中最大直径的边相加
        self.max_depth = 1
        def depth(node):
            if node == None:
                return 0
            L = depth(node.left)
            R = depth(node.right)
            deep = L + R
            self.max_depth = max(deep, self.max_depth)
            
            return max(L, R) + 1
        
        depth(root)

        return self.max_depth

2.6第102题- 二叉树的层序遍历

心得:二叉树必须要熟练掌握BFS和DFS,几乎所有题都会用到,DFS主要就是递归,BFS主要就是队列维护节点。不断弹出和入队。 

第一个有问题的地方就是数组需要==[],而不是None。

第二个地方是store数组需要及时清零。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def levelOrder(self, root):
        """
        :type root: TreeNode
        :rtype: List[List[int]]
        """
        if root == None:
            return []
        # 层序遍历用迭代,迭代用栈或队列
        # 广度优先搜索, BFS加一个层序
        result = []
        queue = []
        queue.append(root)
        store = []
        temp2 = []
        while queue != []:
            temp = queue.pop(0)
            store.append(temp.val)
            if temp.left != None:
                temp2.append(temp.left)
            if temp.right != None:
                temp2.append(temp.right)
            if queue == []:
                result.append(store)
                store = []
                queue = temp2
                temp2 = []
        return result

2.7第108题-将有序数组转化为二叉搜索树

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def sortedArrayToBST(self, nums):
        """
        :type nums: List[int]
        :rtype: TreeNode
        """
        # 递归
        length = len(nums)
        if length == 1:
            return TreeNode(nums[0])
        self.steam = None
        def dfs(nums):
            length = len(nums)
            if length == 1:
                return TreeNode(nums[0])
            mid = nums[length/2]
            left = nums[:length/2]
            L = dfs(left)
            R = None
            if length/2+1 < length:
                right = nums[length/2+1:]
                R = dfs(right)
            self.steam = TreeNode(mid, L, R)
            return self.steam

        dfs(nums)

        return self.steam

2.8第98题-验证二叉搜索树

心得:方法还是很容易想到的,至于遍历方法有递归、迭代、队列,需要自己想一下哪种合适,然后写出每种方法对应的基础框架,在上面进行修改,带入到最左下角的节点,主要考虑终止条件、需要左右子树返回的值,result的填入顺序。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 遍历二叉树,从最左下的点开始遍历,最后数组是绝对升序的
        # 用dfs试试
        result = []
        def dfs(root):
            if root == None:
                return
            dfs(root.left)
            result.append(root.val)
            dfs(root.right)
            return
        
        dfs(root)
        for i in range(1,len(result)):
            if result[i-1] < result[i]:
                continue
            else:
                return False
        
        return True

心得:看了解析,直接递归,每个子树判断一下是否大于下限小于上限即可,并且上下限是实时更新的。这种返回True和False的题最好把递归放到return里面,这样一旦有一个false就能马上弹出递归,很方便。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def isValidBST(self, root):
        """
        :type root: TreeNode
        :rtype: bool
        """
        # 遍历二叉树,从最左下的点开始遍历,最后数组是绝对升序的
        # 用dfs试试
        flag = True
        def helper(root, upper = float('inf'), lower = float('-inf')):
            if root == None:
                return True
            if not (root.val < upper and root.val > lower):
                return False
            return helper(root.left, upper=root.val, lower=lower) and helper(root.right, upper=upper, lower=root.val)
        
        flag = helper(root)

        return flag

2.9第230题-二叉搜索树中第k小元素

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def kthSmallest(self, root, k):
        """
        :type root: TreeNode
        :type k: int
        :rtype: int
        """
        # 首先想到的肯定是遍历打印,然后选出第k个,但是那样效率太低
        # 先找到最小的值,然后慢慢返回查找第k个,中序遍历
        num = 0
        self.result = 0
        def dfs(root, num):
            if root == None:
                return num
            if num == k:
                self.result = root.val
                return num + 1
            temp = dfs(root.left, num)
            if temp == k:
                return k
            num = temp + 1
            if num == k:
                self.result = root.val
                return num + 1
            temp = dfs(root.right, num)
            if temp == k:
                return k

            return temp
 
        dfs(root, num)

        return self.result

用迭代法节省时间,遇到了等于k的就可以结束程序。要熟练掌握迭代法前中后序遍历。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def kthSmallest(self, root, k):
        """
        :type root: TreeNode
        :type k: int
        :rtype: int
        """
        # 首先想到的肯定是遍历打印,然后选出第k个,但是那样效率太低
        # 先找到最小的值,然后慢慢返回查找第k个,中序遍历
        # 用迭代的方法可以不用遍历整个二叉树
        stack = []
        target = 0
        result = 0
        while root or stack:
            if root:
                stack.append(root)
                root = root.left
            else:
                temp = stack.pop()
                target += 1
                if target == k:
                    result = temp.val
                    break
                root = temp.right
        
        return result

2.10第199题-二叉树的右视图

可以用BFS,层序遍历,从左到右遍历存储每层最后一个节点,也可以从右到左,存储每层第一个节点。

可以用DFS,从右开始向下遍历,记录深度,从左子树的时候超过深度就添加到结果中。

两种方法都想到了,但是DFS没写出来,BFS相对简单,写出来了。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def rightSideView(self, root):
        """
        :type root: TreeNode
        :rtype: List[int]
        """
        # 相当于先从右走,如果右不存在再从左走
        # 如果右子树走到头了,左子树比右子树深
        # 则再从左往下走,所以两颗子树应该同时走
        # 层遍历,只保存最右边的
        if root == None:
            return None
        result = []
        result.append(root.val)
        queue = []
        queue.append(root)
        temp2 = []
        store = []
        while queue != []:
            temp = queue.pop(0)
            if temp.right:
                temp2.append(temp.right)
            if temp.left:
                temp2.append(temp.left)
            if queue == []:
                if temp2 != []:
                    result.append(temp2[0].val)
                queue = temp2
                temp2 = []
                
        return result

2.11第114题-二叉树展开为链表

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
  • 展开后的单链表应该与二叉树 先序遍历 顺序相同。

示例 1:

输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

心得:太蠢了,应该想到的,一直想不到怎么使5接到4后面,只需要用前序遍历,维护好子树返回上来的地址就行了。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def flatten(self, root):
        """
        :type root: TreeNode
        :rtype: None Do not return anything, modify root in-place instead.
        """
        # 前序遍历
        # DFS,递归或者迭代
        def dfs(root, result):
            if not root:
                return None
            result.append(root)
            dfs(root.left, result)
            dfs(root.right, result)
            
        result = []
        dfs(root, result)
        for i in range(len(result)-1):
            result[i].right = result[i+1]
            result[i].left = None

        return root

2.12从前序与中序遍历序列构造二叉树

给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。

示例 1:

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]
输出: [3,9,20,null,null,15,7]

心得:递归!! 

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def buildTree(self, preorder, inorder):
        """
        :type preorder: List[int]
        :type inorder: List[int]
        :rtype: TreeNode
        """
        # 前序遍历中,第一个数一定是根节点
        # 中序遍历中,根节点对应val左边的数就是左子树,右边的是右子树
        # 递归
        def dfs(preorder, inorder):
            if len(preorder) == 0:
                return None
            num = preorder.pop(0)
            root = TreeNode(num)
            ind = inorder.index(num)
            left = inorder[:ind]
            right = inorder[ind+1:]
            root.left = dfs(preorder[:len(left)], left)
            root.right = dfs(preorder[len(left):], right)

            return root
            
        return dfs(preorder, inorder)

2.13第437题-路径总和|||

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。

路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

示例 1:

输入:root = [10,5,-3,3,2,null,11,3,-2,null,1], targetSum = 8
输出:3
解释:和等于 8 的路径有 3 条,如图所示。

心得:想用一个数组把每个往下的路径的点存起来,遍历得出有多少条,试一下,果然超时。

# Definition for a binary tree node.
# class TreeNode(object):
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution(object):
    def pathSum(self, root, targetSum):
        """
        :type root: TreeNode
        :type targetSum: int
        :rtype: int
        """
        # dfs前序遍历
        self.count = 0
        self.depth = 0
        def dfs(root, store):
            if not root:
                return None
            self.depth += 1
            deep = self.depth
            store.append(root.val)
            num = 0
            
            for i in range(len(store)):
                for j in range(i,len(store)):
                    num += store[j]
                if num == targetSum:
                    self.count += 1
                num = 0
            temp = dfs(root.left, store)
            if temp:
                store = store[:deep]
            dfs(root.right, store)
            self.depth = deep-1

            return root
        
        store = []
        dfs(root, store)

        return self.count

2.14第236题-二叉树的最近公共祖先

暴力存储

# 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 lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        # DFS, 用栈的方式迭代
        stack = []
        stack.append(root)
        store = []
        store2 = []
        store3 = []
        depth = 0
        while stack or root:
            if store == [] or store2 == []:
                if root:
                    stack.append(root)
                    stack.append(root)
                    if root == p:
                        store = copy.deepcopy(stack)
                    if root == q:
                        store2 = copy.deepcopy(stack)
                    
                    root = root.left
                else:
                    temp = stack.pop()
                    root = temp.right
            else:
                break

        a = len(store)
        b = len(store2)
        for i in reversed(range(a)):
            for j in reversed(range(b)):
                if store[i].val == store2[j].val:
                    return store[i]

猜你喜欢

转载自blog.csdn.net/Orange_sparkle/article/details/132939792