1. 二叉树创建字符串
相信大部分人看了题目描述之后,都会和我一样一脸的懵逼。直到我看到了一个描述才恍然大悟
分为3种情况:
- 左右都为空 --省略
- 右为空,左不为空 – 省略
- 左为空,右不为空–不省略
这里复习一下二叉树的前序遍历、中序遍历、和后序遍历
前序的结果是:ABDEGCF
中序的结果是:DBGEACF
后序的结果是:DGEBFCA
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;
}
};
2. 二叉树的层序遍历
思路大致是这样的:
一个队列,接着一个levelSize
来记录每层有几个数据,如果这个数字是0,则表示这层的数据出完
出3将9和20带到队列,levelSize为2 。如此循环下去。
如果这个队列不为空,就一直循环下去,直到这个队列为空为止。
代码实现:
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()) // 如果队列不为空,就继续
{
// 通过levelSize控制一层一层的出
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;
}
};
3. 二叉树的层序遍历Ⅱ
这个题目与上一题目,差不多,我们只需要将最后的答案逆置即可
4. 二叉树的最近公共祖先
思路一:公共祖先的特征,如果一个在左子树,一个在右子树。那么这个节点就是公共祖先。
class Solution {
public:
bool isInTree(TreeNode* root, TreeNode* x) {
if (root == nullptr) {
return false;
}
return x == root
|| isInTree(root->left, x)
|| isInTree(root->right, x);
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == nullptr) {
return nullptr;
}
if (p == root || q == root) {
return root;
}
// 判断p节点是在root的左边还是右边
bool pInLeft = isInTree(root->left, p);
bool pInRight = !pInLeft;
// 判断q节点是在root的左边还是右边
bool qInLeft = isInTree(root->left, q);
bool qInRight = !qInLeft;
if ((pInLeft && qInRight) || (pInRight && qInLeft)) {
return root;
}
// 如果都在左边,则转换为在左树寻找公共祖先
else if (pInLeft && qInLeft) {
return lowestCommonAncestor(root->left, p, q);
}
else {
return lowestCommonAncestor(root->right, p, q);
}
}
};
思路二:公共祖先的特征,如果一个在我的左子树,一个在我的右子树,我就是公共祖先
如果是搜索二叉树可以优化到O(N)
- 一个比根小,一个比根大,根就是公共祖先
- 都比根小,递归左树查找
- 都比根大,递归右树查找
但是这个题目我们并不是搜索二叉树,要求优化到O(N)
这里只能使用另外一种思路,将p和q的路径求出来,放到容器当中,转换为路径相交问题
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();
}
};
上述代码的关键在于找到每个节点的路径
5. 二叉搜索树与双向链表
看到这个题目我们的第一个想法可能是把所有的节点拿出来,然后尾插到一个双向链表上,其实并没有这么简单,我们能够想到的出题人当然也能够想到。
这个题目有以下几个要求:
我们需要在原树上进行操作。
class Solution {
public:
void inorderTraversal(TreeNode* cur, TreeNode*& prev) {
if (cur == nullptr) {
return;
}
inorderTraversal(cur->left, prev);
cur->left = prev;
if (prev) {
prev->right = cur;
}
prev = cur;
inorderTraversal(cur->right, prev);
}
TreeNode* Convert(TreeNode* pRootOfTree) {
TreeNode* prev = nullptr;
inorderTraversal(pRootOfTree, prev);
TreeNode* head = pRootOfTree;
while (head && head->left) {
head = head->left;
}
return head;
}
};
以上的这幅图是精髓所在
6. 从前序与中序遍历序列构造二叉树
class Solution {
public:
TreeNode* _buildTree(vector<int>& preorder, vector<int>& inorder, int& previ, int inbegin, int inend) {
if (inbegin > inend) {
return nullptr;
}
TreeNode* root = new TreeNode(preorder[previ]);
// 分割出左右区间
int rooti = inbegin;
while (rooti <= inend) {
if (inorder[rooti] == preorder[previ]) {
break;
}
else {
rooti++;
}
}
++previ;
// [inbegin, rooti - 1], rooti, [rooti + 1, inend]
root->left = _buildTree(preorder, inorder, previ, inbegin, rooti - 1);
root->right = _buildTree(preorder, inorder, previ, 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);
}
};
7. 二叉树的前序遍历(非递归)
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
vector<int> v;
while (cur || !st.empty()) {
// 1. 开始访问一棵树
// 2. 左路节点
// 3. 左路节点的右子树
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. 二叉树的中序遍历(非递归)
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;
}
};
8. 二叉树的后序遍历(非递归)
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
vector<int> v;
TreeNode* prve = nullptr;
while (cur || !st.empty()) {
while (cur) {
st.push(cur);
cur = cur->left;
}
// 栈里面取到左路节点,左路节点的左子树访问完了
TreeNode* top = st.top();
// 右为空或者右已经访问过了,可以访问根节点
if (top->right == nullptr || top->right == prve) {
v.push_back(top->val);
st.pop();
prve = top;
}
else {
cur = top->right;
}
}
return v;
}
};
这里对非递归的三种代码进行对比: