数据结构之树结构

这篇博客讲的很好:二叉树题目汇总

关于解树的题目总的就一条,使用递归(递归的本质其实也是编译器帮你维护的栈结构)或者是栈,找好递归的终止条件

所有的题目都经过调试,基本没问题。

Talk is cheap, show my code to you.

一.前序遍历,中序遍历,后序遍历

法一:递归实现(其实递归就是一种栈) 
法二:栈+迭代

递归与非递归

递归版(只要改变后面的return中的东西即可)

前序遍历(根左右)

# 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]:
        if not root:
            return []
        return [root.val] + self.preorderTraversal(root.left) + self.preorderTraversal(root.right)

中序遍历(左 根 右)

class Solution:
    def inorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        return self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)

后序遍历(左 右 根)

class Solution:
    def postorderTraversal(self, root: TreeNode) -> List[int]:
        if not root:
            return []
        return self.postorderTraversal(root.left) + self.postorderTraversal(root.right) +  [root.val] 

 非递归版

非递归版就是用栈来进行即可

前序

# 先序打印二叉树(非递归)
def preOrderTravese(node):
    stack = [node]
    while len(stack) > 0:
        print(node.val)
        if node.right is not None:
            stack.append(node.right)
        if node.left is not None:
            stack.append(node.left)
        node = stack.pop()

 中序

# 中序打印二叉树(非递归)
def inOrderTraverse(node):
    stack = []
    pos = node
    while pos or len(stack) > 0:
        if pos:
            stack.append(pos)
            pos = pos.left
        else:
            pos = stack.pop()
            print(pos.val)
            pos = pos.right

 后序

# 后序打印二叉树(非递归)
# 使用两个栈结构
# 第一个栈进栈顺序:左节点->右节点->跟节点
# 第一个栈弹出顺序: 跟节点->右节点->左节点(先序遍历栈弹出顺序:跟->左->右)
# 第二个栈存储为第一个栈的每个弹出依次进栈
# 最后第二个栈依次出栈
def postOrderTraverse(node):
    stack = [node]
    stack2 = []
    while len(stack) > 0:
        node = stack.pop()
        stack2.append(node)
        if node.left is not None:
            stack.append(node.left)
        if node.right is not None:
            stack.append(node.right)
    while len(stack2) > 0:
        print(stack2.pop().val)

 

二.层次遍历

宽度优先搜索BFS,迭代,使用先进先出的队列queue或两端都可以进出的deque,要让队列有进有出。

def layerTraverse(node):
    
    if not node:
        return None
 
    queue = []  
    queue.append(node)
    while len(queue) > 0:
        tmp = queue.pop(0)
        print(tmp.val)
        if node.left:
            queue.append(tmp.left)
        if node.right:
            queue.append(tmp.right)

三.求树的节点数

遍历。return 1+左子树的个数+右子树的个数

# 求二叉树节点个数
def treeNodenums(node):
    if node is None:
        return 0
    nums = treeNodenums(node.left)
    nums += treeNodenums(node.right)
    return nums + 1

四.求树的叶子数

二叉树叶子节点

遍历。return 左子树的叶结点数+右子树的叶结点数

def leave(self,root):   #递归求叶子节点个数
      if root==None:
          return 0
      elif root.lchild ==None and root.rchild == None :
          return 1
      else:
          return (self.leave(root.lchild)+self.leave(root.rchild))

 

五.求二叉树第k层的结点数

遍历。k作为递归函数的参数,每降低一层,k就要-1。 
return KNodeCount(pRoot->left,k-1)+KNodeCount(pRoot->right,k-1); 

def KNodeCount(node,k):
    if node==None or k<0:
        return 0
    if k==0:
        return 1
    return KNodeCount(node.left,k-1)+KNodeCount(node.right,k-1)

六.求树的深度

遍历。对根结点求深度,就是求其左右子树的深度中最大的那个深度+1

# 二叉树的最大深度
def bTreeDepth(node):
    if node is None:
        return 0
    ldepth = bTreeDepth(node.left)
    rdepth = bTreeDepth(node.right)
    return (max(ldepth, rdepth) + 1)

七.树的最长路径

闭包的实现原理,如何在内部函数修改外部函数的变量

#求解树的最长路径左边最长+右边最长咯   使用闭包就可以了
def largePath(node):
    ans=0
    def getHeight(node):
        nonlocal ans  #函数内部使用函数外的变量,使用闭包
        if node==None:
            return 0
        l=getHeight(node.left)
        r=getHeight(node.right)
        ans=max(ans,l+r+1)
        return max(l,r)+1

    getHeight(node)
    return ans

八.树的宽度

用python写一下求二叉树深度和宽度的代码,求深度用递归;求宽度用队列,然后把每层的宽度求出来,找出最大的就是二叉树的宽度

def treeWidth(node):
    if node==None:
        return 0
    curwidth=1
    maxwidth=0
    stack=[]
    stack.append(node)
    while len(stack):
        n=curwidth
        for _ in range(n):
            node=stack.pop(0)
            curwidth-=1#这里是减一
            if node.left:
                stack.append(node.left)
                curwidth+=1
            if node.right:
                stack.append(node.right)
                curwidth+=1
        maxwidth=max(maxwidth,curwidth)
    return maxwidth

 

