数据结构之美--树(01)

╰半橙微兮° 2022-04-01 18:20 293阅读 0赞

文章目录

  • 1.重建二叉树
      • 思路:前序(根左右)遍历的第一个节点就是根节点,于是我们在中序(左根右)遍历中找到该节点,于是该节点就把树划分成了左子树和右子树,之后递归求解即可
    1. 中序遍历的下一个节点
      • 思路:
    1. 树的子结构
      • 思路:
    1. 序列化二叉树
    1. BST的后序遍历序列
        • 题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同
        • 后序遍历(左右根).最后一个节点一定是`整个树`的根节点,根据`树与递归`的关系,泛化而讲,他会是`树`的根节点(包括左子树,右子树等等).所以我们的思路就是`先找到根,然后判断前部分(相当于左子树)是否小于根,后部分(相当于右子树)是否大于根`即可
  • 公共祖先
        1. 两个节点的最低公共祖先
          • 题目:
          • 思路:将root到两个点的路径保存下来,然后找到最后一个相同的节点,那这个节点就是最近的公共祖先(就是都经过该节点)







  • 6.关于树的深度的问题
      • (1)104. 二叉树的最大深度
      • (2)111. 二叉树的最小深度

1.重建二叉树

题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回

思路:前序(根左右)遍历的第一个节点就是根节点,于是我们在中序(左根右)遍历中找到该节点,于是该节点就把树划分成了左子树和右子树,之后递归求解即可

  1. /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */
  2. class Solution
  3. {
  4. public:
  5. TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder)
  6. {
  7. auto pre_size = preorder.size();
  8. auto in_size = inorder.size();
  9. if (pre_size != in_size || pre_size == 0 || in_size == 0)
  10. return nullptr;
  11. return fun(preorder, 0, pre_size - 1, inorder, 0, in_size - 1);
  12. }
  13. TreeNode *fun(vector<int> &preorder, int preL, int preR,
  14. vector<int> &inorder, int inderL, int inderR)
  15. {
  16. if (preL > preR)
  17. return nullptr;
  18. TreeNode *root = new TreeNode(preorder[preL]);
  19. int i = 0;
  20. for (; i <= inderR; i++)
  21. {
  22. if (inorder[i] == preorder[preL])
  23. break;
  24. }
  25. int left_size = i - inderL;
  26. int right_size = inderR - i;
  27. root->left = fun(preorder, preL + 1, preL + left_size,
  28. inorder, inderL, i - 1);
  29. root->right = fun(preorder, preL + left_size + 1, preR,
  30. inorder, i + 1, inderR);
  31. return root;
  32. }
  33. };

2. 中序遍历的下一个节点

题目:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针

思路:

  • 如果一个节点有右子树,那么下一个节点就是它的右子树中的最左子节点
  • 如果节点没有右子树,并且它是父节点的左子节点,则下一节点就是它的父节点
  • 如果节点没有右子树,并且它是父节点的右子节点,则沿着指向父节点的指针一直向上遍历,直到找到一个是它父节点的左子节点。如果这样的节点存在,那么这个节点的父节点就是我们要找的下一个节点

    class Solution
    {
    public:

    1. TreeLinkNode *GetNext(TreeLinkNode *pNode)
    2. {
    3. if (!pNode)
    4. return nullptr;
    5. if (pNode->right) /*如果右节点没空,就去找右节点的最左边的节点即可*/
    6. {
    7. TreeLinkNode *temp1 = pNode->right;
    8. while (temp1->left)
    9. {
    10. temp1 = temp1->left;
    11. }
    12. return temp1;
    13. }
    14. else /*右节点是空的*/
    15. {
    16. TreeLinkNode *temp2 = pNode;
    17. while (temp2->next && (temp2 != temp2->next->left))
    18. {
    19. temp2 = temp2->next;
    20. }
    21. if (!temp2->next)
    22. return nullptr;
    23. else
    24. return temp2->next;
    25. }
    26. }

    };

