《程序员面试金典(第6版)》面试题 04.06. 后继者

题目描述

设计一个算法,找出二叉搜索树中指定节点的“下一个”节点(也即中序后继)。

如果指定节点没有对应的“下一个”节点,则返回null。

示例 1:
输入: root = [2,1,3], p = 1

  2
 / \
1   3

输出: 2

示例 2:
输入: root = [5,3,6,2,4,null,null,1], p = 6

      5
     / \
    3   6
   / \
  2   4
 /   
1

输出: null

解题思路与代码

因为中序遍历二叉搜索树后会得到一个有序数组,题目让我找出指定节点的“下一个”节点,也就是让我返回指定节点在有序数组中对应元素的下一个的元素节点。

中序遍历 + 设置前驱节点与目标节点法

我们首先先设置一个前驱节点,一个收集目标节点,都先置为nullptr。

然后另写一个中序遍历的递归函数,它的返回值是void。如果节点为空直接返回。然后就是先遍历左子树,之后在中间节点处写找到这个节点的逻辑,最后遍历右子树。

注意:在中间节点的逻辑判断出需要注意的一点是:因为pre会等于2次p,所以目标节点的值可能会被覆盖掉,所以要加上一个锁。只能给目标节点赋一次值。

具体的代码实现如下:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* pre = nullptr;
    TreeNode* ret = nullptr;
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        if(root == nullptr) return nullptr;
        findNode(root,p);
        return ret;
    }

    void findNode(TreeNode* root,TreeNode*p){
        if(root == nullptr) return;
        findNode(root->left,p);
        if(pre!= nullptr && pre == p) {
            if(ret == nullptr){
                ret = root;
            }
            return;
        }
        pre = root;
        findNode(root->right,p);
    }
};

《程序员面试金典(第6版)》面试题 04.06. 后继者_第1张图片

复杂度分析:

时间复杂度:O(n),n为节点的个数。我们需要遍历整棵树,即使找到目标节点还是要继续遍历下去,所以时间复杂度是O(n)
空间复杂度:O(h),h为二叉树的高度,我们递归产生的栈为h,所以空间复杂度为O(h),最坏情况下,二叉树为链表,那就是O(n),n为二叉树的节点个数。

方法二,利用二叉搜索树的性质,直接在节点上进行操作

刚刚我们提到过了,二叉树的性质是什么。我们将二叉树画在纸上。我们可以发现,p节点后序节点只有可能有两种情况,一种出现在p节点的右子树中。如果右子树没有左叶子节点,那右子树节点就是p的后序节点,否则就是p的右子树的左叶子节点。第二种情况,p节点的后序节点就是p节点的父节点,p节点是p后续节点的左子节点。

具体实现请看代码:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* findNode;
    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {

        //第一种情况,若p的右子节点不为空,我们首先要找p右子树的左叶子节点。这个就是p的中序后继节点。若没有左子树则直接返回p的右子节点。
        if(p->right != nullptr){
            findNode = p->right;
            while(findNode->left != nullptr) 
                findNode = findNode->left;
            return findNode;
        }

        //第二种情况,p的中序后继节点是它的父节点。
        TreeNode* father = root;
        while(father != nullptr){
            if(father->val > p->val){
                findNode = father;
                father = father->left;
            }else{
                father = father->right;
            }
        }
        return findNode;
    }
};

《程序员面试金典(第6版)》面试题 04.06. 后继者_第2张图片

复杂度分析

对于该算法的时间复杂度和常数,取决于树的形态,最好情况下它可以在O(1)或O(log n )时间内返回结果,最坏情况下,它将沿左边链迭代p步,然后向上跳遍历节点的祖先直到找到父节点的有效后继元素,需要O(n)时间。

而空间复杂度为 O(1),没有使用额外的动态分配内存。

总结

这道题也不算是太难,需要你去熟悉二叉搜索树的性质

你可能感兴趣的:(算法题解析与个人做题技巧总结,#,面试,算法,数据结构)