九.判断两颗二叉树是否结构相同

判断两个树是否相等和判断tree1是否包含tree2 python实现

(不用考虑val是否相同。结构相同:两颗二叉树的两个根结点对应的的左子树和右子树结构相同。) 
遍历。return 判断两棵树的左子树是否相同&&判断两棵树的右子树是否相同; 
什么叫相同?两个对应的结点同时为空 
什么叫不同?两个对应的结点中只有一个结点为空 
那都不为空时呢?去递归呗。

def equal(node_a, node_b):
    """
    判断两个树是否相等
    :param node_a: 
    :param node_b: 
    :return: 
    """
    if not node_a and not node_b:
        return True
    elif not node_a and node_b or node_a and not node_b or node_a.val != node_b.val:
        return False
    else:
        return equal(node_a.left, node_b.left) and equal(node_a.right, node_b.right)

def tree1_have_tree2(tree1, tree2):
    """
    判断tree1是否包含tree2
    :param tree1: 
    :param tree2: 
    :return: 
    """
    if not tree2:
        return True
    if not tree1:
        return False
    if tree1.val != tree2.val:
        return False
    return tree1_have_tree2(tree1.left, tree2.left) and tree1_have_tree2(tree1.right, tree2.right)

 

十.求二叉树的镜像(翻转二叉树)

剑指offer之二叉树的镜像(Python)

思路:判断本身是否不为空,然后将左子树和右子树相互交换。然后判断是否有左子树,有的话递归函数;同理,判断是否有右子树,有的话递归函数。

class TreeNode():
    def __init__(self,x):
        self.val = x
        self.left = None
        self.right = None
def function(root):
    if root:
        root.left,root.right = root.right,root.left
        if root.left:
            function(root.left)
        if root.right:
            function(root.right)

十一.求两个节点的最近公共祖先结点(LCA)

python--lintcode88. 最近公共祖先

用到了分治法的思想,想一想,若是对于一个树结点,在左子树中找到了A,在右子树中找到了B,那说明此结点是公共节点

class TreeNode:
    def __init__(self, val):
        self.val = val
        self.left, self.right = None, None
 
 
class Solution:
    """
       @param: root: The root of the binary search tree.
       @param: A: A TreeNode in a Binary.
       @param: B: A TreeNode in a Binary.
       @return: Return the least common ancestor(LCA) of the two nodes.
       """
 
    def lowestCommonAncestor(self, root, A, B):
        # A&B=>LCA
        # !A&!B=>None
        # A&!B=>A
        # B&!A=>B
        if(root is None or root==A or root==B):
            return root        #若root为空或者root为A或者root为B,说明找到了A和B其中一个
        left=self.lowestCommonAncestor(root.left,A,B)
        right=self.lowestCommonAncestor(root.right,A,B)
        if(left is not None and right is not None):
            return root      #若左子树找到了A,右子树找到了B,说明此时的root就是公共祖先
        if(left is None):    #若左子树是none右子树不是,说明右子树找到了A或B
            return right
        if(right is None):   #同理
            return left
        return None
 
 
 
a=Tree = TreeNode(2)
b=Tree.left = TreeNode(1)
c=Tree.right = TreeNode(3)
d=b.left=TreeNode(4)
s = Solution()
print(s.lowestCommonAncestor(a,b,d).val)

十二.求任意两个结点的距离

leetcode 找到树中距离最大的两个结点,Python实现

遍历。先找到两个结点的最低公共祖先结点,然后分别计算最低公共祖先结点(根结点)与它们的距离,最后相加即可。也就是两个函数,第二个函数用于求距离:遍历,先在左子树中找,if没找到的话,再在右子树中找,找到了则return res+1;最后都没找到就return -1。

其实就是求深度和最近公共父节点的结合,不难

def LCA(root,nodeA,nodeB):
    if root==None:
        return root
    if nodeA==root or nodeB==root:
        return nodeA
    left=LCA(root.left,nodeA,nodeB)
    right=LCA(root.right,nodeA,nodeB)
    if left is not None and right is not None:
        return root
    if left==None:
        return right
    if right==None:
        return left
    return None
#print(LCA(a,f,g).val)

def get_depth(node):
    if node is None:
        return 0
    else:
        return max(1+get_depth(node.left),1+get_depth(node.right))


def node_distance(root,nodeA,nodeB):
    node=LCA(root,nodeA,nodeB)
    A_depth=get_depth(nodeA)
    B_depth=get_depth(nodeB)
    node_depth=get_depth(node)
    return (node_depth-A_depth)+(node_depth-B_depth)

十三.找出二叉树中某个结点的所有祖先结点

遍历。bool递归函数。if(找出该结点的左子树中某个结点的所有祖先结点||找出该结点的右子树。。。),如果为if条件为true,则输出该结点。

res=[]
def findAllAnc(root,node):
    if root is None:
        return None
    if node==root:
        return True
    if findAllAnc(root.left,node) or findAllAnc(root.right,node):
        res.append(root.val)
        return True
    return False

