剑指offer-树


输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字

剑指 07.重建二叉树

class Solution {
private:
    unordered_map<int, int> index;

public:
    TreeNode* myBuildTree(const vector<int>& preorder, const vector<int>& inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
        if (preorder_left > preorder_right) {
            return nullptr;
        }
        
        // 前序遍历中的第一个节点就是根节点
        int preorder_root = preorder_left;
        // 在中序遍历中定位根节点
        int inorder_root = index[preorder[preorder_root]];
        
        // 先把根节点建立出来
        TreeNode* root = new TreeNode(preorder[preorder_root]);
        // 得到左子树中的节点数目
        int size_left_subtree = inorder_root - inorder_left;
        // 递归地构造左子树,并连接到根节点
        // 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
        root->left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
        // 递归地构造右子树,并连接到根节点
        // 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
        root->right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
        return root;
    }

    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n = preorder.size();
        // 构造哈希映射,帮助我们快速定位根节点
        for (int i = 0; i < n; ++i) {
            index[inorder[i]] = i;
        }
        return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
    }
};

题解来源:官方https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/solution/mian-shi-ti-07-zhong-jian-er-cha-shu-by-leetcode-s/

剑指 Offer 26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。


/**
 * 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 recure(TreeNode* A,TreeNode* B){
        if(B == nullptr)    return true;// 表示B已经遍历完
        if(A == nullptr || B->val != A->val)    return false;// 判断A是否遍历完,或当前A的
        值 是否对于B的值
        return recure(A->left,B->left) && recure(A->right,B->right);
    }
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        if(A == nullptr || B == nullptr)    return false;
        return recure(A,B) || isSubStructure(A->left,B) || isSubStructure(A->right,B);
    }
};

剑指 Offer 27 二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 // DFS
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if(!root) return NULL; // 检查数据有效性 & 退出递归条件
        TreeNode* tem_right=root->right;// 保存右子结点
        root->right=mirrorTree(root->left);//  递归左右子结点,并交换左右结点
        root->left=mirrorTree(tem_right);
        return root;// 返回当前访问的结点 && 最终返回跟结点
    }
};
// BFS
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {
        if(root == nullptr) return nullptr;
        stack<TreeNode*> stack;
        stack.push(root);
        while (!stack.empty())
        {
            TreeNode* node = stack.top();
            stack.pop();
            if (node->left != nullptr) stack.push(node->left);
            if (node->right != nullptr) stack.push(node->right);
            TreeNode* tmp = node->left;
            node->left = node->right;
            node->right = tmp;
        }
        return root;
    }
};

剑指 Offer 28. 对称的二叉树

请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
在这里插入图片描述

/**
 * 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 isSymmetric(TreeNode* root) {
        if(!root) return true;
        return recur(root->left,root->right);
    }

    bool recur(TreeNode* L,TreeNode* R)
    {
        if(L==NULL && R==NULL) return true;
        if(L==NULL || R==NULL || L->val!=R->val) return false;
        return recur(L->left,R->right) && recur(L->right,R->left);
    }
};

题解来自于:面试题28. 对称的二叉树(递归,清晰图解)

剑指 Offer 32 - II. 从上到下打印二叉树 II

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。

/**
 * 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:
    
    
    vector<vector<int>> levelOrder(TreeNode* root) {        
        vector<vector<int>> ans;
        queue<TreeNode*> que;
        if(root) que.push(root);      
    //   vector<int> same_lay;
      while(!que.empty())
      {
          vector<int> same_lay;
          int size = que.size();
          for(int i=0;i<size;i++)// 这里的size是每层结点的个数,不能用que.size(),因为que在这个循环中会变化
          {
            TreeNode* node = que.front();
            que.pop();//每一层有多少个节点 就会被弹多少次  与此同时压入下一层的左右子节点
            same_lay.push_back(node->val);
            // same_lay.push_back(node->val);
            // que.pop();// 
            if(node->left) que.push(node->left);
            if(node->right) que.push(node->right);
          }
          ans.push_back(same_lay);
      }
      return ans;
    }
};

剑指 Offer 37.序列化二叉树

请实现两个函数,分别用来序列化和反序列化二叉树。

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

    // Encodes a tree to a single string.
    string res;
    string serialize(TreeNode* root) {          //序列化阶段
        dfs(root);
        return res;
    }

    void dfs(TreeNode* root)                    //用前序遍历的方式序列化二叉树
    {
        if (!root) res += "NULL,";              //空节点存入NULL字符串
        else
        {
            res += to_string(root->val) + ',';  //非空节点存入字符串数值
            dfs(root->left);                    //这里也可以用中序或后序
            dfs(root->right);                   //只要保证反序列化时也用同样的方式即可
        }
    }

    // Decodes your encoded data to tree.    
    queue<string> q;
    TreeNode* deserialize(string data) {        //反序列化阶段
        int i = 0, j = 0;                       //用双指针按照逗号来分割字符串
        while (i < data.size())                 //以逗号分割字符串,存入队列q中
        {
            while (data[i] != ',' && i < data.size()) i ++ ;
            string tmp = data.substr(j, i - j);
            q.push(tmp);
            i ++ ;
            j = i;
        }

        return de_dfs();                        //递归建树
    }

    TreeNode* de_dfs()
    {
        auto t = q.front();
        q.pop();

        if (t == "NULL") return NULL;

        TreeNode* root = new TreeNode(stoi(t)); //仍然用前序遍历的方式构建二叉树
        root->left = de_dfs();
        root->right = de_dfs();

        return root;
    }
};

// Your Codec object will be instantiated and called as such:
// Codec codec;
// codec.deserialize(codec.serialize(root));

1、序列化:遍历二叉树,将每个节点值保存到string中,空节点的位置存NULL。
2、反序列化:将string中的value保存到deque中,再对deque重建二叉树。
注意:
1、val和NULL后面都有","来分割字符串;
2、保存string时,用两个指针按照逗号分割字符串。

题解来自于:https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof/solution/jian-zhi-offer-37-xu-lie-hua-er-cha-shu-rcu0v/

剑指 Offer 44. 数字序列中某一位的数字

数字以0123456789101112131415…的格式序列化到一个字符序列中。在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等。

请写一个函数,求任意第n位对应的数字。

分析
优雅三步法。这道题其实就是找一下规律,同时需要我们细心计算好一些细节,比如位数、余数、索引等。其实不算难,且看分析。
第一步,我们得找到 n 属于哪个数位里的索引。比如 n = 5,那 n 就是个位这个数位里的索引;或者 n = 11,那 n 就是十位这个数位里的索引。
第二步,确定了 n 属于哪个数位,我们需要进一步定位到 n 具体属于哪个数。比如 n = 11,指的就是 10 这个数。
第三步,确定了 n 属于哪个数,我们就需要算出 n 是这个数的第几位,从而得到最终答案。比如 n = 11,指的是 10 这个数的第 1 位(索引从 0 开始),从而最终答案就是 0。
这里我们定义了几个变量来帮助我们完成上面的三步(代码中对应处也有注释):
digit。表示数位,比如个位,digit = 1;十位,digit = 2;百位,digit = 3;以此类推。
start。表示该数位的所有数的起始点数。比如个位,start = 1(0 做特例处理,不算在内);十位,start = 10;千位,start = 1000;以此类推。
index_count。表示该数位一共有的索引个数。比如个位,index_count = 9(1-9);十位,index_count = 180(10-99);百位,index_count = 2700(100-999);以此类推。
总结规律:index_count = digit * 9 * start。比如十位,index_count = 2 * 9 * 10 = 180。

class Solution {
public:
    int findNthDigit(int n) {
        if(n == 0) {return 0;}

        int digit = 1; // 数位(个位/十位/百位/...,就是1/2/3/...)
        long start = 1; // 属于该数位的所有数的起始点数(个位是1,十位是10,百位是100)
        long index_count = digit * 9 * start; // 该数位的数一共的索引个数(不是数字个数)

        while(n > index_count ) {
            // 找出 n 属于那个数位里的索引
            n -= index_count; 
            ++ digit;
            start *= 10;
            index_count = digit * 9 * start;
        }
        // 上面的循环结束后:
        // digit 等于原始的 n 所属的数位;start 等于原始的 n 所属数位的数的起始点
        // index_count 等于原始的 n 所属数位的索引总个数(不重要了,下面不用)
        // n 等于在当前数位里的第 n - 1 个索引(索引从 0 开始算起)

        long num = start + (n - 1) / digit; // 算出原始的 n 到底对应哪个数字
        int remainder = (n - 1) % digit; // 余数就是原始的 n 是这个数字中的第几位

        string s_num = to_string(num); // 将该数字转为 string 类型
        return int(s_num[remainder] - '0'); // n 对应着第 remainder 位,再转成 int
    }
};

题解来自 力扣(LeetCode)
作者:superkakayong
链接:https://leetcode-cn.com/problems/shu-zi-xu-lie-zhong-mou-yi-wei-de-shu-zi-lcof/solution/zi-jie-ti-ku-jian-44-zhong-deng-shu-zi-xu-lie-zhon/

剑指 Offer 54. 二叉搜索树的第k大节点

给定一棵二叉搜索树,请找出其中第k大的节点。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 // 中序遍历是递增序列,反向中序遍历则位递减序列,找到第k个元素,即为第k大的节点
class Solution {
public:
    int count = 0, res = 0;
    int kthLargest(TreeNode* root, int k) {
        inorder(root, k);
        return res;
    }

    void inorder(TreeNode* root, int k) {
        if (!root) return;

        inorder(root -> right, k); // 右
        ++ count;
        if (count == k) 
        {
            res = root -> val;
            return;
        }
        inorder(root -> left, k); // 左
    }
};

剑指 Offer 55 - I. 二叉树的深度

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 // 递归,当前字树的最大深度 = 其左子树和右子树最大深度的最大值 + 1
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if (!root) return 0;
        int left=maxDepth(root->left);
        int right=maxDepth(root->right);
        return max(left,right)+1;
    }
};

// 层次遍历,记录层数
class Solution {
public:
    int maxDepth(TreeNode* root) {
        if(root == NULL)    return 0;
        queue<TreeNode* >q;
        q.push(root);
        int depth = 0;
        while(!q.empty())
        {
            ++depth;
            int count = q.size();   //保存每一层元素个数
            while(count--)
            {
                TreeNode* temp = q.front();
                q.pop();

                if(temp->left)   q.push(temp->left);
                if(temp->right)  q.push(temp->right);
            }
        }
        return depth;
    }   
};

剑指 Offer 55 - II. 平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过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 isBalanced(TreeNode* root) {
        return depth(root)!=-1;
    }
    int depth(TreeNode* root)
    {
        if (!root) return 0;
        int left=depth(root->left);
        int right=depth(root->right);
        if (left==-1) return -1;
        if (right==-1) return -1;
        if(abs(left-right)<=1) 
        {
            return max(left,right)+1;
        }
        else
        {
            return -1;
        }
    }
};

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。

/**
 * 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(!root) return NULL;
        if(root==p || root==q) return root;

        TreeNode*  left=lowestCommonAncestor(root->left,p,q);
        TreeNode*  right=lowestCommonAncestor(root->right,p,q);
        
        if(left!=NULL && right!=NULL) return root;
        if( left != NULL ) return left;
        if( right != NULL ) return right;
        return NULL; 
        
    }
};

剑指 Offer 68 - II. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
递 归 实 现

/**
 * 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* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)
        {
            return NULL;
        }
        if( root == p || root == q)// 找到 p 或 q 
        {
            return root;
        }
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);
        if( left != NULL && right != NULL )
        {
            return root;
        }
        if( left != NULL )
        {
            return left;
        }
        if( right != NULL )
        {
            return right;
        }
        return NULL;                
    } 
};

面试题 04.12. 求和路径

给定一棵二叉树,其中每个节点都含有一个整数数值(该值或正或负)。设计一个算法,打印节点数值总和等于某个给定值的所有路径的数量。注意,路径不一定非得从二叉树的根节点或叶节点开始或结束,但是其方向必须向下(只能从父节点指向子节点方向)。


/**
 * 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:

    int res=0;
    int pathSum(TreeNode* root, int sum) {
        if(!root) return 0;
        dfs(root,sum);// 从当前结点开始深度遍历,看遍历过程中有没有等于sum的路径
        pathSum(root-> left,sum);// 当前结点遍历完没有找到相应路径,开始从左子树开始找
        pathSum(root-> right,sum);
        return res;
    }

    void dfs(TreeNode* root, int sum)
    {
        if(!root) return;        
        if(root->val == sum) res++;// 先看当前结点值是否等于sum,若等于则找到目标路径,res++
        dfs(root->left,sum-root->val);// 查找左子树中有没有满足条件的路径,注意需要减去当前结点的val
        dfs(root->right,sum-root->val);

    }

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值