五月集训(第18天)—树

1. 2236. 判断根结点是否等于子结点之和

    思路:
        超级大水题

bool checkTree(TreeNode* root) {
    if (root->left->val + root->right->val == root->val) return true;
    return false;
}

2. 面试题 04.10. 检查子树

    思路1:
        分类讨论所有情况,递归求解。

/**
 * 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:
    bool compare(TreeNode *t1, TreeNode *t2) {
        // t1和t2不同则返回false
        if (t1 != NULL && t2 == NULL) return false;
        if (t1 == NULL && t2 != NULL) return false;
        // t1和t2相同则返回true
        if (t1 == NULL && t2 == NULL) return true;
        if (t1->val == t2->val) {   /* t1与t2相同,继续比较左右子树是否相同 */
            return compare(t1->left, t2->left) && compare(t1->right, t2->right);
        } else return false;    /* 出现不同则t2不是t1的子树 */
    }

    bool checkSubTree(TreeNode* t1, TreeNode* t2) {
        if (t1 == NULL && t2 == NULL) return true;  /* 两棵空树 */
        if (t1 != NULL && t2 == NULL) return true;  /* t1不空,t2空,t2必为t1子树 */
        if (t1 == NULL && t2 != NULL) return false; /* t1空,t2不空,t2一定不是t1子树 */
        // t1等于t2,t2是t1左边子树,t2是t1右边子树,满足其一即可
        return compare(t1, t2) || checkSubTree(t1->left, t2) || checkSubTree(t1->right, t2);
    }
};

    思路2:
        对树及其左右子树分别建立hash映射,根据hash值判断。
        注意:递归建立hash的过程中,由于数值的累积,会导致爆int,可以先开long long强转一下,然后转回int。如果觉得强转int有问题,可以直接使用一个hash函数,每次调用时都重新递归的计算hash值,不过复杂度会高很多(因为多次遍历二叉树)。

/**
 * 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:
    void cal_hash(TreeNode *root) {
        if (root == nullptr) return ;

        // 递归的将左右子树建立hash映射
        if (root->left) cal_hash(root->left);
        if (root->right) cal_hash(root->right);

        // 建立映射的方法
        long long l = root->left ? root->left->val : 32;
        long long r = root->right ? root->right->val : 33;
        root->val = (int)((long long)root->val + l * 31 + r * 131);
    }

    bool find_child(TreeNode *root, int target) {
        if (root == nullptr) return false;
        if (root->val == target) return true;   /* 找到目标结点 */
        
        // 在左右子树中寻找目标结点
        return find_child(root->left, target) || find_child(root->right, target);
    }

    bool checkSubTree(TreeNode* t1, TreeNode* t2) {
        if (t2 == nullptr) return true;
        
        // 对以某个结点做为根节点的子树建立hash映射,将hash值直接存入结点的val中
        cal_hash(t1);
        cal_hash(t2);

        // 当t2与t1相同,或为其左右子树时,满足条件
        return (t1->val == t2->val) || find_child(t1->left, t2->val) || find_child(t1->right, t2->val);
    }
};

3. 面试题 04.06. 后继者

    思路:
        根据题目提示,使用树的中序遍历寻找目标点的下一个点。遇到目标点时进行标记,中序遍历到其后一个结点时返回结果。

/**
 * 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 {
        int flag;
        TreeNode *ret;
public:
    void dfs(TreeNode *root, TreeNode *P) {
        if (root == NULL) return ;

        dfs(root->left, P);

        if (flag && ret == NULL) { /* 返回目标点的下一个点 */
            ret = root;
            return ;
        }

        if (root == P) { /* 找到目标点 */
            flag = 1;
        }

        dfs(root->right, P);
    }

    TreeNode* inorderSuccessor(TreeNode* root, TreeNode* p) {
        flag = false;
        ret = NULL;
        dfs(root, p);
        return ret;
    }
};

4. 1110. 删点成林

    思路:
        根据题目中结点的标号方式可知应该使用先序遍历,为了保证树的先序遍历能够正常执行,需要等到每个结点的左右子树遍历之后再删除结点。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    int hash[1010];
    vector<TreeNode*> ret;
public:
    void dfs(TreeNode *parent, int isleft, TreeNode *root) {
        if (root == nullptr) return ;

        // 先序遍历
        dfs(root, true, root->left);
        dfs(root, false, root->right);

        // 遍历之后完成删除操作,否则无法完成遍历
        if (hash[root->val] == 1) {
            if (parent) {   /* 将父节点指向nullptr,完成删除 */
                if (isleft) {
                    parent->left = nullptr;
                } else {
                    parent->right = nullptr;
                }
            }
            // 删除后,将子树加入结果集合
            if (root->left) ret.push_back(root->left);
            if (root->right) ret.push_back(root->right);
        }
    }

    vector<TreeNode*> delNodes(TreeNode* root, vector<int>& to_delete) {
        if (root == nullptr) return {};
        int n = to_delete.size();
        int i;
        // 初始化容器
        ret.clear();
        memset(hash, 0, sizeof(hash));
        for (i = 0; i < n; i++) {
            hash[to_delete[i]] = 1;
        }
        
        // ret中添加删除后剩下的每棵子树
        dfs(nullptr, false, root);
        
        // 单独考虑root结点
        if (hash[root->val] == 0) ret.push_back(root);  /* 根节点未被删除,则加入ret */
        return ret;
    }
};

posted @ 2022-05-18 07:30  番茄元  阅读(26)  评论(0)    收藏  举报