十四.判断二叉树是否为平衡二叉树

扩展:二叉搜索树,在中序遍历中是依次增大的

判断二叉树是否为二叉搜索树、完全二叉树、平衡二叉树 Python实现

平衡二叉树:左右两个子树的高度差不超过1。左右子树也为平衡二叉树

遍历。if(判断左子树是否是平衡二叉树&&判断右子树是否是平衡二叉树),再在这个if中,if(判断深度差left-right的绝对值是否小于1)。。。挺有挑战的一道题。

# 判断二叉树是否为平衡二叉树
# 先判断该节点是否平衡
# 再递归去判断左节点和右节点是否平衡

# 递归求当前节点的深度
def getdepth(node):
    if not node:
        return 0
    ld = getdepth(node.left)
    rd = getdepth(node.right)
    return max(ld, rd) + 1


def isB(head):
    if not head:
        return True
    ld = getdepth(head.left)
    rd = getdepth(head.right)
    if abs(ld - rd) > 1:
        return False
    return isB(head.left) and isB(head.right)

十五.二叉树中和为某一值的路径

剑指offer之二叉树中和为某一值的路径(Python)

思路:首先要理解题意,是从根节点往子节点连。

1、如果只有根节点或者找到叶子节点,我们就把其对应的val值返回

2、如果不是叶子节点,我们分别对根节点的左子树、右子树进行递归,直到找到叶子结点。然后遍历把叶子结点和父节点对应的val组成的序列返回上一层;如果没找到路径,其实也返回了序列,只不过是[]

# -*- coding:utf-8 -*-
class TreeNode():
	def __init__(self,x):
		self.val = x
		self.left = None
		self.right = None
 
def function(root,target_number):
	result = []
	if not root:
		return result
#	如果只有根节点或者找到叶子节点,我们就把其值返回
	if not root.left and not root.right and root.val == target_number:
		return [[root.val]]
	else:
#	如果不是叶子节点,我们分别对根节点的左子树、右子树进行递归,注意修改变量:
		left = function(root.left,target_number - root.val)
		right = function(root.right,target_number - root.val)
		for item in left+right:
			result.append([root.val]+item)
		return result

十六.根据前序和中序重建二叉树

构建 左子树的前序中序 和 右子树的前序中序,再分左右子树递归。 

已知前序遍历和中序遍历,重建二叉树(剑指offer笔记)

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。

由前序遍历可知,1肯定是根节点,有中序遍历可知,1前面的三个数 (4,7,2)肯定是左子树节点的值。因此左子树共有三个左子节点。1后面的四个数(5,3,8,6)肯定是右子树的值。这样我们就找到了左右子树对应的序列。·

这样我们就分别找到了左右子树的前序遍历和中序比那里,用同样的方法分别取构建左右子树,也就是说可以用递归方法去实现。

由前序得到根节点,由中序得到左右子树

# -*- coding:utf-8 -*-
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    # 返回构造的TreeNode根节点
    def reConstructBinaryTree(self, pre, tin):
        #如果排列长度为0则二叉树为空
        if (len(pre)==0 or len(tin)==0):
            return None
        else:
            #把前序排列序列的第一个节点拿出来
            root1 = TreeNode(pre.pop(0))
            index1 = tin.index(root1.val)
            left_pre_next = pre        #这个时候的pre 已经没了第一个节点
            left_tin_next = tin[:index1]
            root1.left  = self.reConstructBinaryTree(left_pre_next,left_tin_next)
            R_pre_next = pre
            R_tin_next = tin[index1+1:]
            root1.right = self.reConstructBinaryTree(R_pre_next,R_tin_next)
        return root1           

十七.二叉树与双向链表

中序遍历。简单题。定义两个变量,一个变量用于记录头结点,一个变量用于改变指向。 

剑指offer:二叉搜索树与双向链表(Python)

1.核心算法依旧是中序遍历
2.不是从根节点开始,而是从中序遍历得到的第一个节点开始
3.定义两个辅助节点listHead(链表头节点)、listTail(链表尾节点)。事实上,二叉树只是换了种形式的链表;listHead用于记录链表的头节点,用于最后算法的返回;listTail用于定位当前需要更改指向的节点。了解了listHead和listTail的作用,代码理解起来至少顺畅80%。
4.提供我画的算法的过程图,有点丑,但有助于理解(帮你们画了,你们就不用画啦),另外图中右上角步骤三应该是“2”标红,“2”和“1”中间的连接为单线黑~~~ 
 

class Solution:
    def __init__(self):
        self.listHead = None
        self.listTail = None
    def Convert(self, pRootOfTree):
        if pRootOfTree==None:
            return
        self.Convert(pRootOfTree.left)
        if self.listHead==None:
            self.listHead = pRootOfTree
            self.listTail = pRootOfTree
        else:
            self.listTail.right = pRootOfTree
            pRootOfTree.left = self.listTail
            self.listTail = pRootOfTree
        self.Convert(pRootOfTree.right)
        return self.listHead

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值