codetop标签树刷题(三)!!暴打面试官!!!!

1.子结构判断

给定两棵二叉树 tree1 和 tree2,判断 tree2 是否以 tree1 的某个节点为根的子树具有 相同的结构和节点值
注意,空树 不会是以 tree1 的某个节点为根的子树具有 相同的结构和节点值
在这里插入图片描述
空树是任何树的子结构

class Solution {
     
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A == nullptr || B == nullptr) return false;

        // 对于树A中的一个节点,如果A->val == B->val, 则A可以作为更节点去尝试匹配
        if(A->val == B->val && compareTree(A, B)) return true;
        return isSubStructure(A->left, B) || isSubStructure(A->right, B);
    }
    bool compareTree(TreeNode* rootA, TreeNode* rootB){
        if(rootB == nullptr) return true;
        if(rootA == nullptr && rootB != nullptr) return false;
        if(rootA->val != rootB->val) return false;

        return compareTree(rootA->left, rootB->left) && compareTree(rootA->right, rootB->right);
    }
};

2.寻找重复的子树

给你一棵二叉树的根节点root,返回所有重复的子树。
对于同一类的重复子树,你只需要返回其中任意 一棵 的根结点即可。
如果两棵树具有 相同的结构 和 相同的结点值 ,则认为二者是 重复 的。
在这里插入图片描述
将二叉树序列化的形式,建立哈希表,统计每次出现的次数,添加到结果集当中

class Solution {
public:
    vector<TreeNode*> findDuplicateSubtrees(TreeNode* root) {
    	vector<TreeNode*> res;
    	unordered_map<string, int> mp;
    	helper(root, res, mp);
    	return res;
    }
    string helper(TreeNode* root, vector<TreeNode*>& res, unordered_map<string, int>& mp){
    	string str;
    	if(!root) return "#";
    	str = to_string(root->val) + ' ' + helper(root->left,result,mp) + ' '+helper(root->right,result,mp);
    	

3.相同的树

编写一个函数,用来检验两棵树是否相同。

class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
    	if(p == nullptr && q == nullptr) return true;
    	if(p == nullptr || q == nullptr) return false;
    	if(p->val != q->val) return false;
    	return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }
};

4.平衡二叉树

给一个二叉树,判断他是不是平衡的

左右子树高度差不超过1就行
计算一次最大深度,计算的过程中在后序位置判断二叉树是否平衡

class Solution {
private:
    bool isBalancedTree = true;
    int maxDepth(TreeNode* root){
        if(root == nullptr) return 0;
        int leftMaxDepth = maxDepth(root->left);
        int rightMaxDepth = maxDepth(root->right);

        if(abs(rightMaxDepth - leftMaxDepth) > 1) isBalancedTree = false;

        return 1 + max(leftMaxDepth, rightMaxDepth);
    }
public:
    bool isBalanced(TreeNode* root) {
        maxDepth(root);
        return isBalancedTree;
    }
};

5.二叉树展开为链表

在这里插入图片描述

class Solution {
public:
    void flatten(TreeNode* root) {
        if(root == nullptr) return;
        
        flatten(root->left);
        flatten(root->right);

        //后序位置
        //1.左右子树已经被拉平成一条链表
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        
        //2.将左子树作为右子树
        root->left = nullptr;
        root->right = left;

        //3.将原先的右子树接到当前右子树的末端
        TreeNode* p = root;
        while(p->right != nullptr) p = p->right;
        p->right = right;
    }
};

6.将二叉搜索树转化为排序的双向链表

将一个 二叉搜索树 就地转化为一个 已排序的双向循环链表
对于双向循环列表,你可以将左右孩子指针作为双向循环链表的前驱和后继指针,第一个节点的前驱是最后一个节点,最后一个节点的后继是第一个节点
特别地,我们希望可以 就地 完成转换操作。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继。还需要返回链表中最小元素的指针
在这里插入图片描述
中序线索化的变型

  1. 使用队列,将二叉搜索树通过中序遍历的方式,依次将节点放入队列中。由于二叉搜索树的特性,通过中序遍历得到的序列是递增的。因此,放入队列中的节点顺序就是转换后双向链表的顺序。
  2. 从队列中弹出节点,将弹出的节点与前一个节点(pre)进行连接。具体的操作是:将当前节点(cur)的左子节点指针指向前一个节点(pre),将前一个节点的右子节点指针指向当前节点。然后,更新前一个节点为当前节点。
  3. 当队列为空时,说明所有节点都已经处理完毕。此时,需要将头节点和尾节点进行连接,形成一个环形双向链表。
  4. 返回头节点。
    双向链表需要定义两个变量cur和pre,关系如下
    在这里插入图片描述
