二叉树的下一个结点

二叉树的下一个结点

http://www.nowcoder.com/questionTerminal/9023a0c988684a53960365b889ceaf5e

题目的主要信息:
  • 题目给出我们一棵树的其中的某一个结点指针
  • 我们需要返回这棵树按照中序遍历的该节点的下一个顺序结点指针
  • 树的每个节点都有三个指针,指向左子节点、右子节点、父节点
举一反三:

学习完本题的思路你可以解决如下题目:

JZ54. 二叉搜索树的第k个节点

JZ68. 二叉搜索树的最近公共祖先

方法一:中序遍历递归(推荐使用)

知识点:二叉树递归

递归是一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。因此递归过程,最重要的就是查看能不能讲原本的问题分解为更小的子问题,这是使用递归的关键。

而二叉树的递归,则是将某个节点的左子树、右子树看成一颗完整的树,那么对于子树的访问或者操作就是对于原树的访问或者操作的子问题,因此可以自我调用函数不断进入子树。

思路:

我们首先要根据给定输入中的结点指针向父级进行迭代,直到找到该树的根节点;然后根据根节点进行中序遍历,当遍历到和给定树节点相同的节点时,下一个节点就是我们的目标返回节点

具体做法:

  • step 1:首先先根据当前给出的结点找到根节点
  • step 2:然后根节点调用中序遍历
  • step 3:将中序遍历结果存储下来
  • step 4:最终拿当前结点匹配是否有符合要求的下一个结点

Java实现代码:

import java.util.*;
public class Solution {
    ArrayList<TreeLinkNode> nodes = new ArrayList<>();
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        // 获取根节点
        TreeLinkNode root = pNode;
        while(root.next != null) root = root.next;
        
        // 中序遍历打造nodes
        InOrder(root);
        
        // 进行匹配
        int n = nodes.size();
        for(int i = 0; i < n - 1; i++) {
            TreeLinkNode cur = nodes.get(i);
            if(pNode == cur) {
                return nodes.get(i+1);
            }
        }
        return null;
    }
    
    // 中序遍历
    void InOrder(TreeLinkNode root) {
        if(root != null) {
            InOrder(root.left);
            nodes.add(root);
            InOrder(root.right);
        }
    }
}

C++实现代码:

class Solution {
public:
    vector<TreeLinkNode*> nodes;
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        TreeLinkNode* root = pNode;
        // 获取根节点
        while(root->next) root = root->next;    
        
        // 中序遍历用nodes储存所有节点指针
        InOrder(root);                          
        int n = nodes.size();
        
        for(int i = 0; i < n - 1; i++) {
            TreeLinkNode* cur = nodes[i];
            // 将结点进行匹配       
            if(pNode == cur) {
                // 如果有匹配到给出的结点,则下一个结点即返回结果                  
                return nodes[i+1];              
            }
        }
        // 否则如果没有下一个结点则返回NULL
        return NULL;                            
    }
    // 中序遍历
    void InOrder(TreeLinkNode* root) {          
        if(root == NULL) return;
        InOrder(root->left);
        nodes.push_back(root);
        InOrder(root->right);
    }
};

python实现代码:

class Solution:
    nodes = []
    
    def GetNext(self, pNode):
        # 查找根节点
        root = pNode
        while root.next:
            root = root.next
        
        # 中序遍历打造nodes
        self.InOrder(root)
        
        # 匹配节点
        for i in range(len(self.nodes) - 1):
            cur = self.nodes[i]
            if pNode == cur:
                return self.nodes[i+1]
        return None
        
    # 中序遍历
    def InOrder(self, root):
        if root == None:
            return
        self.InOrder(root.left)
        self.nodes.append(root)
        self.InOrder(root.right)


复杂度分析:

  • 时间复杂度:O(N)O(N),因为遍历了树中的所有节点
  • 空间复杂度:O(N)O(N),因为引入了存储所有节点的空间
方法二:分类直接寻找

思路:

直接寻找分为三种情况

  1. 如果给出的结点有右子节点,则最终要返回的下一个结点即右子树的最左下的结点
  2. 如果给出的结点无右子节点,且当前结点是其父节点的左子节点,则返回其父节点
  3. 如果给出的结点无右子节点,且当前结点是其父节点的右子节点,则先要沿着左上方父节点爬树,一直爬到当前结点是其父节点的左子节点为止,返回的就是这个父节点;或者没有满足上述情况的则返回为NULL

具体做法:

  • step 1:判断该节点是否符合思路中第一点,则一直找到右子树的左下节点为返回值
  • step 2:判断该节点是否符合思路中第二点,则返回当前节点的父亲节点
  • step 3:判断该节点是否符合思路中第三点,则迭代向上找父节点,直到迭代的当前节点是父节点的左孩子节点为止,返回该父节点;如果不满足上述情况返回NULL

图示:

alt

Java实现代码:

import java.util.*;
public class Solution {
    public TreeLinkNode GetNext(TreeLinkNode pNode) {
        // 情况一
        if(pNode.right != null) {
            TreeLinkNode rchild = pNode.right;
            // 一直找到右子树的最左下的结点为返回值
            while(rchild.left != null) rchild = rchild.left; 
            return rchild;
        }
        
        // 情况二
        if(pNode.next != null && pNode.next.left == pNode) {
            return pNode.next;
        }
        
        // 情况三
        if(pNode.next != null) {
            TreeLinkNode ppar = pNode.next;
            // 沿着左上一直爬树,爬到当前结点是其父节点的左自己结点为止
            while(ppar.next != null && ppar.next.right == ppar) ppar = ppar.next; 
            return ppar.next;
        }
        return null;
    }
}

