【剑指 の 精选】详解「二叉树中序遍历的下一个结点」两种解法 |Python 主题月

简介: 【剑指 の 精选】详解「二叉树中序遍历的下一个结点」两种解法 |Python 主题月

网络异常,图片无法展示
|


题目描述



这是「牛客网」上的「JZ 57 二叉树的下一个结点」,难度为「中等」。


Tag : 「剑指 Offer」、「二叉树」、「中序遍历」


给定一个二叉树其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。


注意,树中的结点不仅包含左右子结点,同时包含指向父结点的 next 指针。


下图为一棵有 个节点的二叉树。树中从父节点指向子节点的指针用实线表示,从子节点指向父节点的用虚线表示。


网络异常,图片无法展示
|


示例:


输入:{8,6,10,5,7,9,11},8
返回:9
解析:这个组装传入的子树根节点,其实就是整颗树,中序遍历{5,6,7,8,9,10,11},根节点8的下一个节点就是9,应该返回{9,10,11},后台只打印子树的下一个节点,所以只会打印9,如下图,其实都有指向左右孩子的指针,还有指向父节点的指针,下图没有画出来。
复制代码


网络异常,图片无法展示
|


输入描述:输入分为2段,第一段是整体的二叉树,第二段是给定二叉树节点的值,后台会将这2个参数组装为一个二叉树局部的子树传入到函数GetNext里面,用户得到的输入只有一个子树根节点。


返回值描述:返回传入的子树根节点的下一个节点,后台会打印输出这个节点。


示例1


输入:{8,6,10,5,7,9,11},8
返回值:9
复制代码


示例2


输入:{8,6,10,5,7,9,11},6
返回值:7
复制代码


示例3


输入:{1,2,#,#,3,#,4},4
返回值:1
复制代码


示例4


输入:{5},5
返回值:"null"
说明:不存在,后台打印"null" 
复制代码


要求:


  • 时间:1 s
  • 空间:64 M


朴素解法



一个朴素的做法是,根据题目对于 TreeLinkNode 的定义,利用 next 属性存储「当前节点的父节点」这一特点。


从入参节点 pNode 出发,不断利用 next 属性往上查找,直到找到整棵树的头节点,令头节点为 root


然后实现二叉树的「中序遍历」,将遍历过程中访问的节点存放到列表 list 中,之后再对 list 进行遍历,找到 pNode 所在的位置 idx,即可确定 pNode 是否存在「下一个节点」以及「下一节点」是哪一个。


网络异常,图片无法展示
|


Java 代码:


import java.util.*;
public class Solution {
    List<TreeLinkNode> list = new ArrayList<>();
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        // 根据传入的节点的 next 指针一直往上找,直到找到根节点 root
        TreeLinkNode root = pNode;
        while (root.next != null) root = root.next;
        // 对树进行一遍「中序遍历」,保存结果到 list 中
        dfs(root);
        // 从 list 中找传入节点 pNode 的位置 idx
        int n = list.size(), idx = -1;
        for (int i = 0; i < n; i++) {
            if (list.get(i).equals(pNode)) {
                idx = i;
                break;
            }
        }
        // 如果 idx 不为「中序遍历」的最后一个元素,那么说明存在下一个节点,从 list 查找并返回
        // 这里的 idx == -1 的判断属于防御性编程
        return idx == -1 || idx == n - 1 ? null : list.get(idx + 1);
    }
    void dfs(TreeLinkNode root) {
        if (root == null) return;
        dfs(root.left);
        list.add(root);
        dfs(root.right);
    }
}
复制代码


Python 3 代码:


class Solution:
    lt = []
    def GetNext(self, pNode):
        # 根据传入的节点的 next 指针一直往上找,直到找到根节点 root
        root = pNode
        while root.next is not None:
            root = root.next
        # 对树进行一遍「中序遍历」,保存结果到 list 中
        self.dfs(root)
        # 从 list 中找传入节点 pNode 的位置 idx
        n = len(self.lt)
        idx = -1
        for i in range(n):
            if self.lt[i] == pNode:
                idx = i
                break
        # 如果 idx 不为「中序遍历」的最后一个元素,那么说明存在下一个节点,从 list 查找并返回
        # 这里的 idx == -1 的判断属于防御性编程
        return None if idx == -1 or idx == n -1 else self.lt[idx + 1]
    def dfs(self, root):
        if root is None:
            return
        self.dfs(root.left)
        self.lt.append(root)
        self.dfs(root.right)
复制代码


  • 时间复杂度:找头节点 root 最多访问的节点数量不会超过树高 h;进行中序遍历的复杂度为 O(n);从中序遍历结果中找到 pNode 的下一节点的复杂度为 O(n)。整体复杂度为 O(n)
  • 空间复杂度:忽略递归带来的额外空间消耗。复杂度为 O(n)


进阶解法



另外一个“进阶”的做法是充分利用「二叉树的中序遍历」来实现的。


我们知道,二叉树「中序遍历」的遍历顺序为 「左-根-右」


