day11-数据结构力扣

最近实验室的事情有点多,天天忙着跑图,我没招了,尽量找时间写

今天开了一个新的,二叉树

二叉树理论基础

定义

二叉树是一种树形数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。二叉树可以为空(不包含任何节点),或由一个根节点和两棵互不相交的子树(左子树和右子树)构成,子树本身也是二叉树。


种类

满二叉树

所有非叶子节点都有两个子节点,且所有叶子节点位于同一层级。若树的高度为 ,则节点总数为

完全二叉树

除最后一层外,其他层节点数达到最大值,且最后一层节点从左到右连续排列。常用于堆的实现。

二叉搜索树(BST)

左子树所有节点的值均小于根节点,右子树所有节点的值均大于根节点。中序遍历结果是有序序列。

平衡二叉树

任意节点的左右子树高度差不超过 1 ,例如 AVL 树或红黑树,保证操作效率为

退化二叉树

每个节点只有一个子节点,退化为链表结构,操作效率降至


存储方式

链式存储

通过节点对象动态分配内存,每个节点包含数据域和左右指针域。

python 复制代码
class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

顺序存储(数组)

按层级顺序将节点存储在数组中,下标为 的节点的左子节点下标为 ,右子节点为 。适合完全二叉树,节省指针空间。

递归遍历

深度优先遍历(DFS)

  • 前序遍历:根 → 左 → 右

  • 中序遍历:左 → 根 → 右(二叉搜索树中结果为有序序列)

  • 后序遍历:左 → 右 → 根

  • 有一个记忆小妙招就是:根在前,就是前序遍历;根在中,就是中序遍历;根在后,就是后续遍历。剩下两个位置就是先左后右

代码实例(前序遍历)

python 复制代码
def preorder(root):
    if not root:
        return []
    return [root.val] + preorder(root.left) + preorder(root.right)
 

这个时候可以刷力扣上面的题

提交

前序遍历

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def preorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        return [root.val]+self.preorderTraversal(root.left)+self.preorderTraversal(root.right)

后序遍历

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def postorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        return self.postorderTraversal(root.left)+self.postorderTraversal(root.right)+[root.val]

中序遍历

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        return self.inorderTraversal(root.left)+[root.val]+self.inorderTraversal(root.right)

这里需要注意的就是:

1.返回的是列表[],包括后面root.val都是加了方括号

2.前面self.preorderTraversal,就是需要加一个self.

非递归实现(使用栈)

python 复制代码
def preorder_stack(root):
    stack, res = [root], []
    while stack:
        node = stack.pop()
        if node:
            res.append(node.val)
            stack.append(node.right)  # 右子节点先入栈
            stack.append(node.left)
    return res

感觉递归就是有点难理解具体的过程,但是代码比较简洁

非递归相对来说可读性高一点,但是代码写的多一点。

但是我之前写遇到比较多的还是递归的这种写法

层序遍历

广度优先遍历(BFS)

按层级从上到下、从左到右遍历,通常使用队列实现。

思路

  • 先判断树空不空,空就直接返回空。

  • 用一个队列,从根节点开始,一层一层 "排队" 遍历。

  • 关键:每次先记住当前队列有多少个节点 = 这一层有多少个节点

  • 循环这么多次,把这一层所有节点取出来,存到结果里。

  • 每取出一个节点,就把它的左孩子、右孩子重新放进队列(下一层)。

  • 一层走完,把这一层结果存起来,继续下一层,直到队列为空。

下面是一个比较经典的层序遍历的实现方式

python 复制代码
# 利用长度法实现二叉树的【层序遍历】(广度优先 BFS)
# 作用:按一层一层的顺序,把二叉树节点的值输出为二维列表
# 每一层对应结果里的一个子列表

# 二叉树节点定义
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val       # 节点值
#         self.left = left     # 左子节点
#         self.right = right   # 右子节点

import collections

