python | 算法-二叉树宽度、判断以及各种实际问题

针对b站视频左神算法与数据结构,自己练习对应的python代码

相关链接:

1️⃣b站视频地址

2️⃣视频笔记(其实主要是题目截图)

1. 二叉树宽度

📝用hash表

# 用hash表
def BTWidth2(self, root):
    if root is None:
        return 0
    ht = {} # hash table
    ht[root] = 1
    que = [] # queue
    que.append(root)
    width = 0 # save max width
    count = 0 # save cur level nodes number
    cur_level = 1
    while len(que) != 0:
        cur = que.pop(0)
        node_level = ht[cur]
        if node_level == cur_level:
            count += 1
        else:
            width = max(width, count)
            count = 1
            cur_level += 1
        if cur.lchild is not None:
            que.append(cur.lchild)
            ht[cur.lchild] = cur_level + 1
        if cur.rchild is not None:
            que.append(cur.rchild)
            ht[cur.rchild] = cur_level + 1
    else:
        width = max(width, count)

    return width

📝不用hash表

# 不用hash表
def BTWidth1(self, root):
    if root is None:
        return 0
    width = 0
    now_last = root # save cur level's last node
    next_last = None # save next level's last node
    queue = []
    queue.append(root)
    count = 0 # count cur level nodes num
    while len(queue) != 0:
        p = queue.pop(0)
        count += 1
        if p.lchild is not None:
            queue.append(p.lchild)
            next_last = p.lchild
        if p.rchild is not None:
            queue.append(p.rchild)
            next_last = p.rchild
        if p == now_last:
            width = max(width, count)
            count = 0
            now_last = next_last
    return width

完整版代码测试结果:(与上一篇同样的例子)

点击查看代码
class BTNode():
    def __init__(self, key=None, lchild=None, rchild=None):
        self.key = key
        self.lchild = lchild
        self.rchild = rchild

class BiTree():
    def __init__(self, data_list):
        self.data = iter(data_list)

    def createBiTree(self, root=None):
        try:
            nextone = next(self.data)
            if nextone == '#':
                root = None
            else:
                root = BTNode(nextone)
                root.lchild = self.createBiTree(root.lchild)
                root.rchild = self.createBiTree(root.rchild)
        except Exception as e:
            print(e)
        return root

    # 不用hash表
    def BTWidth1(self, root):
        if root is None:
            return 0
        width = 0
        now_last = root # save cur level's last node
        next_last = None # save next level's last node
        queue = []
        queue.append(root)
        count = 0 # count cur level nodes num
        while len(queue) != 0:
            p = queue.pop(0)
            count += 1
            if p.lchild is not None:
                queue.append(p.lchild)
                next_last = p.lchild
            if p.rchild is not None:
                queue.append(p.rchild)
                next_last = p.rchild
            if p == now_last:
                width = max(width, count)
                count = 0
                now_last = next_last
        return width

    # 用hash表
    def BTWidth2(self, root):
        if root is None:
            return 0
        ht = {} # hash table
        ht[root] = 1
        que = [] # queue
        que.append(root)
        width = 0 # save max width
        count = 0 # save cur level nodes number
        cur_level = 1
        while len(que) != 0:
            cur = que.pop(0)
            node_level = ht[cur]
            if node_level == cur_level:
                count += 1
            else:
                width = max(width, count)
                count = 1
                cur_level += 1
            if cur.lchild is not None:
                que.append(cur.lchild)
                ht[cur.lchild] = cur_level + 1
            if cur.rchild is not None:
                que.append(cur.rchild)
                ht[cur.rchild] = cur_level + 1
        else:
            width = max(width, count)

        return width

# test
data_list = [1, 2, '#', 4, '#', '#', 3, 5, '#', '#', 6, '#', '#']
biTree = BiTree(data_list) # biTree -> BiTree class, iter
root = biTree.createBiTree() # root -> createBiTree()'s return
print(biTree.BTWidth1(root))
print(biTree.BTWidth2(root))
# output:
# 3
# 3

2. 二叉树的相关概念及其实现判断

2.1 判断一棵二叉树是不是搜索二叉树

1️⃣搜索二叉树:

对于每一棵子树来说,它的左树的节点比它小,右树都比它大

2️⃣判断一棵树是不是搜索二叉树:

中序遍历是依次升序就是搜索二叉树

📝递归方法:

# 判断搜索二叉树 -> 递归
def searchBT1(self, root):
    if root is None:
        return True
    if root.lchild is not None:
        left = self.GetRightMost(root.lchild) # 取左边子树的最后一点(中序遍历的角度)
        if not self.searchBT1(root.lchild) or left.key > root.key:
            return False
    if root.rchild is not None:
        right = self.GetLeftMost(root.rchild) #取右边子树的第一点(中序遍历的角度)
        if not self.searchBT1(root.rchild) or root.key > right.key:
            return False
    return True

