作者:@小萌新
专栏:@数据结构进阶
作者简介:大二学生 希望能和大家一起进步!
本篇博客简介:介绍几道二叉树的oj题
二叉树OJ题
- 题目一 根据二叉树创建字符串
- 题目二 二叉树的层序遍历
- 题目三 二叉树的最近公共祖先
题目一 根据二叉树创建字符串
给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。
空节点使用一对空括号对 “()” 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/construct-string-from-binary-tree
我们先忽略掉题目中要求我们省略括号这个步骤
这个题目其实就是要求我们按照前序遍历的方式遍历整个二叉树 并且将其中的节点变为字符加入到字符串当中去
并且遍历到左右节点之前要加括号 遍历完毕之后要闭合括号
string ans = "";
string tree2str(TreeNode* root)
{
if (root == nullptr)
{
return ans;
}
ans += to_string(root->val);
// 遍历左子树
ans += '(' ;
tree2str(root -> left);
ans += ')' ;
// 遍历右子树
ans += '(' ;
tree2str(root -> right);
ans += ')' ;
return ans;
}
于是我们写出上面的代码 得到的结果如下
这里我们可以发现相比于预期的结果 多了很多不必要的括号
首先就是左右子树为空的情况 针对这种情况我们做出这样子的代码优化
if (root -> left)
{
ans += '(' ;
tree2str(root -> left);
ans += ')' ;
}
// 遍历右子树
if (root -> right)
{
ans += '(' ;
tree2str(root -> right);
ans += ')' ;
}
我们可以发现 经过这样子的优化之后就消除了很多空格
但是还有一种情况没有顾及到
那就是示例2的情况 此种情况中 左子树为空且右子树不为空 也加上了一个空括号 于是乎我们的条件应该变成这样
root -> left || root -> right
这个条件用的很巧妙
因为当树的左孩子为空的时候才会判断第二个条件 这个时候才能符合左子树为空 并且右子树不为空
完整代码如下
string ans = "";
string tree2str(TreeNode* root)
{
if (root == nullptr)
{
return ans;
}
ans += to_string(root->val);
// 遍历左子树
if (root -> left || root -> right)
{
ans += '(' ;
tree2str(root -> left);
ans += ')' ;
}
// 遍历右子树
if (root -> right)
{
ans += '(' ;
tree2str(root -> right);
ans += ')' ;
}
return ans;
}
运行结果如下
题目二 二叉树的层序遍历
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
层序遍历的题目我们之前已经做过了 思路也很简单 使用一个队列即可完成
在这道题目还有一个需要我们解决的问题就是如何确定层序遍历的每一层的个数
并且将它们放到vector当中 这里提供一个使用变量levelsize来记录每一层大小的操作
我们每层遍历完(levelsize为0)的时候确认一次队列的大小
并将其值赋值给levelsize
之后每次出一个数据 levelsizie的大小减一 依此循环
代码表示如下
class Solution {
public:
vector<vector<int>> vv;
queue<TreeNode*> q;
vector<int> v1;
vector<vector<int>> levelOrder(TreeNode* root)
{
// 如果空树 直接返回一个空的vector
if (root == nullptr)
{
return vv;
}
int levelsize = 1;
q.push(root);
while (!q.empty())
{
if (levelsize != 0)
{
TreeNode* front = q.front();
q.pop();
v1.push_back(front->val);
levelsize--;
if (front->left != nullptr)
{
q.push(front->left);
}
if (front->right != nullptr )
{
q.push(front->right);
}
}
else
{
vv.push_back(v1);
v1.clear();
levelsize = q.size();
}
}
// 为什么这里要再push一次 因为最后一次队列为空的时候没有进循环
vv.push_back(v1);
return vv;
}
};
题目三 二叉树的最近公共祖先
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree
我们首先来分析题目
很明显的可以发现 要满足二叉树的公共祖先则必然满足两个节点分布在根节点的两边
那么知道这个限制条件就简单很多了
首先我们先写出一个搜寻函数
bool find(TreeNode* root , TreeNode* x)
{
if (root == NULL)
{
return false;
}
if (root == x)
{
return true;
}
return find(root -> left , x) || find(root -> right , x);
}
在这之后我们设计四个布尔值来确定根节点的位置
bool pleft , pright , qleft , qright;
之后的思路就很简单了
我们只要保证p和q不在同一边
也就是说 pleft与qright相同 或者qleft和pright相同就可以
代码表示如下
class Solution {
public:
bool find(TreeNode* root , TreeNode* x)
{
if (root == NULL)
{
return false;
}
if (root == x)
{
return true;
}
return find(root -> left , x) || find(root -> right , x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if (root == NULL)
{
return NULL;
}
if (root == p || root == q)
{
return root;
}
// 再之后我们使用搜寻函数来确定两个节点的左右
bool pleft , pright , qleft , qright;
pleft = find(root -> left, p);
pright = !pleft;
qleft = find(root -> left , q);
qright = !qleft;
if ((pleft && qright) || (pright && qleft))
{
return root;
}
else if(pleft && qleft)
{
return lowestCommonAncestor(root->left ,p,q);
}
else if (pright && qright)
{
return lowestCommonAncestor(root ->right , p, q);
}
else
{
return root;
}
}
};
运行结果如下