class Solution {
public:
    TreeNode* treeToDoublyList(TreeNode* root) {
        if (!root) return nullptr;

        queue<Node*> queue;
        inorderTraversal(root, queue); // Step 1: In-order traversal to fill the queue

        Node* head = nullptr;
        Node* pre = nullptr;

        // Step 2: Re-link nodes in double linked list style
        while (!queue.empty()) {
            Node* cur = queue.front();
            queue.pop();
            if (!head) {
                head = cur; // the smallest element becomes head
            }
            if (pre) {
                pre->right = cur;
                cur->left = pre;
            }
            pre = cur; // update pre to current
        }

        // Step 3: Make the list circular
        pre->right = head;
        head->left = pre;

        return head;
    }

private:
    void inorderTraversal(Node* node, queue<Node*>& queue) {
        if (!node) return;
        inorderTraversal(node->left, queue);
        queue.push(node);
        inorderTraversal(node->right, queue);
    }
};

7.验证二叉搜索树

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

  • 节点的左子树只包含 小于 当前节点的数。
  • 节点的右子树只包含 大于 当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

初学者做这题很容易有误区:BST 不是左小右大么,那我只要检查 root.val > root.left.val 且 root.val < root.right.val 不就行了?
因为 BST 左小右大的特性是指 root.val 要比左子树的所有节点都更大,要比右子树的所有节点都小,你只检查左右两个子节点当然是不够的。
正确解法是通过使用辅助函数,增加函数参数列表,在参数中携带额外信息,将这种约束传递给子树的所有节点,这也是二叉搜索树算法的一个小技巧吧。

class Solution {
public:
    bool isValidBST(TreeNode* root) {   
        return checkBST(root, nullptr, nullptr);
    }
    bool checkBST(TreeNode* root, TreeNode* min, TreeNode* max){
        if(root == nullptr) return true;
        if(min != nullptr && root->val <= min->val) return false;
        if(max != nullptr && root->val >= max->val) return false;
        return checkBST(root->left, min, root) && checkBST(root->right, root, max); 
    }
};

8.二叉树的完全性检验

给你一棵二叉树的根节点root,请你判断这棵树是否是一棵完全二叉树。
在这里插入图片描述
条件:

  • 除了最后一层,每一层都被完全填满
  • 最后一层中的所有节点都尽可能靠左。
    在这里插入图片描述
    如果按照 BFS 层序遍历的方式遍历完全二叉树,队列最后留下的应该都是空指针
class Solution {
public:
    bool endNull = false;
    bool isCompleteTree(TreeNode* root) {
        //BFS检验
        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty()){
            int size = q.size();
            for(int i = 0; i < size; i ++){
                TreeNode* cur = q.front();
                q.pop();
                if(cur == nullptr) endNull = true;
                else{
                    if(endNull) return false;
                    q.push(cur->left);// 在else里面,不能写在外面,cur为空的时候不用继续加入新节点
                    q.push(cur->right);
                }
            }
        }
        return true;
    }
}; 		

9.完成二叉树的节点个数

给你一棵完全二叉树的根节点root,求出该树的节点个数。

一棵完全二叉树,至少有一棵是满二叉树
计算满二叉树的节点个数不用一个个节点去数,可以直接通过树高算出来,这也是这道题提高效率的关键点。

class Solution {
public:
    int countNodes(TreeNode* root) {
    	TreeNode* l = root;
    	TreeNode* r = root;
    	int heightL = 0, heightR = 0;
    	while(l != nullptr){
    		l = l->left;
    		heightL ++;
    	}
    	while(r != nullptr){
            r = r->right;
            heightR ++;
        }
        if(heightL == heightR) return (int)pow(2, heightL) - 1;
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

10.删除二叉搜索树中的节点

给定一个二叉搜索树的根节点root和一个值key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

  1. A刚好是末端节点,两个子节点都为空,那么可以直接删除
  2. A只有一个非空子节点,那么需要让这个孩子接替自己的位置
  3. A有两子节点,为了不破坏BST的性质,A必须找到左子树中最大的节点或者是右子树中最小的节点来接替自己
class Solution {
public:
    TreeNode* deleteNode(TreeNode* root, int key) {
        if(root == nullptr) return nullptr;
        if(root->val == key){
            // 情况12
            if(root->left == nullptr) return root->right;
            if(root->right == nullptr) return root->left;
            
            //情况3
            // 找到
            TreeNode* minNode = getMin(root->right);
            // 删除
            root->right = deleteNode(root->right, minNode->val);
            // 替换
            minNode->left = root->left;
            minNode->right = root->right;
            root = minNode;
        }
        else if(root->val > key) root->left = deleteNode(root->left, key);
        else if(root->val < key) root->right = deleteNode(root->right, key);
        return root;
    }
    TreeNode* getMin(TreeNode* node){
        while(node->left != nullptr) node = node->left;
        return node;

    }
};

11.寻找二叉树中的目标节点

某公司组织架构以二叉搜索树形式记录,请返回第cnt大的员工编号。

class Solution {
private:
    int res = 0;
    int rank = 0;
    void traverse(TreeNode* root, int cnt){
        if(root == nullptr) return;
        traverse(root->right, cnt);
        rank++;
        if(cnt == rank){
            res = root->val;
            return;
        }
        traverse(root->left, cnt);
    }

public:

    int findTargetNode(TreeNode* root, int cnt) {
        traverse(root, cnt);
        return res;

    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值