1、根据二叉树创建字符串
https://leetcode.cn/problems/construct-string-from-binary-tree


算法思路:左为空,右不为空,不能省略空括号。其他情况,空括号都可以省略。
参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
string tree2str(TreeNode* root)
{
string str;
if(root == nullptr)
return 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;
}
};
2、二叉树的层序遍历
https://leetcode.cn/problems/binary-tree-level-order-traversal

算法思路:在层序遍历的过程中,增加一个levelSize,记录每层的数据个数。树不为空的情况下,第一层levelSize = 1,循环控制,第一层出完了,第二层就都进队列了,队列中size就是第二层的数据个数。以此类推,假设levelSize为第n层的数据个数,因为层序遍历思想为当前层结点出队列,带入下一层结点(也就是子结点),循环控制第n层数据出完了,那么第n+1层结点都进队列了,队列的size就是下一层结点的levelSize。

参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> q;
int LevelSize;
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;
}
};
这道题还有一个变式题:
https://leetcode.cn/problems/binary-tree-level-order-traversal-ii

算法思路 :刚刚是让我们自上而下来层序遍历,现在要自下而上来层序遍历,该怎么做呢?其实就是把我们刚刚得到的二维数组逆置一下即可。
参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<vector<int>> levelOrderBottom(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();
}
reverse(vv.begin(), vv.end());
return vv;
}
};
3、二叉树的最近公共祖先
https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree

算法思路1:仔细观察一下,两个结点,最近公共祖先的特征就是一个结点在最近公共祖先的左边,一个结点在最近公共祖先的右边。比如,6和4的公共祖先有5和3,但是只有最近的公共祖先5满足6在左边,4在右边。其他公共祖先都不满足,只有最近公共祖先满足这个规则。

参考题解1:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
bool IsInTree(TreeNode* t, TreeNode* x)
{
if(t == nullptr)
return false;
return t == x
|| IsInTree(t->left, x)
|| IsInTree(t->right, x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
if(root == nullptr)
return nullptr;
if(root == p || root == q)
return root;
bool pInLeft = IsInTree(root->left, p);
bool pInRight = !pInLeft;
bool qInLeft = IsInTree(root->left, q);
bool qInRight = !qInLeft;
// 1、p和q分别在左和右,root就是最近公共祖先
// 2、p和q都在左,递归到左子树查找
// 3、p和q都在右,递归到右子树查找
if((pInLeft && qInRight) || (pInRight && qInLeft))
return root;
else if(pInLeft && qInLeft)
return lowestCommonAncestor(root->left, p, q);
else
return lowestCommonAncestor(root->right, p, q);
}
};
算法思路2 :如果能求出两个结点到根结点的路径,那么就可以转换为链表相交问题。比如:6到3的路径为6->5->3,4到3的路径为4->2->5->3,那么看做两个链表找交点,交点5就是最近公共祖先。如果是三叉链的话就很好获取路径,但是这里是二叉链,该怎么得到路径呢?这里就要用到前序遍历 的思路,用栈来记录查找路径,遇到结点先入栈,因为该结点就算不是我们要找的结点x,但是也有可能是分支结点。如果左右子树都没有结点x,那么说明入栈的结点不在路径上,所以要出栈,继续去其他分支路径进行查找。

参考题解2:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
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();
}
};
4、将二叉搜索树转化为排序的双向链表
https://leetcode.cn/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof

算法思路1:中序遍历搜索二叉树,遍历顺序是有序的,将二叉树的结点指针放到一个vector中,再把前后结点的连接关系进行修改。这个思路最简单,但是要消耗O(N)的空间复杂度。
算法思路2 :依旧中序遍历搜索二叉树,遍历顺序是有序的,遍历过程中修改左指针为前驱 ,右指针为后继。记录一个cur和prev,cur为当前中序遍历遍历到的结点,prev为上一个中序遍历的结点,cur->left指向prev,prev->right指向cur。