def GetLeftMost(self, node):
    while node is not None and node.lchild is not None:
        node = node.lchild
    return node

def GetRightMost(self, node):
    while node is not None and node.rchild is not None:
        node = node.rchild
    return node

📝非递归方法:

# 判断搜索二叉树 -> 非递归
def searchBT2(self, root):
    if root is None: return True
    help = [] # save results
    stack = [] # save path
    while len(stack) != 0 or root is not None:
        if root is not None:
            stack.append(root)
            root = root.lchild
        else:
            root = stack.pop()
            help.append(root.key)
            root = root.rchild

    post = help.pop()
    while len(help) != 0:
        pre = help.pop()
        if pre > post: return False
        post = pre
    return True

📝测试结果:

# test
data_list = [1, 2, '#', 4, '#', '#', 3, 5, '#', '#', 6, '#', '#']
biTree = BiTree(data_list) # biTree -> BiTree class, iter
root = biTree.createBiTree() # root -> createBiTree()'s return
print(biTree.searchBT1(root))
print(biTree.searchBT2(root))
# output:
# False
# False

2.2 判断一棵二叉树是不是完全二叉树

1️⃣完全二叉树

​ 一棵深度为k的有n个结点的二叉树,对树中的结点按从上至下、从左到右的顺序进行编号,如果编号为i(1≤i≤n)的结点与满二叉树中编号为i的结点在二叉树中的位置相同,则这棵二叉树称为完全二叉树。

2️⃣判断完全二叉树

​ 宽度优先遍历

# 判断完全二叉树
def isCBT(self, root):
    if root is None: return True
    queue = []
    queue.append(root)
    leaf = False # leaf -> pre leaf that has left but no right child
    while len(queue) != 0:
        now = queue.pop(0)
        l, r = now.lchild, now.rchild
        if (l is None and r is not None) or \
                (leaf and (l is not None or r is not None)):
            return False
        if l is not None:
            queue.append(l)
        if r is not None:
            queue.append(r)
        if (l is None or r is None):
            leaf = True

    return True

# test
data_list = [1, 2, 4, 8, '#', '#', '#', 5, '#', '#', 3, 6, '#', '#', 7, '#', '#']
biTree = BiTree(data_list) # biTree -> BiTree class, iter
root = biTree.createBiTree() # root -> createBiTree()'s return
print(biTree.isCBT(root))
# True

代码中二叉树结构和创建,记录在python | 算法大神左神(左程云)算法课程 二叉树部分【上】

2.3 判断一棵二叉树是不是满二叉树

​ 1️⃣树的最大深度L和节点个数N之间有等量关系:$N=2^L-1$,满足,则是满二叉树。

​ 2️⃣宽度优先遍历,同时记录节点数和树的深度,最后判断上述等式是否成立。

#判断满二叉树 F-full
def isFBT(self, root):
    if root is None: return True
    queue = []
    queue.append(root)
    cur_last = root
    next_last = None
    level = 0 # save height of BiTree
    n = 0 # count number of node
    while len(queue) != 0:
        cur = queue.pop(0)
        n += 1
        if cur.lchild is not None:
            queue.append(cur.lchild)
            next_last = cur.lchild
        if cur.rchild is not None:
            queue.append(cur.rchild)
            next_last = cur.rchild
        if cur == cur_last:
            level += 1
            cur_last = next_last

    if 2 ** level - 1 == n:
        return True
    return False
    
# test
# data_list = [1, 2, 4, '#', '#', 5, '#', '#', 3, 6, '#', '#', 7, '#', '#']
# True

2.4 判断一棵二叉树是否是平衡二叉树

1️⃣平衡二叉树

​ 对于任何子树来说,其左子树和右子树的高度差不超过1

2️⃣判断平衡二叉树

​ 递归,分别判断左右子树,再判断整棵树,递归函数有两个返回值:是否是平衡二叉树以及树高

# 判断平衡二叉树 B-balanced
def isBBT(self, root):
    return self.process(root)[0]
def process(self, root):
    if root is None:
        return [True, 0]
    left = self.process(root.lchild)
    right = self.process(root.rchild)
    height = max(left[1], right[1])
    isBalanced = left[0] and right[0] and abs(left[1] - right[1]) < 2
    return [isBalanced, height]
    
# test
# data_list = [1, 2, 4, '#', '#', 5, '#', '#', 3, 6, '#', '#', 7, '#', '#']
# True

3. 二叉树问题实际训练

3.1 最低公共祖先

给定两个二叉树的节点node1和node2,找到他们的最低公共祖先节点

参考这里,解析很清楚

1️⃣ 设置一个 HashMap 保存节点与该节点的父节点(设根节点的父节点为本身),然后再用一个集合 set1 保存 node1 的全部祖先节点,对node2往上求祖先节点,看是否在 set1 中,若在,则这个节点即为最低公共祖先节点。

