• 二叉树的OJ题——C++


    一、根据二叉树创建字符串

    题目链接:

    606. 根据二叉树创建字符串 - 力扣(LeetCode)

    题目描述:

    前序遍历二叉树,并且将结果存入到一个string中,并且要使用括号去分割和表示每个节点和子树之间的关系,并且要求省略掉可以省略的括号

    解题思路:

    根据题意,就是一个二叉树前序递归的问题,对是否省略的问题上分类讨论一下即可

    参考代码:

    1. class Solution
    2. {
    3. public:
    4. string tree2str(TreeNode* root)
    5. {
    6. if(root == nullptr)
    7. {
    8. return "";
    9. }
    10. string ret = to_string(root->val);
    11. if(root->left || root->right)
    12. {
    13. ret+="(";
    14. ret+= tree2str(root->left);
    15. ret+=")";
    16. }
    17. if(root->right)
    18. {
    19. ret+="(";
    20. ret+=tree2str(root->right);
    21. ret+=")";
    22. }
    23. return ret;
    24. }
    25. };

    二、二叉树的层序遍历

    题目链接:

    102. 二叉树的层序遍历 - 力扣(LeetCode)

    题目描述:

    二叉树的层序遍历,就是对二叉树一层一层的进行遍历,题目要求将遍历的结果存在一个二维数组里返回。

    解题思路:

    利用一个队列,每遍历一个节点,就将该节点的左子树和右子树的根放入队列,每次取队头元素遍历,有种排队进出的感觉,由于需要一层一层的出,因此从第一层开始,就用一个size去记录每一层的个数,通过size去控制每一层出几次,遇到空则不入队列

    参考代码:

    1. vectorint>> levelOrder(TreeNode* root)
    2. {
    3. vectorint>> ret;
    4. queue q;
    5. int num;
    6. if(root !=nullptr)
    7. {
    8. q.push(root);
    9. }
    10. while(!q.empty())
    11. {
    12. num = q.size();
    13. vector<int> tmp;
    14. while(num--)
    15. {
    16. TreeNode* node = q.front();
    17. if(node->left != nullptr)
    18. {
    19. q.push(node->left);
    20. }
    21. if(node->right != nullptr)
    22. {
    23. q.push(node->right);
    24. }
    25. tmp.push_back(node->val);
    26. q.pop();
    27. }
    28. ret.push_back(tmp);
    29. }
    30. return ret;
    31. }

    拓展:

    反向层序遍历,要求反向层序遍历,没有特殊要求,则直接将结果逆置即可

    107. 二叉树的层序遍历 II - 力扣(LeetCode)

    三、二叉树的最近公共祖先

    题目链接:

    236. 二叉树的最近公共祖先 - 力扣(LeetCode)

    题目描述:

    找两个节点p和q的最近的公共祖先,并且节点本身也能成为祖先,不存在找不到的测试用例

    解题思路:

    思路一:

    通过观察可以发现,当p和q分别在一个节点的左子树和右子树时,这个节点就一定是p和q的最近公共祖先

    思路二:

    关键在Path如何实现

    参考代码:

    思路一:
    1. bool FindNode(TreeNode* root,TreeNode* goal)
    2. {
    3. if(root == nullptr)
    4. {
    5. return false;
    6. }
    7. if(root == goal)
    8. {
    9. return true;
    10. }
    11. return FindNode(root->left,goal) || FindNode(root->right,goal);
    12. }
    13. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
    14. {
    15. if(root == nullptr)
    16. {
    17. return nullptr;
    18. }
    19. if(root == p)
    20. {
    21. return root;
    22. }
    23. if(root == q)
    24. {
    25. return root;
    26. }
    27. bool q_left = FindNode(root->left,q);
    28. bool q_right = !q_left;
    29. bool p_left = FindNode(root->left,p);
    30. bool p_right = !p_left;
    31. if((q_left && p_right) || (q_right && p_left))
    32. {
    33. return root;
    34. }
    35. else if(q_left && p_left)
    36. {
    37. return lowestCommonAncestor(root->left,p,q);
    38. }
    39. else
    40. {
    41. return lowestCommonAncestor(root->right,p,q);
    42. }
    43. }
    思路二:
    1. bool Path(TreeNode* root,TreeNode* goal,stack& st)
    2. {
    3. if(root == nullptr)
    4. {
    5. return false;
    6. }
    7. st.push(root);
    8. if(root == goal)
    9. {
    10. return true;
    11. }
    12. if(Path(root->left,goal,st))
    13. {
    14. return true;
    15. }
    16. if(Path(root->right,goal,st))
    17. {
    18. return true;
    19. }
    20. else
    21. {
    22. st.pop();
    23. return false;
    24. }
    25. }
    26. TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
    27. {
    28. stack st_p;
    29. stack st_q;
    30. Path(root,p,st_p);
    31. Path(root,q,st_q);
    32. while(st_p.size() > st_q.size())
    33. {
    34. st_p.pop();
    35. }
    36. while(st_p.size() < st_q.size())
    37. {
    38. st_q.pop();
    39. }
    40. while(st_p.top() != st_q.top())
    41. {
    42. st_p.pop();
    43. st_q.pop();
    44. }
    45. return st_p.top();
    46. }

    四、二叉搜索树与双向链表

    题目链接:

    二叉搜索树与双向链表_牛客题霸_牛客网 (nowcoder.com)

    题目描述:

    将一颗二叉搜索树转换为一个有序的双向不循环链表,然后返回任意一边的节点,并且要求在原树上进行改造,不允许额外的开空间。

    解题思路:

    首先,需要有序的遍历二叉搜索树,我们需要使用中序遍历,在中序遍历中,我们需要一边走,一边改变指针的方向,使得前后相互链接,因此我们可以两个指针一起走,同时遍历,记录每一步的前一个节点,并且将他们两个互相链接起来,最后遍历完整棵树,也就改造好了

    参考代码:

    1. void InOrder(TreeNode* root, TreeNode*& prev) {
    2. if (root == nullptr)
    3. return;
    4. InOrder(root->left, prev);
    5. root->left = prev;
    6. if (prev)
    7. prev->right = root;
    8. prev = root;
    9. InOrder(root->right, prev);
    10. }
    11. TreeNode* Convert(TreeNode* pRootOfTree)
    12. {
    13. TreeNode* prev = nullptr;
    14. InOrder(pRootOfTree, prev);
    15. TreeNode* head = pRootOfTree;
    16. while(head && head->left)
    17. {
    18. head = head->left;
    19. }
    20. return head;
    21. }

    五、根据前序与中序遍历序列构造二叉树

    题目链接:

    105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)

    题目描述:

    题目给了一个二叉树前序遍历后的序列和中序遍历后的序列,需要我们根据这两个序列还原构建一个二叉树

    解题思路:

    首先要知道,如果根据前序和中序序列去还原一颗二叉树,我们需要通过前序序列去确定根,然后在中序序列中找到根的位置,把根位置的两边分别分割成左子树和右子树,然后继续去前序序列中确定子树的根,直到将前序遍历完,也就能够完整分割中序序列,重新构建回一个二叉树

    参考代码:

    1. TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,int& prei,int inbegin,int inend)
    2. {
    3. if(inbegin > inend)
    4. {
    5. return nullptr;
    6. }
    7. int root_val = preorder[prei++];
    8. TreeNode* root = new TreeNode(root_val);
    9. //找到在中序序列中根的位置,分割区间
    10. int rooti = inbegin;
    11. while(inorder[rooti] != root_val)
    12. {
    13. rooti++;
    14. }
    15. root->left = _buildTree(preorder,inorder,prei,inbegin,rooti-1);
    16. root->right =_buildTree(preorder,inorder,prei,rooti+1,inend);
    17. return root;
    18. }
    19. TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
    20. {
    21. int prei = 0;
    22. return _buildTree(preorder,inorder,prei,0,inorder.size()-1);
    23. }

    拓展:

    通过中序遍历和后序遍历序列重构二叉树,思路上和这个类似

    106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)

    六、非递归_前序遍历

    题目链接:

    144. 二叉树的前序遍历 - 力扣(LeetCode)

    题目描述:

    用非递归的方式走前序遍历

    解题思路:

    我们需要借助一个栈,将问题划分为两步,一是先左边的节点全部入栈,然后再一个个出栈解决每个节点的右子树,每个右子树又得当成子问题一样处理

    参考代码:

    1. vector<int> preorderTraversal(TreeNode* root)
    2. {
    3. vector<int> ret;
    4. stack st;
    5. TreeNode* cur = root;
    6. while(cur || !st.empty())
    7. {
    8. while(cur)
    9. {
    10. ret.push_back(cur->val);
    11. st.push(cur);
    12. cur=cur->left;
    13. }
    14. TreeNode* top = st.top();
    15. st.pop();
    16. cur = top->right;
    17. }
    18. return ret;
    19. }

    拓展:

    非递归_中序遍历和非递归_后序遍历,思路上一样,后序遍历需要一定的特殊处理

    94. 二叉树的中序遍历 - 力扣(LeetCode)

    145. 二叉树的后序遍历 - 力扣(LeetCode)

    后序遍历的处理,需要对什么时候取节点的值需要进行判断,根据画图分析,将情况分类为两种,一种是栈顶元素的右子树为空时,则输出,并且将该节点pop掉,当右子树不为空时,则需要进一步对右子树的值进行入栈,并且不能将该节点pop,只有在该节点被输出后才可以pop

    对节点什么时候输出分两种情况,一种是左子树走完,右子树为空,另一种是左子树走完,右子树也走完,出栈元素一定是走完左子树的,对于右子树需要进一步判断,而需要判断右子树是否走完则需要额外的一个节prev去记录上一次pop的数据,如果上一次pop的数据是该节点的右子树,则说明该节点走完了右子树,需要输出

  • 相关阅读:
    mongodb 基本概念
    MYSQL入门与进阶(六)
    java基础15
    C语言文件操作(超详细版)
    虚拟机字节码执行引擎——动态类型语言支持
    vim使用技巧
    Latex学习(1)——latex中的字体颜色
    【华为OD机试python】查找众数及中位数【2023 B卷|100分】
    PyTorch实战 | 文本情感分类任务 | LSTM与LSTM+Attention
    第十四章 使用Vercel部署在线文档
  • 原文地址:https://blog.csdn.net/china_chk/article/details/134269746