参考题解:
cpp
/*
// Definition for a Node.
class Node {
public:
int val;
Node* left;
Node* right;
Node() {}
Node(int _val) {
val = _val;
left = NULL;
right = NULL;
}
Node(int _val, Node* _left, Node* _right) {
val = _val;
left = _left;
right = _right;
}
};
*/
class Solution {
public:
void InOrderConvert(Node* cur, Node*& prev)
{
if(cur == nullptr)
return;
InOrderConvert(cur->left, prev);
// cur中序
// left指向中序前一个,左变成前驱
cur->left = prev;
// 中序前一个节点的右指向cur,右变成后继
if(prev)
prev->right = cur;
prev = cur;
InOrderConvert(cur->right, prev);
}
Node* treeToDoublyList(Node* root)
{
if(root == nullptr)
return nullptr;
Node* prev = nullptr;
InOrderConvert(root, prev);
Node* head = root;
while(head->left)
{
head = head->left;
}
//循环链表
head->left = prev;
prev->right = head;
return head;
}
};
5、从前序与中序遍历序列构造二叉树
https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal

算法思路:前序确定根,中序分割出左右子树。
参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* build(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)
{
if(preorder[prei] == inorder[rooti])
break;
else
rooti++;
}
prei++;
// [inbegin, rooti-1] rooti [rooti+1, inend]
root->left = build(preorder, inorder, prei, inbegin, rooti - 1);
root->right = build(preorder, inorder, prei, rooti + 1, inend);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder)
{
int i = 0;
return build(preorder, inorder, i, 0, inorder.size() - 1);
}
};
6、从中序与后序遍历序列构造二叉树
https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal

算法思路:和上一题类似,只不过这次是通过后序遍历序列确定根,先递归构建右子树,再构建左子树。
参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
TreeNode* build(vector<int>& inorder, vector<int>& postorder, int& posti, int inbegin, int inend)
{
if(inbegin > inend)
return nullptr;
TreeNode* root = new TreeNode(postorder[posti]);
int rooti = inbegin;
while(rooti <= inend)
{
if(inorder[rooti] == postorder[posti])
break;
else
rooti++;
}
posti--;
root->right = build(inorder, postorder, posti, rooti + 1, inend);
root->left = build(inorder, postorder, posti, inbegin, rooti - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder)
{
int i = postorder.size() - 1;
return build(inorder, postorder, i, 0, inorder.size() - 1);
}
};
7、二叉树的非递归前序遍历
https://leetcode.cn/problems/binary-tree-preorder-traversal

算法思路:先访问左路结点,再访问左路结点的右子树。

参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
TreeNode* cur = root;
vector<int> v;
while(cur || !st.empty())
{
//每次循环开始代表访问一棵树的开始
//访问左路结点,左路结点入栈
while(cur)
{
v.push_back(cur->val);
st.push(cur);
cur = cur->left;
}
//取一个左路结点的右子树出来访问
TreeNode* top = st.top();
st.pop();
//循环子问题的方式访问右子树
cur = top->right;
}
return v;
}
};
8、二叉树的非递归中序遍历
https://leetcode.cn/problems/binary-tree-inorder-traversal

算法思路:中序遍历和前序遍历思想是类似的,就是访问根的时候不一样。
参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
TreeNode* cur = root;
vector<int> v;
while(cur || !st.empty())
{
while(cur)
{
st.push(cur);
cur = cur->left;
}
TreeNode* top = st.top();
st.pop();
v.push_back(top->val);
cur = top->right;
}
return v;
}
};
9、二叉树的非递归后序遍历
https://leetcode.cn/problems/binary-tree-postorder-traversal

算法思路:取到一个左路结点时,左子树已经访问过了。如果左路结点右子树不为空,右子树没有访问,那么上一个访问结点是左子树的根。如果左路结点右子树不为空,右子树已经访问过了,那么上一个访问结点是右子树的根。
参考题解:
cpp
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root)
{
stack<TreeNode*> st;
TreeNode* cur = root;
TreeNode* prev = nullptr;
vector<int> v;
while(cur || !st.empty())
{
while(cur)
{
st.push(cur);
cur = cur->left;
}
// 取一个左路结点的右子树出来访问,此时代表左路结点的左子树已经访问过了
TreeNode* top = st.top();
// 右子树为空或者上一个访问的结点是右子树的根,代表右子树也访问过了
// 可以访问当前根结点
if(top->right == nullptr || prev == top->right)
{
v.push_back(top->val);
st.pop();
prev = top;
}
else
{
// 循环子问题的方式访问右子树
cur = top->right;
}
}
return v;
}
};