class BTNode():
    def __init__(self, key=None, lchild=None, rchild=None):
        self.key = key
        self.lchild = lchild
        self.rchild = rchild

class BiTree():
    def __init__(self, data_list):
        self.len = len(data_list)
        self.data = iter(data_list)

    def createBiTree(self, root=None):
        try:
            nextone = next(self.data)
            if nextone == '#':
                root = None
            else:
                root = BTNode(nextone)
                root.lchild = self.createBiTree(root.lchild)
                root.rchild = self.createBiTree(root.rchild)
        except Exception as e:
            print(e)
        return root

# Lowest Common Ancestor
def lca(head=None, node1=None, node2=None):
    """Here we assume that node1 and node2 must belong to the tree headed by head"""
    # Get a hash table of all nodes mapped to their parents
    father_map = {}
    father_map[head] = head
    process(head, father_map)
    # Get all ancestor node of node1, and put them in list1
    list1 = []
    cur = node1
    while(cur is not father_map[cur]):
        list1.append(cur)
        cur = father_map[cur]
    else:
        list1.append(cur)
    # Get ancestor of node2, and judge weather it is in list1 -> find the target ancestor
    cur = node2
    while(cur not in list1):
        cur = father_map[cur]

    return cur # this is the lowest common ancestor

def process(head, father_map):
    if head is None:
        return
    father_map[head.lchild] = head
    father_map[head.rchild] = head
    process(head.lchild, father_map)
    process(head.rchild, father_map)

2️⃣ 递归操作

  • 基本事件是当一棵子树的头节点为 null 或 为 node1 或 node2 时,就返回这个头节点。

  • 对左右子树作递归,获得左右子树的返回值。递归的返回值只有四种可能:null 、node1 、node2,两节点的最低公共祖先节点。

  • 当左子树返回值不空,右子树返回值不空(即两节点分别落于左右子树上),返回头节点(该头节点即为最低公共祖先节点)。

  • 当不满足左右子树返回值均不空这个条件时,若左子树返回值不空时返回该值(可能为node1 、node2,两节点的最低公共祖先节点),否则返回右子树的值(可能为null 、node1 、node2,两节点的最低公共祖先节点)。

    详细解析看这里

def lca2(head=None, node1=None, node2=None):
    if head is None or head == node1 or head == node2:
        return head
    left = lca2(head.lchild, node1, node2)
    right = lca2(head.rchild, node1, node2)
    if left is not None and right is not None:
        return head
    else:
        return left if (left is not None) else right

3.2 在二叉树中找到一个节点的后继节点

image

1️⃣ node有右子树时,其后继节点一定是右子树的中序遍历第一个节点

2️⃣ node没有右子树时,一路向上,找到某个节点是其父亲节点的左孩子,那么该父亲节点就是node的后继节点

def SuccessorNode(node):
    if node is None:
        return node
    if node.right is not None: # node有右子树
        return GetLeftMost(node.right) #寻找右子树的中序遍历第一个节点
    else: # node没有右子树
        parent = node.parent # 向祖先寻找后继节点
        while parent is not None and parent.left != node: # 循环找当前节点是其父节点的左子树
            node = parent
            parent = node.parent
        return parent

def GetLeftMost(node):
    while node is not None:
        node = node.left
    return node.parent

3.3 二叉树的序列化和反序列化

参考链接

# Definition for a binary tree node
class BTNode():
    def __init__(self, value=None, left=None, right=None):
        self.value = value
        self.left = left
        self.right = right

class Solution():

    def serialize(self, root):
        """
        Encodes a tree to a single string
        :param root: BTNode
        :return: str
        """
        if root is None:
            return 'null,'
        left_serialize = self.serialize(root.left)
        right_serialize = self.serialize(root.right)
        return str(root.value) + ',' + left_serialize + right_serialize

    def deserialize(self, data):
        """
        Decodes your encoded data to a binary tree
        :param data: str
        :return: BTNode
        """
        def dfs(queue): # dfs -> Deep First Search
            val = queue.popleft()
            if val == "null":
                return None
            node = BTNode(value=val)
            node.left = dfs(queue)
            node.right = dfs(queue)
            return node

        from collections import deque
        queue = deque(data.split(','))
        return dfs(queue)

3.4 折纸问题

image

分析折纸过程,发现所求是如下二叉树的中序遍历:

image

def PrintAllFolds(N):
    PrintProcess(1, N, True)

def PrintProcess(i, N, down=True):
    # i 是节点层数, N是一共的层数, down == true 代表凹,打印down, down == false 代表凸 打印up
    if i > N: return
    PrintProcess(i+1, N, True)
    print("down" if down else "up")
    PrintProcess(i+1, N, False)

posted @ 2022-09-03 11:17  万国码aaa  阅读(153)  评论(6编辑  收藏  举报