给你二叉树的根节点 root ,请你采用前序遍历的方式,将二叉树转化为一个由括号和整数组成的字符串,返回构造出的字符串。
要用括号分别括住左右子树
空节点使用一对空括号对 “()” 表示,转化后需要省略所有不影响字符串与原始二叉树之间的一对一映射关系的空括号对。
链接:力扣(LeetCode)
省略:
左右都为空
右子树为空,左子树不为空
若仅左为空,不省略
前序遍历
class Solution {
public:
string tree2str(TreeNode* root) {
if (root == nullptr)
return string();
string str;
str += to_string(root->val);
//左边不为空或是左边为空右边不为空,左边需要加括号
if (root->left || root->right)
{
str += '(';
str += tree2str(root->left);
str += ')';
}
//右边不为空,右边需要加括号
if (root->right)
{
str += '(';
str += tree2str(root->right);
str += ')';
}
return str;
}
};
问题点:return str;是传值返回
解决:写一个子函数,用str的引用传参
链接
给你二叉树的根节点 root ,返回其节点值的 层序遍历 。 (即逐层地,从左到右访问所有节点)。
难点:
想办法去区分是否是同一层的结点
控制一层一层出——levelsize
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> q;
size_t levelSize = 0;
if (root)
{
//第一层——只有根
q.push(root);
levelSize = 1;
}
vector<vector<int>> vv;
while (!q.empty())
{
//控制一层一层的出
vector<int> v;
for (size_t i = 0; i < levelSize; ++i)
{
//出的是结点的指针,存的也是结点的指针
//树的指针
TreeNode* front = q.front();
q.pop();//出队列,出的是队列中存的值,树结点的指针已经被保存了
//将队列中的值尾插进v中
v.push_back(front->val);
//下一层的进队列
if (front->left)
{
q.push(front->left);
}
if (front->right)
{
q.push(front->right);
}
}
vv.push_back(v);//二维数组,一层一层
//当前层出完了,下一层都进队列,队列的size就是下一层的数据个数
levelSize = q.size();
}
return vv;
}
};
还有另外一种思路:双队列
1.存结点指针
2.存层数
链接
给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)
逆置
reverse(vv.begin(),vv.end());
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
queue<TreeNode*> q;
size_t levelSize = 0;
if (root)
{
q.push(root);
levelSize = 1;
}
vector<vector<int>> vv;
while (!q.empty())
{
//控制一层一层的出
vector<int> v;
for (size_t i = 0; i < levelSize; ++i)
{
TreeNode* front = q.front();
q.pop();
v.push_back(front->val);
if (front->left)
{
q.push(front->left);
}
if (front->right)
{
q.push(front->right);
}
}
vv.push_back(v);
//当前层出完了,下一层都进队列,队列的size就是下一层的数据个数
levelSize = q.size();
}
reverse(vv.begin(),vv.end());
return vv;
}
};
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
链接:力扣(LeetCode)
规则:一个是左子树里的结点,一个是右子树的结点,那么他就是最近公共祖先
class Solution {
public:
bool Find(TreeNode* sub, TreeNode* x)
{
if (sub == nullptr)
{
return false;
}
return sub == x || Find(sub->left, x) || Find(sub->right, x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr)
{
return nullptr;
}
if (root == p || root == q)
{
return root;
}
bool pInLeft, pInRight, qInLeft, qInRight;
pInLeft = Find(root->left, p);
pInRight != pInLeft;
qInLeft = Find(root->left, q);
qInRight != qInLeft;
//1.一个在左一个在右,root就是最近公共祖先
//2.都在左,递归去左子树找
//3.都在右,递归去右子树找
if ((pInLeft && qInRight) || (qInLeft && pInRight))
{
return root;
}
else if (pInLeft && qInLeft)
{
return lowestCommonAncestor(root->left, p, q);
}
else if (pInRight && qInRight)
{
return lowestCommonAncestor(root->right, p, q);
}
else
{
//理论而言不会走到这里
return nullptr;
}
}
};
优化:O(H*N)——O(H)
class Solution {
public:
bool FindPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& path)
{
if (root == nullptr)
return false;
path.push(root);
if (root == x)
return true;
if (FindPath(root->left, x, path))
return true;
if (FindPath(root->right, x, path))
return true;
path.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
stack<TreeNode*> pPath, qPath;
FindPath(root, p, pPath);
FindPath(root, q, qPath);
while (pPath.size() != qPath.size())
{
if (pPath.size() > qPath.size())
pPath.pop();
else
qPath.pop();
}
while (pPath.top() != qPath.top())
{
pPath.pop();
qPath.pop();
}
return pPath.top();
}
};
注意:
1.要求不能创建任何新的结点,只能调整树中结点指针的指向。当转化完成以后,树中节点的左指针需要指向前驱,树中节点的右指针需要指向后继
2.返回链表中的第一个节点的指针
3.函数返回的TreeNode,有左右指针,其实可以看成一个双向链表的数据结构
4.你不用输出双向链表,程序会根据你的返回值自动打印输出
class Solution {
public:
void InOrderConvert(TreeNode* cur, TreeNode*& prev)
{
if (cur == nullptr)
{
return;
}
InOrderConvert(cur->left, prev);
cur->left = prev;
if (prev)
prev->right = cur;
prev = cur;
InOrderConvert(cur->right, prev);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
TreeNode* prev = nullptr;
InOrderConvert(pRootOfTree, prev);
TreeNode*head = pRootOfTree;
while (head && head->left)
head = head->left;
return head;
}
};
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
链接:力扣(LeetCode)
![在这里插入图片描述](https://img-blog.csdnimg.cn/80dacb7baa124869961060990b7de541.png#pic_center
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder,int& prei,int inbegin, int inend) {
if (inbegin > inend)
return nullptr;
TreeNode* root = new TreeNode(preorder[prei++]);
//分割中序
int ini = inbegin;
while (ini <= inend)
{
if(inorder[ini] == root->val)
break;
else
++ini;
}
root->left = _buildTree(preorder, inorder, prei, inbegin, ini - 1);
root->right = _buildTree(preorder, inorder, prei, ini + 1, inend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int i = 0;
return _buildTree(preorder,inorder,i,0,inorder.size()-1);
}
};
链接
class Solution {
int post_idx;
unordered_map<int, int> idx_map;
public:
TreeNode* helper(int in_left, int in_right, vector<int>& inorder, vector<int>& postorder){
// 如果这里没有节点构造二叉树了,就结束
if (in_left > in_right) {
return nullptr;
}
// 选择 post_idx 位置的元素作为当前子树根节点
int root_val = postorder[post_idx];
TreeNode* root = new TreeNode(root_val);
// 根据 root 所在位置分成左右两棵子树
int index = idx_map[root_val];
// 下标减一
post_idx--;
// 构造右子树
root->right = helper(index + 1, in_right, inorder, postorder);
// 构造左子树
root->left = helper(in_left, index - 1, inorder, postorder);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
// 从后序遍历的最后一个元素开始
post_idx = (int)postorder.size() - 1;
// 建立(元素,下标)键值对的哈希表
int idx = 0;
for (auto& val : inorder) {
idx_map[val] = idx++;
}
return helper(0, (int)inorder.size() - 1, inorder, postorder);
}
};
递归调用太深,容易奔溃。栈空间不大,会栈溢出
循环迭代,一般不会有空间的消耗
堆——动态申请 空间大
链接
给你二叉树的根节点 root ,返回它节点值的 前序 遍历。
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
while (cur || !st.empty())
{
//开始访问一棵树——cur指向结点的树
//1.左路结点
while (cur)
{
v.push_back(cur->val);
st.push(cur);
cur = cur->left;
}
//2.左路结点的右子树需要访问
TreeNode* top = st.top();
st.pop();
cur = top->right;//子问题访问右子树
}
return v;
}
};
一个结点从栈里面出来意味着:这个结点及它的左子树访问完了,还剩右子树
链接
给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
while (cur || !st.empty())
{
//1.左路结点入栈
while (cur)
{
st.push(cur);
cur = cur->left;
}
//2.当左路结点从栈中出来,表示左子树已经访问过了,应该访问它的结点和他的右子树
TreeNode* top = st.top();
st.pop();
v.push_back(top->val);
cur = top->right;//子问题访问右子树
}
return v;
}
};
链接
给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历 。
一个结点右不为空的情况下:
1.右子树没有访问,访问右子树
2.右子树已经访问过了,访问根结点
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> v;
TreeNode* cur = root;
TreeNode* prev = nullptr;
while (cur || !st.empty())
{
//1.左路结点入栈
while (cur)
{
st.push(cur);
cur = cur->left;
}
//2.当左路结点从栈中出来,表示左子树已经访问过了
TreeNode* top = st.top();
//栈顶结点右子树为空或上一个访问结点是右子树的根,说明右子树已经访问过了,可以访问这个栈顶结点
//否则 子问题访问top的右子树
if (top->right == nullptr || top->right == prev)
{
v.push_back(top->val);
prev = top;
cur = nullptr;
st.pop();
}
else{
cur = top->right;//子问题访问右子树
}
}
return v;
}
};