可以根据传入节点 pNode 是否有「右儿子」,以及传入节点 pNode 是否为其「父节点」的「左儿子」来进行分情况讨论:


  • 传入节点 pNode 有「右儿子」:根据「中序遍历」的遍历顺序为 「左-根-右」,可以确定「下一个节点」必然为当前节点的「右子树」中「最靠左的节点」;
  • 传入节点 pNode 没有「右儿子」,这时候需要根据当前节点是否为其「父节点」的「左儿子」来进行分情况讨论:
  • 如果传入节点 pNode 本身是其「父节点」的「左儿子」,那么根据「中序遍历」的遍历顺序为为 「左-根-右」 可知,下一个节点正是该父节点,直接返回该节点即可;
  • 如果传入节点 pNode 本身是其「父节点」的「右儿子」,那么根据「中序遍历」的遍历顺序为为 「左-根-右」 可知,其父节点已经被遍历过了,我们需要递归找到符合 node.equals(node.next.left) 的节点作为答案返回,如果没有则说明当前节点是整颗二叉树最靠右的节点,这时候返回 null 即可。


代码:


public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        if (pNode.right != null) {
            // 如果当前节点有右儿子,下一节点为当前节点「右子树中最靠左的节点」
            pNode = pNode.right;
            while (pNode.left != null) pNode = pNode.left;
            return pNode;
        } else {
            // 如果当前节点没有右儿子,则「往上找父节点」,直到出现满足「其左儿子是当前节点」的父节点
            while (pNode.next != null) {
                if (pNode.equals(pNode.next.left)) return pNode.next;
                pNode = pNode.next;
            }
        }
        return null;
    }
}
复制代码


Python 3 代码:


class Solution:
    def GetNext(self, pNode):
        if pNode.right is not None:
            # 如果当前节点有右儿子,下一节点为当前节点「右子树中最靠左的节点」
            pNode = pNode.right
            while pNode.left is not None:
                pNode = pNode.left
            return pNode
        else:
            #  如果当前节点没有右儿子,则「往上找父节点」,直到出现满足「其左儿子是当前节点」的父节点
            while pNode.next is not None:
                if pNode == pNode.next.left:
                    return pNode.next
                pNode = pNode.next
        return None
复制代码


  • 时间复杂度:O(n)
  • 空间复杂度:O(1)


最后



这是我们「剑指 の 精选」系列文章的第 No.57 篇,系列开始于 2021/07/01。


该系列会将牛客网「剑指 Offer」中比较经典而又不过时的题目都讲一遍。


在提供追求「证明」&「思路」的同时,提供最为简洁的代码。


欢迎关注,交个朋友 (`・ω・´)

相关文章
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - III. 从上到下打印二叉树 III
本文介绍了两种Python实现方法,用于按照之字形顺序打印二叉树的层次遍历结果,实现了在奇数层正序、偶数层反序打印节点的功能。
55 6
|
3天前
|
算法 定位技术 Python
震惊!Python 图结构竟然可以这样玩?DFS&BFS 遍历技巧大公开
在 Python 编程中,图是一种重要的数据结构,而深度优先搜索(DFS)和广度优先搜索(BFS)是遍历图的两种关键算法。本文将通过定义图的数据结构、实现 DFS 和 BFS 算法,并通过具体示例展示其应用,帮助读者深入理解这两种算法。DFS 适用于寻找路径和检查图连通性,而 BFS 适用于寻找最短路径。掌握这些技巧,可以更高效地解决与图相关的复杂问题。
10 2
|
7天前
|
Python
不容错过!Python中图的精妙表示与高效遍历策略,提升你的编程艺术感
本文介绍了Python中图的表示方法及遍历策略。图可通过邻接表或邻接矩阵表示,前者节省空间适合稀疏图,后者便于检查连接但占用更多空间。文章详细展示了邻接表和邻接矩阵的实现,并讲解了深度优先搜索(DFS)和广度优先搜索(BFS)的遍历方法,帮助读者掌握图的基本操作和应用技巧。
23 4
|
8天前
|
算法 Python
Python图论探索:从理论到实践,DFS与BFS遍历技巧让你秒变技术大牛
图论在数据结构与算法中占据重要地位,应用广泛。本文通过Python代码实现深度优先搜索(DFS)和广度优先搜索(BFS),帮助读者掌握图的遍历技巧。DFS沿路径深入搜索,BFS逐层向外扩展,两者各具优势。掌握这些技巧,为解决复杂问题打下坚实基础。
18 2
|
2月前
|
数据处理 Python
python遍历文件夹所有文件按什么排序
python遍历文件夹所有文件按什么排序
|
2月前
|
数据处理 Python
Python遍历文件夹所有文件并按指定排序
Python遍历文件夹所有文件并按指定排序
|
3月前
|
索引 Python
【Leetcode刷题Python】从列表list中创建一颗二叉树
本文介绍了如何使用Python递归函数从列表中创建二叉树,其中每个节点的左右子节点索引分别是当前节点索引的2倍加1和2倍加2。
55 7
|
2月前
|
存储 消息中间件 大数据
Python里for循环要遍历的数据很多很大怎么办?
遇到大数据量问题时,重要的是确定最优解决方案,这取决于数据的来源、性质以及所需的处理方式。分析数据传输、存储与处理的瓶颈是提升性能的关键。通过结合上述的技巧和方法,可以在内存和性能方面找到合适的平衡点来处理大规模数据集。
103 0
|
3月前
|
算法 Python
【Leetcode刷题Python】剑指 Offer 33. 二叉搜索树的后序遍历序列
本文提供了一种Python算法,用以判断给定整数数组是否为某二叉搜索树的后序遍历结果,通过识别根节点并递归验证左右子树的值是否满足二叉搜索树的性质。
22 3
|
3月前
|
Python
【Leetcode刷题Python】剑指 Offer 32 - II. 从上到下打印二叉树 II
本文提供了一种Python实现方法,用于层次遍历二叉树并按层打印结果,每层节点按从左到右的顺序排列,每层打印到一行。
37 3