文章目录
一、根据二叉树创建字符串
思路:
- to_string函数,数值转换字符串
- 加括号判断
- 加左括号:左空右不空 和 左不空
- 加右括号:右不空
class Solution
{
public:
string tree2str(TreeNode* root)
{
if(root == nullptr)
return "";
string 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;
}
};
二、二叉树的层序遍历
思路:
- 利用queue层序,先让root入队
- 用levelSize作为循环次数,一层层处理
- 每个节点出队时,带左右孩子入队
class Solution
{
public:
vector<vector<int>> levelOrder(TreeNode* root)
{
queue<TreeNode*> q;
int levelSize = 0;
if(root)
{
q.push(root);
levelSize = 1;
}
vector<vector<int>> vv;
while(!q.empty())
{
vector<int> v;
while(levelSize--)//一层一层数据进行处理
{
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);
levelSize = q.size();//更新每层数据个数
}
return vv;
}
};
三、二叉树的最近公共祖先
思路:
- 转换为路径相交问题,利用stack记录路径
- 写子函数GetPath,返回值为bool,每次先让root入栈,以前序遍历递归进行查找节点,如果找到返回true,最后没找到让当前root出栈,返回false
- 最后得到路径,让两条路径长度相等,再比较判断相交节点
class Solution
{
public:
bool GetPath(TreeNode* root, TreeNode* x, stack<TreeNode*>& Path)
{
if(root == nullptr)
return false;
Path.push(root);
if(root == x)
return true;
if(GetPath(root->left, x, Path))
return true;
if(GetPath(root->right, x, Path))
return true;
Path.pop();
return false;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
stack<TreeNode*> pPath, qPath;
GetPath(root, p, pPath);
GetPath(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();
}
};
四、二叉搜索树转换双向链表
思路:
- 设置head记录最左节点,设置prev记录上个访问的节点
- 中序遍历递归,首次初始化head和prev,后面利用prev和root进行双向链接
class Solution
{
public:
TreeNode* head = nullptr;
TreeNode* prev = nullptr;
TreeNode* Convert(TreeNode* root)
{
if(root == nullptr)
return nullptr;
Convert(root->left);
if(prev == nullptr)
{
head = root;
prev = root;
}
else
{
prev->right = root;
root->left = prev;
prev = root;
}
Convert(root->right);
return head;
}
};
五、构造二叉树
5.1 前序与中序
思路:
- 写子函数_buildTree,方便控制条件递归,参数新增prei,inbegin,inend
- 前序确定根,创建根节点,中序找到分割点rooti,递归分割左右子树子区间(先左后右)
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 rooti = inbegin;
while(rooti <= inend && inorder[rooti] != preorder[prei])
{
++rooti;
}
++prei;
//分割左右子树子区间
root->left = _buildTree(preorder, inorder, prei, inbegin, rooti-1);
root->right = _buildTree(preorder, inorder, prei, rooti+1, inend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
int i = 0;
return _buildTree(preorder, inorder, i, 0, inorder.size()-1);
}
};
5.2 中序与后序
思路:
- 写子函数_buildTree,方便控制条件递归,参数新增posti,inbegin,inend
- 后序确定根,创建根节点,中序找到分割点rooti,递归分割左右子树子区间(先右后左)
class Solution
{
public:
TreeNode* _buildTree(vector<int>& postorder, vector<int>& inorder, int& posti, int inbegin, int inend)
{
if(inbegin > inend)
{
return nullptr;
}
//后序确定根
TreeNode* root = new TreeNode(postorder[posti]);
//查找中序分割点
int rooti = 0;
while(rooti <= inend && inorder[rooti] != postorder[posti])
{
++rooti;
}
--posti;
//分割左右子树子区间
root->right = _buildTree(postorder, inorder, posti, rooti+1, inend);
root->left = _buildTree(postorder, inorder, posti, inbegin, rooti-1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int i = postorder.size()-1;
return _buildTree(postorder, inorder, i, 0, inorder.size()-1);
}
};
六、二叉树的前中后序遍历(非递归)
6.1 前序
class Solution
{
public:
vector<int> preorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
vector<int> v;
//v.reserve(100);
TreeNode* cur = root;
while(cur || !st.empty())
{
//访问一棵树
//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;
}
};
6.2 中序
class Solution
{
public:
vector<int> inorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
vector<int> v;
//v.reserve(100);
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;
}
};
6.3 后序
思路:
- 前中后序共同思路:先访问左路节点,再访问左路节点的右子树
- 利用stack记录左路节点,直到cur走到空
- 后序的关键:设置prev指针,记录上一个访问的节点
- 如果右为空 或者 右为prev,根节点出栈遍历,否则cur指向右
class Solution
{
public:
vector<int> postorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
vector<int> v;
//v.reserve(100);
TreeNode* prev = nullptr;//关键:设置前置指针
TreeNode* cur = root;
while(cur || !st.empty())
{
//1、左路节点
while(cur)
{
st.push(cur);
cur = cur->left;
}
//2、左路节点的右子树
TreeNode* top = st.top();
//如果右为空,或者右为上一个访问的节点
if(top->right == nullptr || top->right == prev)
{
v.push_back(top->val);
st.pop();
prev = top;
}
else
{
cur = top->right;
}
}
return v;
}
};