zoukankan      html  css  js  c++  java
  • 树与树算法

    树与树算法

    树的概念

    是一种抽象数据类型(ADT)或是实作这种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>=1)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

    • 每个节点有零个或多个子节点;

    • 没有父节点的节点称为根节点;

    • 每一个非根节点有且仅有一个父节点;

    • 除了根节点外。每个子节点可以分为多个不想交的子树;

    树的术语

    • 节点的度:一个节点中含有子树的个数称为该节点的度;

    • 树的度:一棵树中,最大的节点的度称为树的度;

    • 叶节点终端节点:度为零的节点;

    • 父亲节点父节点:若一个节点含有子节点,则这个节点称为其子节点的父节点;

    • 孩子节点子节点:一个节点含有的子树的根节点称为该节点的子节点;

    • 兄弟节点:具有相同父节点的节点互称为兄弟节点;

    • 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;

    • 树的高度深度:树中节点的最大层次;

    • 堂兄弟节点:父节点在同一层的节点互为堂兄弟;

    • 节点的祖先:从根到该节点所经分支上的所有节点;

    • 子孙:以某节点为根的子树中任一节点都称为该节点的子孙。

    • 森林:由m(m>=0)棵互不相交的树的集合称为森林;

    树的种类

    • 无序树:树中任意节点的子节点之间没有顺序关系,这种树称为无序树,也称为自由树;
    • 有序树:树中任意节点的子节点之间有顺序关系,这种树称为有序树;
      • 二叉树:每个节点最多含有两个子树的树称为二叉树;
        • 完全二叉树:对于一颗二叉树,假设其深度为d(d>1)。除了第d层外,其它各层的节点数目均已达最大值,且第d层所有节点从左向右连续地紧密排列,这样的二叉树被称为完全二叉树,其中满二叉树的定义是所有叶节点都在最底层的完全二叉树;
        • 平衡二叉树(AVL树):当且仅当任何节点的两棵子树的高度差不大于1的二叉树;
        • 排序二叉树(二叉查找树(英语:Binary Search Tree),也称二叉搜索树、有序二叉树);
      • 霍夫曼树(用于信息编码):带权路径最短的二叉树称为哈夫曼树或最优二叉树;
      • B树:一种对读写操作进行优化的自平衡的二叉查找树,能够保持数据有序,拥有多余两个子树。

    常见的一些树的应用场景

    • xml,html等,那么编写这些东西的解析器的时候,不可避免用到树
    • 路由协议就是使用了树的算法
    • mysql数据库索引
    • 文件系统的目录结构
    • 所以很多经典的AI算法其实都是树搜索,此外机器学习中的decision tree也是树结构 # 二叉树

    二叉树的基本概念

    二叉树是每个节点最多有两个子树的树结构。通常子树被称作“左子树”(left subtree)和“右子树”(right subtree)

    二叉树的性质(特性)

    • 性质1: 在二叉树的第i层上至多有2^(i-1)个结点(i>0)
    • 性质2: 深度为k的二叉树至多有2^k - 1个结点(k>0)
    • 性质3: 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0=N2+1;
    • 性质4:具有n个结点的完全二叉树的深度必为 log2(n+1)
    • 性质5:对完全二叉树,若从上至下、从左至右编号,则编号为i 的结点,其左孩子编号必为2i,其右孩子编号必为2i+1;其双亲的编号必为i/2(i=1 时为根,除外)

    二叉树的节点表示以及树的创建

    通过使用Node类定义三个属性,分别为elem本身的值,还有lchild左孩子和rchild右孩子。

    class Node(object):
        """节点类"""
        def __init__(self, elem=-1, lchild=None, rchild=None):
            self.elem = elem
            self.lchild = lchild
            self.rchild = rchild
            
        class Tree(object):
        """树类"""
            def __init__(self, root=None):
                self.root = root
            def add(self, elem):
            """为树添加节点"""
            node = Node(elem)
            # 如果树是空的,则对根节点赋值
            if self.root == None:
                self.root = node
            else:
                queue = []
                queue.append(self.root)
                # 对已有的节点进行层次遍历
                while queue:
                    #弹出队列的第一个元素
                    cur = queue.pop(0)
                    if cur.lchild == None:
                        cur.lchild = node
                        return
                    elif cur.rchild == None:
                        cur.rchild = node
                        return
                    else:
                        # 如果左右子树都不为空,加入队列继续判断
                        queue.append(cur.lchild)
                        queue.append(cur.rchild)
    

    二叉树的遍历

    树的遍历是树的一种重要的运算。所谓遍历是指对树中所有结点的信息的访问,即依次对树中每个结点访问一次且仅访问一次,我们把这种对所有节点的访问称为遍历(traversal)。那么树的两种重要的遍历模式是深度优先遍历和广度优先遍历,深度优先一般用递归,广度优先一般用队列。一般情况下能用递归实现的算法大部分也能用堆栈来实现。

    深度优先遍历

    对于一颗二叉树,深度优先搜索(Depth First Search)是沿着树的深度遍历树的节点,尽可能深的搜索树的分支。
    那么深度遍历有重要的三种方法。这三种方式常被用于访问树的节点,它们之间的不同在于访问每个节点的次序不同。这三种遍历分别叫做先序遍历(preorder),中序遍历(inorder)和后序遍历(postorder)。我们来给出它们的详细定义,然后举例看看它们的应用。

    • 先序遍历在先序遍历中,我们先访问根节点,然后递归使用先序遍历访问左子树,再递归使用先序遍历访问右子树:根节点->左子树->右子树
    def preorder(self, root):
          """递归实现先序遍历"""
          if root == None:
              return
          print(root.elem)
          self.preorder(root.lchild)
          self.preorder(root.rchild)
    
    • 中序遍历 在中序遍历中,我们递归使用中序遍历访问左子树,然后访问根节点,最后再递归使用中序遍历访问右子树:左子树->根节点->右子树
    def inorder(self, root):
          """递归实现中序遍历"""
          if root == None:
              return
          self.inorder(root.lchild)
          print(root.elem)
          self.inorder(root.rchild)
    
    • 后序遍历 在后序遍历中,我们先递归使用后序遍历访问左子树和右子树,最后访问根节点:左子树->右子树->根节点
    def postorder(self, root):
          """递归实现后续遍历"""
          if root == None:
              return
          self.postorder(root.lchild)
          self.postorder(root.rchild)
          print(root.elem)
    

    广度优先遍历(层次遍历)

    从树的root开始,从上到下从左到右遍历整个树的节点。

    def breadth_travel(self, root):
            """利用队列实现树的层次遍历"""
            if root == None:
                return
            queue = []
            queue.append(root)
            while queue:
                node = queue.pop(0)
                print(node.elem)
                if node.lchild != None:
                    queue.append(node.lchild)
                if node.rchild != None:
                    queue.append(node.rchild)
    

    二叉搜索树

    概念:二叉查找树(Binary Search Tree),(又:[二叉搜索树],二叉排序树)它或者是一棵空树,或者是具有下列性质的[二叉树]: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为[二叉排序树]。

    LeetCode题目总结

    94. 二叉树的中序遍历

    题目:给定一个二叉树,返回它的中序遍历。
    解题思路:中序遍历(左子树->根节点->右子树),使用递归的思想。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def inorderTraversal(self, root):
            ans=[]
            def helper(root):
                # 如果root为空则跳出循环
                if root == None:
                    return
                helper(root.left)
                ans.append(root.val)
                helper(root.right)
            helper(root)
            return ans
    

    144. 二叉树的前序遍历

    题目:给定一个二叉树,返回它的前序遍历。
    解题思路:前序遍历(根节点->左子树->右子树),使用递归的思想。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def preorderTraversal(self, root: TreeNode) -> List[int]:
            ans = []
            def helper(root):
                if root == None:
                    return 
                ans.append(root.val)
                heloer(root.left)
                helper(root.right)
            helper(root)
            return ans
    

    145. 二叉树的后序遍历

    题目:给定一个二叉树,返回它的后续遍历。
    解题思路:后续遍历(左子树->右子树->根节点),使用递归思想。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def postorderTraversal(self, root: TreeNode) -> List[int]:
            ans = []
            def helper(root):
                if root == None:
                    return
                helper(root.left)
                helper(root.right)
                ans.append(root.val)
            helper(root)
            return ans
    

    102. 二叉树的层次遍历

    题目:给定一个二叉树,返回其按层次遍历的节点值。(即逐层地,从左到右访问所有节点)。
    解题思路:层次遍历属于广度遍历,优先使用队列。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def levelOrder(self, root: TreeNode) -> List[List[int]]:
            if root == None:
                return []
            # 定义一个队列和一个列表
            q, ans = collections.deque([root]), []
            while q:
                cur_level = []
                n = len(q)
                for _ in range(n):
                    cur = q.popleft()
                    cur_level.append(cur.val)
                    if cur.left:
                        q.append(cur.left)
                    if cur.right:
                        q.append(cur.right)
                ans.append(cur_level)
            return ans
    

    定义队列
    q = collections.deque([root])

    100. 相同的树

    题目:给定两个二叉树,编写一个函数来检验它们是否相同。如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。
    解题思路:使用先序遍历两棵树的方法逐个元素确认是否相同,找对判断的出口是程序的关键。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    class Solution:
        def isSameTree(self, p: TreeNode, q: TreeNode) -> bool:
        # 判断两棵树是否为空,如果为空则返回True
        if p==None and q==None:
            return True
        # 使用先序遍历的方法逐个元素确认是否相同
        if p and q: 
            return p.val==q.val and self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
            return Flase
    

    101. 对称二叉树

    题目:给定一个二叉树,检查它是否是镜像对称的。
    解题思路:找准出口,采用递归的思想。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def isSymmetric(self, root: TreeNode) -> bool:
            if root==None:
                return True
            def helper(left, right):
                if left==None and right==None:
                    return True
                if left and right:
                    return left.val==right.val and helper(left.left,right.right) and helper(left.righr, right.left)
                return False
            return helper(root.left, root.right)
    

    二叉搜索树

    95. 不同的二叉搜索树(二)

    题目:给定一个整数 n,生成所有由 1 ... n 为节点所组成的二叉搜索树。
    解题思路: 分治,对于1~n这n个数,都有可能成为根结点。枚举1到n,对于 1<=i<=n,有i的左结点为[1,i-1] (i-1 >=1),右结点是[i+1,n] i+1 <=n
    递归的获取左右子结点,然后只需要建立根结点,使根结点的左右子结点指向获取的左右子结点即可。
    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def generateTrees(self, n: int) -> List[TreeNode]:
            def helper(s, e):
                if s > e:
                    return [None]
                ans = []
                for i in range(s, e+1):
                    L = helper(s, i-1)
                    R = helper(i+1, e)
                    for left in L:
                        for right in R:
                            root = TreeNode(i)
                            root.left, left.root = left, right
                            ans.append(root)
                    return ans
                return helper(1, n) if n >= 1 else []
    

    96. 不同的二叉搜索树

    题目:给定一个整数n, 求以 1 ... n 为节点组成的二叉搜索树有多少种。
    解题思路:
    解题代码:

    
    

    103. 二叉树的锯齿层次遍历

    题目:给定一个二叉树,返回其节点值的锯齿形层次遍历。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

    解题思路:此题与层次遍历很相似,只是在输出每层结果的时候判断是应该正序输出还是逆序输出。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def zigzagLevelOrder(self, root: TreeNode) -> List[List[int]]:
            if root == None:
                return []
            # 定义队列
            q = collections.deque([root])
            ans = []
            level = 0
            while q:
                cur_level = []
                cur_size = len(q)
                for i in range(cur_size):
                    cur = q.popleft()
                    if cur.left:
                        q.append(cur.left)
                    if cur.right:
                        q.append(cur.right)
                    cur_level.append(cur.val)
                ans.append(cur_level[::-1] if level & 1 else cur_level)
                level += 1
            return ans
    

    111.二叉树的最小深度

    题目:给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

    解题思路:此题使用递归的方法处理,关键点是找到4个出口。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def minDepth(self, root: TreeNode) -> int:
            def dfs(root, depth):
                # 4个if语句找到4个出口
                if root == None:
                    return depth
                if root.left == None and root.right:
                    return dfs(root.right, depth+1)
                if root.left and root.right==None:
                    return dfs(root.left, depth+1)
                if root.left==None and root.right == None:
                    return depth + 1
                # 如果以上4个出口都不符合则使用递归的方法循环处理
                L , R = dfs(root.left,depth+1), dfs(root.right,depth+1)    
                return min(L,R)
            if root == None:
                return 0
            return dfs(root, 0)
    

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

    题目:根据一棵树的前序遍历与中序遍历构造二叉树。例如,给出前序遍历 preorder = [3,9,20,15,7] 中序遍历 inorder = [9,3,15,20,7]。

    解题思路:前序遍历为: 3,9,20,15,7 中序遍历为: 9,3,15,20,7

    前序遍历的第一个一定是根结点,而中序遍历的根结点把树准确的分为左子树和右子树。所以,在中序遍历中查找3,发现下标为1,那么[0,1)为左子树,[2,5)为右子树。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
            # 找到程序出口
            if not preorder:
                return None
            root = TreeNode(preorder[0])
            index = inorder.index(preorder[0])
            # 采用递归的方式构造左子树和右子树
            root.left = self.buildTree(preorder[1: index + 1], inorder[:index])
            root.right = self.buildTree(preorder[1: index + 1], inorder[:index]) 
            # 最后只需返回二叉树的根节点
            return root
    

    106.从中序与后序遍历序列构造二叉树

    题目:根据一棵树的中序遍历与后序遍历构造二叉树。例如,给出 中序遍历 inorder = [9,3,15,20,7] 后序遍历 postorder = [9,15,7,20,3]。

    解题思路:解题思路与105题类似,不过根节点在最后。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    # 此题需要重新复习
    class Solution:
        def buildTree(self, inorder: List[int], postorder: List[int]) -> TreeNode:
            def build(ib, ie, pb, pe, inorder, preorder):
                if ib >= ie or pb > pe:
                    return None
                root = TreeNode(preorder[pe - 1])
                len_left = inorder.index(preorder[pe - 1]) - ib
                root.left = build(ib, ib + len_left, pb, pb + len_left, inorder, preorder)
                root.right = build(ib + len_left + 1, ie, pb + len_left, pe - 1, inorder, preorder)
                return root
            return build(0, len(inorder), 0, len(postorder), inorder, postorder)
    

    112.路径总和

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

    解题思路:熟练运用递归的思想,每次遇到根节点就减掉此根节点的值,找到两个出口,第一是根节点是否为空;第二是减去根节点的值后,如果此根节点的左右子树均为空,则判断减掉之后的值是否为0。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def hasPathSum(self, root: TreeNode, sum: int) -> bool:
            # 第一个出口,如果根节点为空,则返回False
            if not root:
                return False
            sum -= root.val
            # 第二个出口,如果左右节点均为空,则判断剩下的值是否为0
            if not root.left and not root.right:
                return sum == 0
            # 递归调用以上程序,并且使用or,只要有一个为True则返回True
            return self.hasPathSum(root.left, sum) or self.hasPathSum(root.right, sum)
    

    113.路径总和(2)

    题目描述:给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

    解题思路:此题。。。。。。。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    # 此题理解的不是很好,还应该仔细复习
    class Solution:
        def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
            ans, path = [], []
            self.dfs(0, root, sum, path, ans)
            return ans
        
        def dfs(self, cur, root, sum, path, ans):
            if not root: return 
            if not root.left and not root.right:
                if cur+root.val == sum:
                    path.append(root.val)
                    ans.append(path[:])
                    path.pop()
                return        
            path.append(root.val)
            self.dfs(cur+root.val,root.left,sum,path,ans)
            self.dfs(cur+root.val,root.right,sum,path,ans)
            path.pop()
    

    114.二叉树展开为列表

    题目描述:给定一个二叉树,原地将它展开为链表。

    解题思路:按照题目中给的例子,应该按照先序遍历的方法将二叉树展开为链表。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    # 此题涉及二叉树和链表的知识点,需要重新思考,解题。
    class Solution:
        def flatten(self, root: TreeNode) -> None:
            """
            Do not return anything, modify root in-place instead.
            """
            if not root:
                return
            self.flatten(root.left)
            self.flatten(root.right)
            left = root.left
            if not left: return
            while left.right:
                left = left.right
            left.right = root.right
            root.right = root.left
            root.left = None
    

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

    117.充每个节点的下一个右侧节点指针

    124.二叉树的最大路径和

    129.求根到叶子节点数字之和

    题目描述:给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。

    解题思路:此题使用先序遍历的方法,当判断此节点为叶子节点时则把值加到ans[0]上,如果不是叶子节点,则把数值加到cur上。

    解题代码:

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.left = None
    #         self.right = None
    
    class Solution:
        def sumNumbers(self, root: TreeNode) -> int:
            def dfs(root, cur, ans):
                if not root:
                    return 
                # 如果是叶子节点,则加到ans[0]上
                if not root.left and not root.right:
                    ans[0] += cur * 10 + root.val
                # 如果不是叶子节点,则把对应的值加到cur上
                else:
                    if root.left:
                        dfs(root.left, cur * 10 + root.val, ans)
                    if root.right:
                        dfs(root.right, cur * 10 + root.val, ans)
            ans = [0]
            dfs(root, 0, ans)
            return ans[0]
    

    156.上下翻转二叉树

    173.二叉搜索迭代器

    199.二叉树的右视图

  • 相关阅读:
    JDBC 查询的三大参数 setFetchSize prepareStatement(String sql, int resultSetType, int resultSetConcur)
    有空必看
    SpringMVC 利用AbstractRoutingDataSource实现动态数据源切换
    FusionCharts JavaScript API Column 3D Chart
    FusionCharts JavaScript API
    FusionCharts JavaScript API
    Extjs 继承Ext.Component自定义组件
    eclipse 彻底修改复制后的项目名称
    spring 转换器和格式化
    Eclipse快速生成一个JavaBean类的方法
  • 原文地址:https://www.cnblogs.com/ffjsls/p/11261159.html
Copyright © 2011-2022 走看看