3. 树的子结构

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

思路:

  1. /* struct TreeNode { int val; struct TreeNode *left; struct TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) { } };*/
  2. class Solution
  3. {
  4. public:
  5. bool HasSubtree(TreeNode *pRoot1, TreeNode *pRoot2)
  6. {
  7. if (!pRoot1 || !pRoot2)
  8. return false;
  9. bool ret = false;
  10. if (pRoot1->val == pRoot2->val)
  11. {
  12. ret = (sametree(pRoot1->left, pRoot2->left) && sametree(pRoot1->right, pRoot2->right));
  13. if(ret) return true;
  14. }
  15. return (HasSubtree(pRoot1->left, pRoot2) || HasSubtree(pRoot1->right, pRoot2));
  16. }
  17. bool sametree(TreeNode *t1, TreeNode *t2)
  18. {
  19. if (!t2)
  20. {
  21. return true;
  22. }
  23. else
  24. {
  25. if (!t1)
  26. return false;
  27. }
  28. if (t1->val == t2->val)
  29. return (sametree(t1->left, t2->left) && sametree(t1->right, t2->right));
  30. else
  31. return false;
  32. }
  33. };

4. 序列化二叉树

题目:实现两个函数,分别用来序列化和反序列化二叉树
题解:首先,前序遍历化为一个序列! 
反序列化时,第一个就是root,之后前半部分是左子树,后半部分是右子树,遇到一个’#‘就得回到前面去找其

  1. class Codec
  2. {
  3. public:
  4. // Encodes a tree to a single string.
  5. string serialize(TreeNode *root)
  6. {
  7. if (root == nullptr)
  8. return "#";
  9. return to_string(root->val) + "," + serialize(root->left) +","+ serialize(root->right);
  10. };
  11. // Decodes your encoded data to tree.
  12. TreeNode *deserialize(string data)
  13. {
  14. return fun(data);
  15. }
  16. private:
  17. TreeNode *fun(string &data)
  18. {
  19. if (data == "")
  20. return nullptr;
  21. if (data[0] == '#')
  22. {
  23. data = data.substr(data.find(',')+1);
  24. return nullptr;
  25. }
  26. size_t idx=0;
  27. int x = stoi(data,&idx);
  28. data = data.substr(idx + 1);
  29. TreeNode *root = new TreeNode(x);
  30. root->left = fun(data);
  31. root->right = fun(data);
  32. return root;
  33. }
  34. };

这是最骚的:

  1. class Codec {
  2. private:
  3. TreeNode* _root;
  4. public:
  5. // Encodes a tree to a single string.
  6. string serialize(TreeNode* root) {
  7. _root = root;
  8. return string();
  9. }
  10. // Decodes your encoded data to tree.
  11. TreeNode* deserialize(string data) {
  12. return _root;
  13. }
  14. };

7. BST的后序遍历序列

题目:输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同

后序遍历(左右根).最后一个节点一定是整个树的根节点,根据树与递归的关系,泛化而讲,他会是的根节点(包括左子树,右子树等等).所以我们的思路就是先找到根,然后判断前部分(相当于左子树)是否小于根,后部分(相当于右子树)是否大于根即可

  1. class Solution
  2. {
  3. public:
  4. bool VerifySquenceOfBST(vector<int> sequence)
  5. {
  6. int sz = sequence.size();
  7. if (sz == 0)
  8. return false;
  9. return IsBST(sequence, 0, sz - 1);
  10. }
  11. //第一部分是左子树结点的值,它们都比根结点的值小
  12. bool IsBST(const vector<int> &sequence, int left, int right)
  13. {
  14. if (left >= right)
  15. return true;
  16. int mid, tp = left;
  17. int root = sequence.at(right);
  18. /*先找左子树*/
  19. while (tp < right && sequence.at(tp) < root)
  20. {
  21. tp++;
  22. }
  23. if (tp < right)
  24. {
  25. mid = tp;
  26. //第二部分是右子树结点的值,它们都比根结点的值大
  27. // 查找右子树是否符合要求
  28. while (tp < right)
  29. {
  30. if (sequence.at(tp) < root)
  31. {
  32. return false;
  33. }
  34. tp++;
  35. }
  36. }
  37. // 递归的判断左右子树是否符合要求
  38. return IsBST(sequence, left, mid - 1) && IsBST(sequence, mid, right - 1);
  39. }
  40. };