class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        # 边界判断:如果根节点为空,直接返回空列表
        if not root:
            return []
        
        # 初始化队列:把根节点放进去,队列用于 BFS 按层遍历
        # collections.deque 是双端队列,popleft() 效率更高
        queue = collections.deque([root])
        
        # 最终结果列表:存储每一层的节点值(二维列表)
        result = []
        
        # 只要队列不为空,就说明还有节点没遍历完
        while queue:
            # 临时列表:存储【当前这一层】的所有节点值
            level = []
            
            # 核心:先记录当前队列的长度 = 当前层的节点个数
            # 固定长度,保证 for 循环只遍历【本层】的节点,不会混入下一层
            for _ in range(len(queue)):
                # 从队列头部取出一个节点(先入先出)
                cur = queue.popleft()
                
                # 把当前节点的值加入本层列表
                level.append(cur.val)
                
                # 如果当前节点有左孩子,加入队列(下一层)
                if cur.left:
                    queue.append(cur.left)
                
                # 如果当前节点有右孩子,加入队列(下一层)
                if cur.right:
                    queue.append(cur.right)
            
            # 本层遍历完毕,把本层结果加入最终总结果
            result.append(level)

        return result

据说学完了层序遍历,可以一下打10个题

提交

**102.**二叉树的层序遍历

102. 二叉树的层序遍历 - 力扣(LeetCode)

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        queue=collections.deque([root])
        res=[]
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(level)
        return res

在 queue=collections.deque([root])不要忘记了root

**107.**二叉树的层次遍历 II

107. 二叉树的层序遍历 II - 力扣(LeetCode)

思路:我能不能直接先从上到下遍历,然后在返回结果的时候把列表的顺序颠倒一下

好的,提交通过,在上面基础上改了一行代码

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def levelOrderBottom(self, root: Optional[TreeNode]) -> List[List[int]]:
        if not root:
            return []
        queue=collections.deque([root])
        res=[]
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(level)
        return res[::-1]

**199.**二叉树的右视图

199. 二叉树的右视图 - 力扣(LeetCode)

思路:也就是说,我需要这一层的最右边那个元素,然后把每层最右边的放到一起

提交通过,也是只改了一行代码 res.append(level[-1])

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        queue=collections.deque([root])
        res=[]
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(level[-1])
        return res

637.二叉树的层平均值

637. 二叉树的层平均值 - 力扣(LeetCode)

思路:平均值,那我要统计每层的数量,每层的总和,要用总和除以每层的数量。

看起来简单,其实需要注意的地方不少

1.我刚开始直接在后面加res.append(sum1/len(queue)),但是这个时候在for循环之后,len(queue)已经不是本层的长度,而是存储的下一层的数据。所以我们需要再for循环之前用一个变量获取到本层的长度

2.sum1=0的位置,因为是每层的平均值,每层都需要初始化,所以是在while循环里面

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def averageOfLevels(self, root: Optional[TreeNode]) -> List[float]:
        if not root:
            return []
        queue=collections.deque([root])
        res=[]
        while queue:
            level=[]
            sum1=0
            count=len(queue)
            for _ in range(len(queue)):
                cur=queue.popleft()
                sum1+=cur.val
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(sum1/count)
        return res

429.N叉树的层序遍历

题目链接429. N 叉树的层序遍历 - 力扣(LeetCode)

思路:就是这里写下一层queue不能直接判断左右孩子,而是判断他有没有孩子,先试一下

但是要怎么遍历他的每一个孩子?

原来这里是,判断有无孩子,然后extend直接把这个节点的孩子全部加进来

python 复制代码
"""
# Definition for a Node.
class Node:
    def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
        self.val = val
        self.children = children
"""
import collections
class Solution:
    def levelOrder(self, root: 'Node') -> List[List[int]]:
        if not root:
            return []
        queue=collections.deque([root])
        res=[]
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                # 若当前节点存在子节点
                if cur.children:
                    # 将所有子节点加入队列,作为下一层待遍历节点
                    queue.extend(cur.children)
            res.append(level)
        return res

515.在每个树行中找最大值

题目链接515. 在每个树行中找最大值 - 力扣(LeetCode)