C++实现代码:

class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode) {
        // 如果有右子树
        if(pNode->right) {    
            TreeLinkNode* rchild = pNode->right;
            // 一直找到右子树的最左下的结点为返回值
            while(rchild->left) rchild = rchild->left;    
            return rchild;
        }
        // 如果无右子树且当前结点是其父节点的左子结点
        if(pNode->next && pNode->next->left == pNode) {    
            // 返回当前结点的父节点
            return pNode->next;   
        }
        // 如果无右子树且当前结点是其父节点的右子节点
        if(pNode->next) {         
            TreeLinkNode* ppar = pNode->next;
            // 沿着左上一直爬树,爬到当前结点是其父节点的左自己结点为止
            while(ppar->next && ppar->next->right == ppar) ppar = ppar->next;    
            // 返回当前结点的父节点
            return ppar->next;    
        }
        return NULL;
    }
};

python实现代码:

class Solution:
    def GetNext(self, pNode):
        # 情况一
        if pNode.right:
            rchild = pNode.right
            # 一直找到右子树的最左下的结点为返回值
            while(rchild.left):                     
                rchild = rchild.left
            return rchild
        
        # 情况二
        # 如果无右子树且当前结点是其父节点的左子结点
        if pNode.next and pNode.next.left == pNode: 
            return pNode.next
        
        # 情况三
        if pNode.next:
            ppar = pNode.next
            # 沿着左上一直爬树,爬到当前结点是其父节点的左自己结点为止
            while(ppar.next and ppar.next.right == ppar): 
                ppar = ppar.next
            return ppar.next
        
        return None

复杂度分析:

  • 时间复杂度:O(N)O(N),最大代价是当树退化成一个只包含右子节点的链表,当给定节点是中序遍历最后一个节点时,会进入情况三的分析部分,在向左上方向一直迭代直到根节点,才会发现应该返回NULL,即无下一个节点,此时代价最大。
  • 空间复杂度:O(1)O(1),无额外空间的借用
全部评论
亲,这边建议把方法一中的函数名换成in_order呢
4 回复 分享
发布于 2021-02-04 15:08
JAVA代码实现 public class Solution { public TreeLinkNode GetNext(TreeLinkNode pNode) { { if (pNode==null) { return pNode; } // 属于[2 3 6]类 if (pNode.right!=null) { pNode = pNode.right; while (pNode.left!=null) { pNode = pNode.left; } return pNode; } // 属于 [1] 和 [4 5] while (pNode.next!=null) { TreeLinkNode root = pNode.next; if (root.left == pNode) { return root; } pNode = pNode.next; } // 属于[7] return null; } }}
1 回复 分享
发布于 2021-11-03 11:00
第一种方法编译不通过啊喂
1 回复 分享
发布于 2020-11-07 17:42
简明扼要一点吧;就两种情况:1)这个节点有右孩子,那下一个节点(根据中序遍历)肯定就是右孩子子树的最左边的叶节点;2)这个节点没有右孩子,那就往上走;如果这个节点是他父亲节点的左孩子,那就返回这个父亲节点;如果该节点是父亲节点的右孩子,那就一直往上走,直到找到那个满足是左孩子节点的父节点即可。
15 回复 分享
发布于 2021-08-05 21:38
我一开始也是想用方法二,但发现总是漏掉一些情况。所以说要是做笔试,最好就直接用方法一,递归遍历省时省力还不容易错。面试要是手撕这道,最好先跟hr说方法一,然后再表明:其实我还有一种更好的方法.....之后再尝试写方法二
13 回复 分享
发布于 2020-09-09 13:01
感觉题目描述不清晰,看了题解才知道题目是什么~~
4 回复 分享
发布于 2021-11-09 11:41
搞不懂题目意思
1 回复 分享
发布于 2022-08-22 16:55 湖北
题解的第二种方式代码运行报错,StringIndexOutOfBoundsException。
点赞 回复 分享
发布于 2024-01-04 14:37 黑龙江
你这题目写的什么💩
点赞 回复 分享
发布于 2023-05-28 16:39 福建
python代码 全右子节点也满足代码逻辑了 明显有问题
点赞 回复 分享
发布于 2023-01-30 15:10 北京
题目写的是个***啥啊
点赞 回复 分享
发布于 2022-03-12 18:08
还是没懂题目什么意思。。。
点赞 回复 分享
发布于 2022-01-23 23:20
应该是in_order吧 命名不太对哈哈
点赞 回复 分享
发布于 2021-07-20 16:24
我觉得时间复杂度最坏应该是O(logN),因为最坏情况也就是从根节点遍历到叶子节点
点赞 回复 分享
发布于 2021-06-29 09:44
来个java暴力版本的,需要的看过来: import java.util.*; public class Solution { public TreeLinkNode GetNext(TreeLinkNode pNode) { //1 Find root with pNode TreeLinkNode root=pNode; while(root.next!=null){ root=root.next; } //2 用列表记录中序遍历顺序 ArrayList<treelinknode> list = new ArrayList<>(); pre_order(root,list); //3 在list中查找pNode for(int i=0;i<list.size> list){ if(root == null) return; pre_order(root.left,list); list.add(root); pre_order(root.right,list); } }</list.size></treelinknode>
点赞 回复 分享
发布于 2021-04-09 22:42
原来有官方题解了,蒟蒻先赞一个
点赞 回复 分享
发布于 2020-06-27 13:51

相关推荐

07-07 12:25
门头沟学院 Java
程序员牛肉:你这个智邮公司做的就是那个乐山市税务系统的服务吗?
点赞 评论 收藏
分享
评论
97
5
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务