公共祖先

1. 两个节点的最低公共祖先

题目:

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。
示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

在这里插入图片描述
说明:

所有节点的值都是唯一的。
p、q 为不同节点且均存在于给定的二叉树中。

思路:将root到两个点的路径保存下来,然后找到最后一个相同的节点,那这个节点就是最近的公共祖先(就是都经过该节点)
  1. class Solution
  2. {
  3. public:
  4. TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
  5. {
  6. if (!root || !p || !q)
  7. return NULL;
  8. if (p == q)
  9. return p;
  10. dfs(root, p, q);
  11. // 去找最后相同的第一个数字即可
  12. // ....
  13. if (paths.size() != 2)
  14. return NULL;
  15. int idx = 0 ;
  16. while(paths[0][idx] == paths[1][idx])
  17. idx++;
  18. return paths[0][idx-1];
  19. }
  20. private:
  21. void dfs(TreeNode *root, TreeNode *p, TreeNode *q)
  22. {
  23. if (!root)
  24. return;
  25. path.push_back(root);
  26. if (root == p || root == q)
  27. paths.push_back(path);
  28. if (root->left)
  29. dfs(root->left, p, q);
  30. if (root->right)
  31. dfs(root->right, p, q);
  32. path.pop_back();
  33. }
  34. vector<vector<TreeNode *>> paths;
  35. vector<TreeNode *> path;
  36. };

#

#

#

#

#

#

#

#

#

#

#

#

#

#

#

6.关于树的深度的问题

(1)104. 二叉树的最大深度

给定一个二叉树,找出其最大深度。

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

  1. 3

/
9 20
/
15 7

返回它的最大深度 3 。

  1. class Solution
  2. {
  3. public:
  4. int maxDepth(TreeNode *root)
  5. {
  6. if (!root)
  7. return 0;
  8. int leftdepth = maxDepth(root->left);
  9. int rightdepth = maxDepth(root->right);
  10. return leftdepth > rightdepth ? leftdepth + 1 : rightdepth + 1;
  11. }
  12. };

(2)111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明: 叶子节点是指没有子节点的节点。

示例:

给定二叉树 [3,9,20,null,null,15,7],

  1. 3

/
9 20
/
15 7
返回它的最小深度 2

  1. class Solution
  2. {
  3. public:
  4. int minDepth(TreeNode *root)
  5. {
  6. if (root == NULL)
  7. return 0;
  8. int left = minDepth(root->left), right = minDepth(root->right);
  9. if (root->left && root->right)
  10. return 1 + min(left, right);
  11. else //只要有一方 左边或者右边 为空
  12. return 1 + left + right;
  13. /* 1.左空:1+0+右 2.右空:1+左+0 3.左右都空的:1+0+0 */
  14. }
  15. };

#

#

#

发表评论

表情:
评论列表 (有 0 条评论,293人围观)

还没有评论,来说两句吧...

相关阅读

    相关 数据结构-

    二叉树: 平衡二叉树 B树 B+树 红黑树的特点: 红黑树是一种自平衡的二叉查找树,除了符合二叉查找树的基本特征外,它还具有下列的附加特征。

    相关 数据结构

    树的定义 树:树是n个结点的有限集。n=O 时称为空树. 在任意一棵非空树中: ( 1 ) 有旦仅有一个特定的称为根的结点: ( 2 ) 当n > 1 时,其余结