思路:把每层元素存到一个数组之后,找出这个数组的最大值,加入结果数组

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def largestValues(self, root: Optional[TreeNode]) -> List[int]:
        if not root:
            return []
        queue=collections.deque([root])
        res=[]
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            res.append(max(level))
        return res

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

116. 填充每个节点的下一个右侧节点指针 - 力扣(LeetCode)

直接加#,结果他把后面的括号给注释掉了。我刚开始完全没怎么看题,只看输出,就在那写。

不是直接在数组每行后面加#,是要对每行的next做出修改

python 复制代码
"""
# Definition for a Node.
class Node:
    def __init__(self, val: int = 0, left: 'Node' = None, right: 'Node' = None, next: 'Node' = None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next
"""
import collections
class Solution:
    def connect(self, root: 'Optional[Node]') -> 'Optional[Node]':
        if not root:
            return root
        
        q = collections.deque([root])
        
        while q:
            level_size = len(q)
            for i in range(level_size):
                node = q.popleft()
                # 不是本层最后一个,next 指向右边节点
                if i < level_size - 1:
                    node.next = q[0]
                # 加入孩子
                if node.left:
                    q.append(node.left)
                if node.right:
                    q.append(node.right)
                    
        return root

q[0]的理解

假设这一层有 3 个节点:A → B → C队列一开始是:[A, B, C]

第一步:处理 A

  • 执行 node = q.popleft()
  • 队列变成:[B, C]
  • 此时 A 的右边就是 B
  • 所以 A.next = q[0] → 就是 B

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

题目链接117. 填充每个节点的下一个右侧节点指针 II - 力扣(LeetCode)

上道题一样的代码,提交也能通过。

104.二叉树的最大深度

题目链接104. 二叉树的最大深度 - 力扣(LeetCode)

思路:题目描述就是数最多有几层,我们平时不就是一层一层遍历的,那while循环就是每一层,那我让count在while循环里面数就行了。

注意一下返回值是整数,所以之前判断根为空的时候应该返回0,而不是空数组。

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def maxDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        queue=collections.deque([root])
        count=0
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
            count+=1
        return count

111.二叉树的最小深度

题目链接111. 二叉树的最小深度 - 力扣(LeetCode)

思路:如果一个节点,既没有左孩子又没有右孩子,那就可以在while中停止计数。

其实我刚开始想用break停止计数,但是break只跳出了一层循环,但是我要的是,遇到没有孩子的直接返回结果,这里是在循环里面直接return count

至于count的初始值为什么是1,我刚开始给的0,然后结果运行都少了1,我就直接加了1

我去搜了一下:最小深度从根节点开始算,根节点就是第 1 层,所以 count 初始 = 1。

python 复制代码
# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
import collections
class Solution:
    def minDepth(self, root: Optional[TreeNode]) -> int:
        if not root:
            return 0
        queue=collections.deque([root])
        count=1
        while queue:
            level=[]
            for _ in range(len(queue)):
                cur=queue.popleft()
                level.append(cur.val)
                if cur.left:
                    queue.append(cur.left)
                if cur.right:
                    queue.append(cur.right)
                if not cur.right and not cur.left:
                    return count
            count+=1

终于写完这一小节的题了,没有力气,也没有手段

相关推荐
月落归舟2 小时前
Lambda + Arrays---小练习
数据结构·算法
SilentSlot2 小时前
[数据结构]B树的基本定义和操作
数据结构·b树·前端框架
2601_955354462 小时前
seo臻系统和百度seo有什么区别
算法
君义_noip2 小时前
信息学奥赛一本通 1487:【例 2】北极通讯网络
算法·图论·信息学奥赛·csp-s
会编程的土豆3 小时前
【leetcode hot 100】二叉树二叉树
数据结构·算法·leetcode
一直都在5723 小时前
B树和B+树详解
数据结构·b树
XiYang-DING3 小时前
【LeetCode】203. 移除链表元素(Remove Linked List Elements)
算法·leetcode·链表
墨神谕3 小时前
希尔排序详解
数据结构·算法·排序算法
胡楚昊3 小时前
Polar PWN (4)
linux·运维·算法