一、二叉树的遍历方式
1.1 二叉树的前序遍历
递归:
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
vec.push_back(cur->val);
traversal(cur->left, vec);
traversal(cur->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
};
迭代:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> stk;
vector<int> res;
if (root == nullptr) return res;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top();
stk.pop();
res.push_back(cur->val);
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
}
return res;
}
};
前中后遍历统一风格迭代代码版本:前序遍历
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if (root == nullptr) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur != nullptr) {
stk.pop();
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
stk.push(cur); stk.push(nullptr);
} else {
stk.pop();
cur = stk.top(); stk.pop();
res.push_back(cur->val);
}
}
return res;
}
};
1.1.1 N 叉树的前序遍历
class Solution {
public:
vector<int> preorder(Node* root) {
vector<int> res;
if (!root) return res;
stack<Node*> stk;
stk.push(root);
while (!stk.empty()) {
Node* cur = stk.top(); stk.pop();
res.push_back(cur->val);
int cnt = cur->children.size();
for (int i = cnt - 1; i >= 0; i -- )
stk.push(cur->children[i]);
}
return res;
}
};
1.2 二叉树的中序遍历
递归:
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
traversal(cur->left, vec);
vec.push_back(cur->val);
traversal(cur->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
};
迭代:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
if (root == nullptr) return res;
TreeNode* cur = root;
while (cur != nullptr || !stk.empty())
if (cur != nullptr)
stk.push(cur), cur = cur->left;
else {
cur = stk.top(); stk.pop();
res.push_back(cur->val);
cur = cur->right;
}
return res;
}
};
前中后遍历统一风格迭代代码版本:中序遍历
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
if (root == nullptr) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur != nullptr) {
stk.pop(); // 弹出节点,避免重复操作
if (cur->right) stk.push(cur->right);
stk.push(cur); stk.push(nullptr);
if (cur->left) stk.push(cur->left);
} else {
stk.pop(); // 弹出空节点
cur = stk.top(); stk.pop();
res.push_back(cur->val);
}
}
return res;
}
};
1.3 二叉树的后序遍历
递归:
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
traversal(cur->left, vec);
traversal(cur->right, vec);
vec.push_back(cur->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
traversal(root, res);
return res;
}
};
迭代:
先序遍历顺序是根左右,调整一下代码后变成根右左,最后翻转变成左右根。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
if (root == nullptr) return res;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top(); stk.pop();
res.push_back(cur->val);
if (cur->left) stk.push(cur->left);
if (cur->right) stk.push(cur->right);
}
reverse(res.begin(), res.end());
return res;
}
};
前中后遍历统一风格迭代代码版本:后序遍历
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if (root == nullptr) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur != nullptr) {
stk.pop();
stk.push(cur); stk.push(nullptr);
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
} else {
stk.pop();
cur = stk.top(); stk.pop();
res.push_back(cur->val);
}
}
return res;
}
};
1.3.1 N 叉树的后序遍历
class Solution {
public:
vector<int> postorder(Node* root) {
vector<int> res;
if (!root) return res;
stack<Node*> stk;
stk.push(root);
while (!stk.empty()) {
Node* cur = stk.top(); stk.pop();
res.push_back(cur->val);
int cnt = cur->children.size();
for (int i = 0; i < cnt; i ++ )
stk.push(cur->children[i]);
}
reverse(res.begin(), res.end());
return res;
}
};
1.4 二叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (root == nullptr) return res;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
vector<int> vec;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front();
que.pop();
vec.push_back(cur->val);
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
res.push_back(vec);
}
return res;
}
};
1.4.1 二叉树的层序遍历 II
class Solution {
public:
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
vector<int> vec;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
vec.push_back(cur->val);
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
res.push_back(vec);
}
reverse(res.begin(), res.end());
return res;
}
};
1.4.2 二叉树的右视图
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> res;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (i == cnt - 1) res.push_back(cur->val);
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return res;
}
};
1.4.3 二叉树的层平均值
class Solution {
public:
vector<double> averageOfLevels(TreeNode* root) {
vector<double> res;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()) {
int cnt = que.size();
double sum = 0;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
sum += (double)cur->val;
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
res.push_back(sum / cnt);
}
return res;
}
};
1.4.4 N叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> res;
if (!root) return res;
queue<Node*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
vector<int> vec;
for (int i = 0; i < cnt; i ++ ) {
Node* cur = que.front(); que.pop();
vec.push_back(cur->val);
for (int i = 0; i < cur->children.size(); i ++ )
if (cur->children[i]) que.push(cur->children[i]);
}
res.push_back(vec);
}
return res;
}
};
1.4.5 在每个树行中找最大值
class Solution {
public:
vector<int> largestValues(TreeNode* root) {
vector<int> res;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size(), x = INT_MIN;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
x = max(x, cur->val);
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
res.push_back(x);
}
return res;
}
};
1.4.6 填充每个节点的下一个右侧节点指针
class Solution {
public:
Node* connect(Node* root) {
if (!root) return nullptr;
queue<Node*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
Node *pre, *cur;
for (int i = 0; i < cnt; i ++ ) {
if (i == 0) {
pre = que.front(); que.pop();
cur = pre;
} else {
cur = que.front(); que.pop();
pre->next = cur;
pre = cur;
}
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
pre->next = nullptr;
}
return root;
}
};
1.4.7 填充每个节点的下一个右侧节点指针II
这个题目和上一个题目的区别在于上一个题目说明了树是一个完美二叉树,但是解题思路是一样的,代码没有区别。
class Solution {
public:
Node* connect(Node* root) {
if (!root) return nullptr;
queue<Node*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
Node *pre, *cur;
for (int i = 0; i < cnt; i ++ ) {
if (!i) {
pre = que.front(); que.pop();
cur = pre;
} else {
cur = que.front(); que.pop();
pre->next = cur;
pre = cur;
}
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
pre->next = nullptr;
}
return root;
}
};
1.4.8 二叉树的最大深度
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
queue<TreeNode*> que;
que.push(root);
int depth = 0;
while (!que.empty()) {
int cnt = que.size();
depth ++ ;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return depth;
}
};
1.4.9 二叉树的最小深度
class Solution {
public:
int minDepth(TreeNode* root) {
if (!root) return 0;
queue<TreeNode*> que;
que.push(root);
int depth = 0;
while (!que.empty()) {
int cnt = que.size();
depth ++ ;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
if (!cur->left && !cur->right) return depth;
}
}
return depth;
}
};
二、二叉树的属性
2.1 对称二叉树
递归(后序遍历):
class Solution {
public:
bool compare(TreeNode* l, TreeNode* r) {
if (l && !r || !l && r) return false;
else if (!l && !r) return true;
else if (l->val != r->val) return false;
else return compare(l->left, r->right) && compare(l->right, r->left);
}
bool isSymmetric(TreeNode* root) {
if (!root) return true;
return compare(root->left, root->right);
}
};
迭代(使用队列,这个题将队列原封不动换成栈也可以):
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (!root) return true;
queue<TreeNode*> que;
que.push(root->left); que.push(root->right);
while (!que.empty()) {
TreeNode* l = que.front(); que.pop();
TreeNode* r = que.front(); que.pop();
if (!l && !r) continue;
if (!l || !r || (l->val != r->val)) return false; // 一个为空或者值不同
que.push(l->left); que.push(r->right);
que.push(l->right); que.push(r->left);
}
return true;
}
};
2.1.1 相同的树
递归:
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if (p && !q || !p && q) return false;
else if (!p && !q) return true;
else if (p->val != q->val) return false;
else return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
};
迭代:
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
queue<TreeNode*> que;
que.push(p); que.push(q);
while (!que.empty()) {
TreeNode* l = que.front(); que.pop();
TreeNode* r = que.front(); que.pop();
if (!l && r || l && !r) return false;
if (!l && !r) continue;
if (l->val != r->val) return false;
que.push(l->left); que.push(r->left);
que.push(l->right); que.push(r->right);
}
return true;
}
};
2.1.2 另一个树的子树
class Solution {
public:
bool isSame(TreeNode* l, TreeNode* r) {
if (!l && r || l && !r) return false;
else if (!l && !r) return true;
else if (l->val != r->val) return false;
else return isSame(l->left, r->left) && isSame(l->right, r->right);
}
bool dfs(TreeNode* l, TreeNode* r) {
if (!l) return false;
return isSame(l, r) || dfs(l->left, r) || dfs(l->right, r);
}
bool isSubtree(TreeNode* root, TreeNode* subRoot) {
return dfs(root, subRoot);
}
};
2.2 二叉树的最大深度
注意区分几个个概念:
- 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始),多使用前序遍历求解。
- 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数后者节点数(取决于高度从0开始还是从1开始),多使用后序遍历求解。
- 二叉树根节点的高度就是二叉树的最大深度
后序遍历:
class Solution {
public:
int getDepth(TreeNode* root) {
if (!root) return 0;
int l = getDepth(root->left);
int r = getDepth(root->right);
return 1 + max(l, r);
}
int maxDepth(TreeNode* root) {
return getDepth(root);
}
};
后序遍历精简版本:
class Solution {
public:
int maxDepth(TreeNode* root) {
if (!root) return 0;
return 1 + max(maxDepth(root->left), maxDepth(root->right));
}
};
前序遍历:
class Solution {
public:
int res = 0;
int maxDepth(TreeNode* root) {
if (!root) return res;
getDepth(root, 1);
return res;
}
void getDepth(TreeNode* root, int depth) {
res = max(res, depth); // 中
if (!root->left && !root->right) return;
if (root->left) { // 左
depth ++ ;
getDepth(root->left, depth);
depth -- ;
}
if (root->right) { // 右
depth ++ ;
getDepth(root->right, depth);
depth -- ;
}
return;
}
};
前序遍历精简版本:
class Solution {
public:
int res = 0;
int maxDepth(TreeNode* root) {
if (!root) return res;
getDepth(root, 1);
return res;
}
void getDepth(TreeNode* root, int depth) {
res = max(res, depth);
if (!root->left && !root->right) return;
if (root->left) getDepth(root->left, depth + 1);
if (root->right) getDepth(root->right, depth + 1);
return;
}
};
层序遍历:
class Solution {
public:
int maxDepth(TreeNode* root) {
int res = 0;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
res ++ ;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return res;
}
};
2.2.1 N 叉树的最大深度
递归:
class Solution {
public:
int maxDepth(Node* root) {
if (!root) return 0;
int res = 0;
for (int i = 0; i < root->children.size(); i ++ )
res = max(res, maxDepth(root->children[i]));
return res + 1;
}
};
迭代:
class Solution {
public:
int maxDepth(Node* root) {
int res = 0;
if (!root) return res;
queue<Node*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
res ++ ;
for (int i = 0; i < cnt; i ++ ) {
Node* cur = que.front(); que.pop();
for (int j = 0; j < cur->children.size(); j ++ )
que.push(cur->children[j]);
}
}
return res;
}
};
2.3 二叉树的最小深度
注意:二叉树的最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
后序遍历:
class Solution {
public:
int minDepth(TreeNode* root) {
return getDepth(root);
}
int getDepth(TreeNode* root) {
if (!root) return 0;
int l = getDepth(root->left);
int r = getDepth(root->right);
if (!root->left && root->right) return 1 + r;
if (root->left && !root->right) return 1 + l;
return 1 + min(l, r);
}
};
后序遍历精简版本:
class Solution {
public:
int minDepth(TreeNode* root) {
if (!root) return 0;
if (!root->left && root->right) return 1 + minDepth(root->right);
if (root->left && !root->right) return 1 + minDepth(root->left);
return 1 + min(minDepth(root->left), minDepth(root->right));
}
};
前序遍历:
class Solution {
public:
int res = 0;
int minDepth(TreeNode* root) {
if (!root) return 0;
res = INT_MAX;
getDepth(root, 1);
return res;
}
void getDepth(TreeNode* root, int depth) {
if (!root->left && !root->right) {
res = min(res, depth);
return;
}
if (root->left) getDepth(root->left, depth + 1);
if (root->right) getDepth(root->right, depth + 1);
return;
}
};
层序遍历:
class Solution {
public:
int minDepth(TreeNode* root) {
int res = 0;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
res ++ ;
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
if (!cur->left && !cur->right) return res;
}
}
return res;
}
};
2.4 完全二叉树的节点个数
递归:
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) return 0;
return getNodesNum(root);
}
int getNodesNum(TreeNode* root) {
if (!root) return 0;
int l = getNodesNum(root->left);
int r = getNodesNum(root->right);
return 1 + l + r;
}
};
递归精简版:
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
层序遍历:
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) return 0;
int res = 0;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
res ++ ;
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return res;
}
};
利用完全二叉树的性质:
完全二叉树只会存在两种情况:要么是一个满二叉树,要么最后一层未满。如果是一个满二叉树,就可以使用公式 2 h − 1 2^h - 1 2h−1 来计算该树的节点数目,其中 h h h 指该树的高度,且该树的根节点高度为 1 1 1。如果该树不是一个满二叉树,就分别递归其左右孩子,递归到一定深度就一定会有一棵左孩子或者有孩子为满二叉树的子树。
对于在完全二叉树中判断一个子树是否是一棵满二叉树,可以按照在完全二叉树中,如果递归向左遍历的深度等于递归向右遍历的深度,那说明就是满二叉树的方法来判断。
class Solution {
public:
int countNodes(TreeNode* root) {
if (!root) return 0;
TreeNode* lNode = root->left, *rNode = root->right;
int l = 0, r = 0;
while (lNode) lNode = lNode->left, l ++ ;
while (rNode) rNode = rNode->right, r ++ ;
if (l == r) return (2 << l) - 1;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
2.5 平衡二叉树
递归:
class Solution {
public:
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
int getHeight(TreeNode* root) {
if (!root) return 0;
int l = getHeight(root->left);
if (l == -1) return -1;
int r = getHeight(root->right);
if (r == -1) return -1;
if (abs(l - r) > 1) return -1;
return 1 + max(l, r);
}
};
迭代(效率很低):
使用一个后序遍历专门用于求解每个节点的高度
class Solution {
public:
bool isBalanced(TreeNode* root) {
if (!root) return true;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top(); st.pop();
if (abs(getDepth(cur->left) - getDepth(cur->right)) > 1) return false;
if (cur->left) st.push(cur->left);
if (cur->right) st.push(cur->right);
}
return true;
}
// 后序遍历求节点高度
int getDepth(TreeNode* root) {
if (!root) return 0;
stack<TreeNode*> stk;
stk.push(root);
int depth = 0, res = 0;
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur) {
stk.pop();
stk.push(cur); stk.push(nullptr);
depth ++ ;
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
} else {
stk.pop();
cur = stk.top();
stk.pop();
depth -- ;
}
res = max(res, depth);
}
return res;
}
};
2.6 二叉树的所有路径
提示:前序遍历、回溯
递归:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
vector<int> path;
if (!root) return res;
traversal(root, path, res);
return res;
}
void traversal(TreeNode* cur, vector<int>& path, vector<string>& res) {
path.push_back(cur->val); // 中
if (!cur->left && !cur->right) {
string sPath;
for (int i = 0; i < path.size() - 1; i ++ )
sPath += to_string(path[i]), sPath += "->";
sPath += to_string(path[path.size() - 1]); // 加入叶子节点
res.push_back(sPath);
return;
}
if (cur->left) traversal(cur->left, path, res), path.pop_back(); // 左
if (cur->right) traversal(cur->right, path, res), path.pop_back(); // 右
}
};
递归精简版本:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
string path;
if (!root) return res;
traversal(root, path, res);
return res;
}
void traversal(TreeNode* cur, string path, vector<string>& res) {
path += to_string(cur->val);
if (!cur->left && !cur->right) {
res.push_back(path);
return;
}
if (cur->left) traversal(cur->left, path + "->", res);
if (cur->right) traversal(cur->right, path + "->", res);
}
};
注意这里的回溯体现在了这两行代码中:
if (cur->left) traversal(cur->left, path + "->", res);
if (cur->right) traversal(cur->right, path + "->", res);
要直观体现回溯,可以将上面代码修改为如下:
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
path.pop_back(); // 回溯 '>'
path.pop_back(); // 回溯 '-'
}
if (cur->right) {
path += "->";
traversal(cur->right, path, result); // 右
path.pop_back(); // 回溯'>'
path.pop_back(); // 回溯 '-'
}
迭代:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> treeStk; // 保存遍历到的节点
stack<string> pathStk; // 保存遍历路径的节点
vector<string> res; // 保存结果
if (!root) return res;
treeStk.push(root);
pathStk.push(to_string(root->val));
while (!treeStk.empty()) {
TreeNode* cur = treeStk.top(); treeStk.pop();
string path = pathStk.top(); pathStk.pop();
if (!cur->left && !cur->right) res.push_back(path);
if (cur->right) {
treeStk.push(cur->right);
pathStk.push(path + "->" + to_string(cur->right->val));
}
if (cur->left) {
treeStk.push(cur->left);
pathStk.push(path + "->" + to_string(cur->left->val));
}
}
return res;
}
};
2.7 左叶子之和
左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点。判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子
递归:递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (!root) return 0;
if (!root->left && !root->right) return 0;
int l = sumOfLeftLeaves(root->left);
if (root->left && !root->left->left && !root->left->right)
l = root->left->val;
int r = sumOfLeftLeaves(root->right);
return l + r;
}
};
递归精简版:
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (!root) return 0;
int l = 0;
if (root->left && !root->left->left && !root->left->right)
l = root->left->val;
return l + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
}
};
迭代:
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (!root) return 0;
stack<TreeNode*> stk;
stk.push(root);
int res = 0;
while (!stk.empty()) {
TreeNode* cur = stk.top(); stk.pop();
if (cur->left && !cur->left->left && !cur->left->right)
res += cur->left->val;
if (cur->left) stk.push(cur->left);
if (cur->right) stk.push(cur->right);
}
return res;
}
};
2.8 找树左下角的值
递归:
class Solution {
public:
int maxDepth = INT_MIN, res;
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0); // 传入根节点和深度
return res;
}
void traversal(TreeNode* root, int depth) {
if (!root->left && !root->right) {
if (depth > maxDepth) // 这里就保证了同一层只会更新一次
maxDepth = depth, res = root->val;
return;
}
if (root->left) {
depth ++ ;
traversal(root->left, depth);
depth -- ;
}
if (root->right) {
depth ++ ;
traversal(root->right, depth);
depth -- ;
}
return;
}
};
递归精简版:
class Solution {
public:
int maxDepth = INT_MIN, res;
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0); // 传入根节点和深度
return res;
}
void traversal(TreeNode* root, int depth) {
if (!root->left && !root->right) {
if (depth > maxDepth) // 这里就保证了同一层只会更新一次
maxDepth = depth, res = root->val;
return;
}
if (root->left) traversal(root->left, depth + 1);
if (root->right) traversal(root->right, depth + 1);
return;
}
};
迭代:
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
if (!root )return 0;
int res = 0;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (!i) res = cur->val;
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
}
return res;
}
};
2.9 路经总和
递归(体现了回溯过程):
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
return traversal(root, targetSum - root->val);
}
bool traversal(TreeNode*cur, int count) {
if (!cur->left && !cur->right && !count) return true;
if (!cur->left && !cur->right) return false;
if (cur->left) {
count -= cur->left->val;
if (traversal(cur->left, count)) return true;
count += cur->left->val;
}
if (cur->right) {
count -= cur->right->val;
if (traversal(cur->right, count)) return true;
count += cur->right->val;
}
return false;
}
};
递归精简版:
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
if (!root->left && !root->right && targetSum == root->val) return true;
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
迭代:
class Solution {
public:
bool hasPathSum(TreeNode *root, int targetSum) {
if (!root) return false;
stack<pair<TreeNode*, int>> stk; // <节点指针,路径数值>
stk.push(pair<TreeNode*, int>(root, root->val));
while (!stk.empty()) {
pair<TreeNode*, int> cur = stk.top(); stk.pop();
if (!cur.first->left && !cur.first->right && targetSum == cur.second) return true;
if (cur.first->right) stk.push(pair<TreeNode*, int>(cur.first->right, cur.first->right->val + cur.second));
if (cur.first->left) stk.push(pair<TreeNode*, int>(cur.first->left, cur.first->left->val + cur.second));
}
return false;
}
};
2.10 路径总和Ⅱ
这个题目和上一个题目的区别在于要遍历整个树,找到所有路径,所以递归函数不要返回值!
这个题目使用迭代很麻烦。
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
res.clear();
path.clear();
if (!root) return res;
path.push_back(root->val);
traversal(root, targetSum - root->val);
return res;
}
void traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && !count) {
res.push_back(path);
return;
}
if (!cur->left && !cur->right) return;
if (cur->left) {
path.push_back(cur->left->val);
count -= cur->left->val;
traversal(cur->left, count);
count += cur->left->val;
path.pop_back();
}
if (cur->right) {
path.push_back(cur->right->val);
count -= cur->right->val;
traversal(cur->right, count);
count += cur->right->val;
path.pop_back();
}
return;
}
};
2.11 二叉树的最近公共祖先
这个题目做之前建议看看参考题解。
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == p || root == q || !root) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left && right) return root;
if (!left && right) return right;
else if (left && !right) return left;
else return nullptr; // 空节点
}
};
三、二叉树的构造与修改
3.1 翻转二叉树
递归:
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return root;
swap(root->left, root->right);
invertTree(root->left);
invertTree(root->right);
return root;
}
};
迭代(DFS,先中后这里都可以):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur) {
stk.pop();
if (cur->right) stk.push(cur->right);
if (cur->left) stk.push(cur->left);
stk.push(cur); stk.push(nullptr);
} else {
stk.pop();
cur = stk.top(); stk.pop();
swap(cur->left, cur->right);
}
}
return root;
}
};
迭代(BFS):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int cnt = que.size();
for (int i = 0; i < cnt; i ++ ) {
TreeNode* cur = que.front(); que.pop();
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
swap(cur->left, cur->right);
}
}
return root;
}
};
3.2 从中序与后序遍历序列构造二叉树
class Solution {
public:
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
if (!inorder.size() || !postorder.size()) return nullptr;
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
TreeNode *
traversal(vector<int> &inorder, int inorderBegin, int inorderEnd, vector<int> &postorder, int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return nullptr; // 始终维护的是左开右闭区间
int rootVal = postorder[postorderEnd - 1];
TreeNode *root = new TreeNode(rootVal); // 中间节点
if (postorderEnd - postorderBegin == 1) return root; // 叶子节点
int mid;
for (mid = inorderBegin; mid < inorderEnd; mid++)
if (inorder[mid] == rootVal) break;
// 切割中序数组
int leftInorderBegin = inorderBegin, leftInorderEnd = mid; // 左闭右开区间
int rightInorderBegin = mid + 1, rightInorderEnd = inorderEnd;
// 根据中序数组切割区间大小切割后序数组
int leftPostorderBegin = postorderBegin, leftPostorderEnd = postorderBegin + mid - inorderBegin;
int rightPostorderBegin = postorderBegin + (mid - inorderBegin), rightPostorderEdn = postorderEnd - 1;
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEdn);
return root;
}
};
简化版本:
class Solution {
public:
TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder) {
if (!inorder.size() || !postorder.size()) return nullptr;
return traversal(inorder, postorder, 0, inorder.size(), 0, postorder.size()); // 左闭右开区间
}
TreeNode *traversal(vector<int> &in, vector<int> &po, int il, int ir, int pl, int pr) {
if (pl == pr) return nullptr; // 左闭右开区间
int rootVal = po[pr - 1]; // 注意要减一
TreeNode *root = new TreeNode(rootVal);
if (pr - pl == 1) return root; // 叶子节点
int mid;
for (mid = il; mid < ir; mid++)
if (in[mid] == rootVal) break;
root->left = traversal(in, po, il, mid, pl, pl + mid - il);
root->right = traversal(in, po, mid + 1, ir, pl + mid - il, pr - 1); // 注意减一
return root;
}
};
3.3 从前序与中序遍历序列构造二叉树
class Solution {
public:
TreeNode* traversal(vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& preorder, int preorderBegin, int preorderEnd) {
if (preorderBegin == preorderEnd) return nullptr;
int rootVal = preorder[preorderBegin];
TreeNode* root = new TreeNode(rootVal);
if (preorderEnd - preorderBegin == 1) return root;
int mid;
for (mid = inorderBegin; mid < inorderEnd; mid ++ )
if (inorder[mid] == rootVal) break;
int leftInorderBegin = inorderBegin, leftInorderEdn = mid;
int rightInorderBegin = mid + 1, rightInorderEnd = inorderEnd;
int leftPreorderBegin = preorderBegin + 1, leftPreorderEnd = preorderBegin + 1 + mid - inorderBegin;
int rightPreorderBegin = preorderBegin + 1 + mid - inorderBegin, rightPreorderEnd = preorderEnd;
root->left = traversal(inorder, leftInorderBegin, leftInorderEdn, preorder, leftPreorderBegin, leftPreorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, preorder, rightPreorderBegin, rightPreorderEnd);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (!inorder.size() || !preorder.size()) return nullptr;
return traversal(inorder, 0, inorder.size(), preorder, 0, preorder.size());
}
};
简化版本:
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (!preorder.size() || !inorder.size()) return nullptr;
return traversal(preorder, inorder, 0, preorder.size() , 0, inorder.size()); // 左闭右开的区间
}
TreeNode* traversal(vector<int>& pre, vector<int>& in, int pl, int pr, int il, int ir) {
if (pl == pr) return nullptr; // 永远维护的是一个左闭右开的区间!
int rootVal = pre[pl];
TreeNode* root = new TreeNode(rootVal);
if (pr - pl == 1) return root; // 叶子节点
int mid;
for (mid = il; mid < ir; mid ++ )
if (in[mid] == rootVal) break;
root->left = traversal(pre, in, pl + 1, pl + 1 + mid - il, il, mid);
root->right = traversal(pre, in, pl + 1 + mid - il, pr, mid + 1, ir);
return root;
}
};
3.4 最大二叉树
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return traversal(nums, 0, nums.size()); // 维护的是一个左闭右开区间
}
TreeNode* traversal(vector<int>& nums, int l, int r) {
if (l >= r) return nullptr; // 左闭右开区间!
int index = l; // 最大值的下标
for (int i = l + 1; i < r; i ++ )
if (nums[i] > nums[index]) index = i;
TreeNode* root = new TreeNode(nums[index]);
root->left = traversal(nums, l, index); // 左闭右开区间!
root->right = traversal(nums, index + 1, r);
return root;
}
};
3.5 合并二叉树
前序遍历:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
root1->val += root2->val;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
};
中序遍历:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
root1->left = mergeTrees(root1->left, root2->left);
root1->val += root2->val;
root1->right = mergeTrees(root1->right, root2->right);
return root1;
}
};
后序遍历:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
root1->left = mergeTrees(root1->left, root2->left);
root1->right = mergeTrees(root1->right, root2->right);
root1->val += root2->val;
return root1;
}
};
层序遍历:
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
if (!t1) return t2;
if (!t2) return t1;
queue<TreeNode*> que;
que.push(t1); que.push(t2);
while (!que.empty()) {
TreeNode* cur1 = que.front(); que.pop();
TreeNode* cur2 = que.front(); que.pop();
cur1->val += cur2->val;
if (cur1->left && cur2->left)
que.push(cur1->left), que.push(cur2->left);
if (cur1->right && cur2->right)
que.push(cur1->right), que.push(cur2->right);
if (!cur1->left && cur2->left)
cur1->left = cur2->left;
if (!cur1->right && cur2->right)
cur1->right = cur2->right;
}
return t1;
}
};
四、二叉搜索树的属性
4.1 二叉搜索树中的搜索
递归:
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root || root->val == val) return root;
if (root->val > val) return searchBST(root->left, val);
if (root->val < val) return searchBST(root->right, val);
return nullptr;
}
};
迭代:
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
while (root) {
if (root->val > val) root = root->left;
else if (root->val < val) root = root->right;
else return root;
}
return nullptr;
}
};
4.2 验证二叉搜索树
迭代思路一:二叉搜索树的中序遍历是一个递增数组
注意一个点:搜索树里面不存在值相同的两个节点!
class Solution {
public:
vector<int> vec; // 存储中序遍历所有节点
bool isValidBST(TreeNode* root) {
vec.clear();
traversal(root);
for (int i = 1; i < vec.size(); i ++ )
if (vec[i] <= vec[i - 1]) return false;
return true;
}
void traversal(TreeNode* root) {
if (!root) return;
traversal(root->left);
vec.push_back(root->val);
traversal(root->right);
}
};
迭代思路二:在递归遍历的时候判断是否有序
注意几个点:
-
不能仅仅比较左节点小于中间节点,右节点大于中间节点就完事了,即如下代码:
if (root->val > root->left->val && root->val < root->right->val) return true; else return false;
我们要比较的是左子树所有节点小于中间节点,右子树所有节点大于中间节点。所以以上代码的判断逻辑是错误的。
-
样例中最小节点可能是 int 的最小值,如果这样使用最小的 int 来比较也是不行的。此时可以初始化比较元素为 long long 的最小值。那么最小节点值为 long long 该怎么办呢?可以考虑使用左子树的最左下角节点的值来比较。
class Solution {
public:
long long max_val = LONG_MIN;
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left);
if (max_val < root->val) max_val = root->val; // 中序遍历
else return false; // 元素相同也要返回false
bool right = isValidBST(root->right);
return left && right;
}
};
class Solution {
public:
TreeNode* pre = nullptr; // 用来记录前一个节点
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left);
if (pre && pre->val >= root->val) return false;
pre = root;
bool right = isValidBST(root->right);
return left && right;
}
};
迭代:
class Solution {
public:
bool isValidBST(TreeNode* root) {
if (!root) return true;
stack<TreeNode*> stk;
stk.push(root);
TreeNode* pre = nullptr;
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur) {
stk.pop();
if (cur->right) stk.push(cur->right);
stk.push(cur); stk.push(nullptr);
if (cur->left) stk.push(cur->left);
} else {
stk.pop();
cur = stk.top(); stk.pop();
if (pre && pre->val >= cur->val) return false;
pre = cur;
}
}
return true;
}
};
4.3 二叉搜索树的最小绝对差
注意二叉搜索树的中序遍历是一个有序数组
递归方法一:中序遍历二叉搜索树,将其节点转换为一个有序数组,再在有序数组上求解最小差值即可。
class Solution {
public:
vector<int> vec;
int getMinimumDifference(TreeNode* root) {
vec.clear();
traversal(root);
if (vec.size() < 2) return 0;
int res = INT_MAX;
for (int i = 1; i < vec.size(); i ++ )
res = min(res, vec[i] - vec[i - 1]);
return res;
}
void traversal(TreeNode* root) {
if (!root) return;
traversal(root->left);
vec.push_back(root->val);
traversal(root->right);
}
};
递归方法二:在中序遍历过程中直接找出最小差值
class Solution {
public:
int res = INT_MAX;
TreeNode* pre = nullptr;
int getMinimumDifference(TreeNode* root) {
traversal(root);
return res;
}
void traversal(TreeNode* cur) {
if (!cur) return;
traversal(cur->left); // 左
if (pre) res = min(res, cur->val - pre->val); // 中
pre = cur;
traversal(cur->right); // 右
}
};
迭代:
class Solution {
public:
int getMinimumDifference(TreeNode* root) {
if (!root) return 0;
stack<TreeNode*> stk;
stk.push(root);
TreeNode* pre = nullptr;
int res = INT_MAX;
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur) {
stk.pop();
if (cur->right) stk.push(cur->right);
stk.push(cur); stk.push(nullptr);
if (cur->left) stk.push(cur->left);
} else {
stk.pop();
cur = stk.top(); stk.pop();
if (pre) res = min(res, cur->val - pre->val);
pre = cur;
}
}
return res;
}
};
4.4 二叉搜索树中的众数
递归方法一:如果该二叉树是一棵普通二叉树的时候(通用解法)
class Solution {
public:
vector<int> findMode(TreeNode* root) {
unordered_map<int, int> mp; // (元素:出现频率)
vector<int> res;
if (!root) return res;
searchBST(root, mp);
vector<pair<int, int>> vec(mp.begin(), mp.end());
sort(vec.begin(), vec.end(), cmp);
res.push_back(vec[0].first);
for (int i = 1; i < vec.size(); i ++ )
if (vec[i].second == vec[0].second)
res.push_back(vec[i].first);
return res;
}
void searchBST(TreeNode* cur, unordered_map<int, int>& mp) {
if (!cur) return;
mp[cur->val] ++ ;
searchBST(cur->left, mp);
searchBST(cur->right, mp);
}
bool static cmp(const pair<int, int>& a, const pair<int, int>& b) {
return a.second > b.second;
}
};
递归思路二:对于一个二叉搜索树来说,没这么麻烦,仅需遍历一次树即可。
class Solution {
public:
int maxCount = 0, count = 0; // 最大频率,当前统计的频率
TreeNode* pre = nullptr;
vector<int> res;
vector<int> findMode(TreeNode* root) {
count = 0;
maxCount = 0;
pre = nullptr;
res.clear();
searchBST(root);
return res;
}
void searchBST(TreeNode* cur) {
if (!cur) return;
searchBST(cur->left);
if (!pre) count = 1; // 第一个节点
else if (pre->val == cur->val) count ++ ;
else count = 1; // 与前一个节点数值不同
pre = cur;
if (count == maxCount) res.push_back(cur->val);
if (count > maxCount) {
maxCount = count;
res.clear();
res.push_back(cur->val);
}
searchBST(cur->right);
return;
}
};
迭代方法:
class Solution {
public:
int maxCount = 0, count = 0;
TreeNode* pre = nullptr;
vector<int> res;
vector<int> findMode(TreeNode* root) {
maxCount = 0, count = 0;
pre = nullptr;
res.clear();
if (!root) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur) {
stk.pop();
if (cur->right) stk.push(cur->right);
stk.push(cur); stk.push(nullptr);
if (cur->left) stk.push(cur->left);
} else {
stk.pop();
cur = stk.top(); stk.pop();
if (!pre) count = 1;
else if (pre->val == cur->val) count ++ ;
else count = 1;
if (count == maxCount) res.push_back(cur->val);
else if (count > maxCount) {
maxCount = count;
res.clear();
res.push_back(cur->val);
}
pre = cur;
}
}
return res;
}
};
4.5 二叉搜索树的最近公共祖先
从上向下去递归遍历,第一次遇到 cur 节点是数值在 [p, q] 区间中,那么 cur 就是 p 和 q 的最近公共祖先.
递归:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
return traversal(root, p, q);
}
TreeNode* traversal(TreeNode* cur, TreeNode* p, TreeNode* q) {
if (!cur) return cur;
if (cur->val > p->val && cur->val > q->val) {
TreeNode* left = traversal(cur->left, p, q); // 遍历树一边的写法而不是整棵树
if (left) return left;
}
if (cur->val < p->val && cur->val < q->val) {
TreeNode* right = traversal(cur->right, p, q);
if (right) return right;
}
return cur;
}
};
精简后的递归代码:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root->val > p->val && root->val > q->val)
return lowestCommonAncestor(root->left, p, q);
else if (root->val < p->val && root->val < q->val)
return lowestCommonAncestor(root->right, p, q);
else return root;
}
};
迭代:
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
while (root) {
if (root->val > p->val && root->val > q->val)
root = root->left;
else if (root->val < p->val && root->val < q->val)
root = root->right;
else return root;
}
return nullptr;
}
};
五、二叉搜索树的修改与改造
5.1 二叉搜索树中的插入操作
有返回值的递归函数:
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (!root) {
TreeNode* cur = new TreeNode(val);
return cur;
}
if (root->val > val) root->left = insertIntoBST(root->left, val);
if (root->val < val) root->right = insertIntoBST(root->right, val);
return root;
}
};
无返回值的递归函数:
class Solution {
public:
TreeNode* pre; // 上一个节点
TreeNode* insertIntoBST(TreeNode* root, int val) {
pre = new TreeNode(0);
if (!root) root = new TreeNode(val);
travsersal(root, val);
return root;
}
void travsersal(TreeNode* cur, int val) {
if (!cur) {
TreeNode* node = new TreeNode(val);
if (val > pre->val) pre->right = node;
else pre->left = node;
return;
}
pre = cur;
if (cur->val > val) travsersal(cur->left, val);
if (cur->val < val) travsersal(cur->right, val);
return;
}
};
迭代:
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (!root) {
TreeNode* node = new TreeNode(val);
return node;
}
TreeNode* cur = root, *pre = root;
while (cur) {
pre = cur;
if (cur->val > val) cur = cur->left;
else cur = cur->right;
}
TreeNode *node = new TreeNode(val);
if (val < pre->val) pre->left = node;
else pre->right = node;
return root;
}
};
5.2 删除二叉搜索树中的节点
分情况讨论如下:
- 第一种情况:没找到删除的节点,遍历到空节点直接返回
- 找到删除的节点:
- 第二种情况:左右孩子都为空(叶子节点),直接删除节点, 返回NULL为根节点
- 第三种情况:删除节点的左孩子为空,右孩子不为空,删除节点,右孩子补位,返回右孩子为根节点
- 第四种情况:删除节点的右孩子为空,左孩子不为空,删除节点,左孩子补位,返回左孩子为根节点
- 第五种情况:左右孩子节点都不为空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return root;
if (root->val == key) {
if (!root->left && !root->right) { // 第二种情况
delete root;
return nullptr;
}
else if (!root->left) {// 第三种情况
TreeNode* tmp = root->right;
delete root;
return tmp;
}
else if (!root->right) { // 第四种情况
TreeNode* tmp = root->left;
delete root;
return tmp;
}
else { // 第五种情况
TreeNode* cur = root->right;
while (cur->left) cur = cur->left;
cur->left = root->left;
TreeNode* tmp = root->right;
delete root;
return tmp;
}
}
if (root->val > key) root->left = deleteNode(root->left, key);
if (root->val < key) root->right = deleteNode(root->right, key);
return root;
}
};
普通树节点的删除方式:
代码中目标节点(要删除的节点)被操作了两次:
- 第一次是和目标节点的右子树最左面节点交换。
- 第二次直接被 nullptr 覆盖了。
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return root;
if (root->val == key) {
if (!root->right) return root->left; // 这里第二次操作目标值:最终删除的作用
TreeNode* cur = root->right;
while (cur->left) cur = cur->left;
swap(root->val, cur->val); // 这里第一次操作目标值:交换目标值其右子树最左面节点。
}
root->left = deleteNode(root->left, key);
root->right = deleteNode(root->right, key);
return root;
}
};
迭代:
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return root;
TreeNode* cur = root, *pre = nullptr;
while (cur) {
if (cur->val == key) break;
pre = cur;
if (cur->val > key) cur = cur->left;
else cur = cur->right;
}
if (!pre) return deleteOneNode(cur); // 树只有头节点
if (pre->left && pre->left->val == key) pre->left = deleteOneNode(cur);
if (pre->right && pre->right->val == key) pre->right = deleteOneNode(cur);
return root;
}
TreeNode* deleteOneNode(TreeNode* target) {
if (!target) return target;
if (!target->right) return target->left;
TreeNode* cur = target->right;
while (cur->left) cur = cur->left;
cur->left = target->left;
return target->right;
}
};
5.3 修剪二叉搜索树
递归:
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (!root) return nullptr;
if (root->val < low) return trimBST(root->right, low, high);
if (root->val > high) return trimBST(root->left, low, high);
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
迭代:
三步:
- 将 root 移动到 [ l o w , h i g h ] [low, high] [low,high] 范围内,注意是左闭右闭区间
- 剪枝左子树
- 剪枝右子树
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (!root) return nullptr;
while (root && (root->val < low || root->val > high))
if (root->val < low) root = root->right;
else root = root->left;
TreeNode* cur = root;
while (cur) {
while (cur->left && cur->left->val < low)
cur->left = cur->left->right;
cur = cur->left;
}
cur = root;
while (cur) {
while (cur->right && cur->right->val > high)
cur->right = cur->right->left;
cur = cur->right;
}
return root;
}
};
5.4 将有序数组转换为二叉搜索树
递归:
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
return traversal(nums, 0, nums.size() - 1);
}
TreeNode* traversal(vector<int> &nums, int left, int right) {
if (left > right) return nullptr; // 左闭右闭区间
int mid = left + ((right - left) >> 1);
TreeNode* root = new TreeNode(nums[mid]);
root->left = traversal(nums, left, mid - 1);
root->right = traversal(nums, mid + 1, right);
return root;
}
};
迭代:
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if (!nums.size()) return nullptr;
TreeNode* root = new TreeNode(0);
queue<TreeNode*> nodeQue; // 存放遍历的节点
queue<int> leftQue, rightQue; // 存放左右区间下标
nodeQue.push(root);
leftQue.push(0), rightQue.push(nums.size() - 1);
while (!nodeQue.empty()) {
TreeNode* curNode = nodeQue.front(); nodeQue.pop();
int left = leftQue.front(); leftQue.pop();
int right = rightQue.front(); rightQue.pop();
int mid = left + ((right - left) >> 1);
curNode->val = nums[mid];
if (left <= mid - 1) {
curNode->left = new TreeNode(0);
nodeQue.push(curNode->left);
leftQue.push(left), rightQue.push(mid - 1);
}
if (right >= mid + 1) {
curNode->right = new TreeNode(0);
nodeQue.push(curNode->right);
leftQue.push(mid + 1), rightQue.push(right);
}
}
return root;
}
};
5.5 把二叉搜索树转换为累加树
反中序遍历,右中左,就可以求出累加值了。
递归:
class Solution {
public:
int pre = 0;
TreeNode* convertBST(TreeNode* root) {
pre = 0;
traversal(root);
return root;
}
void traversal(TreeNode* cur) {
if (!cur) return;
traversal(cur->right);
cur->val += pre, pre = cur->val;
traversal(cur->left);
}
};
迭代:
class Solution {
public:
TreeNode* convertBST(TreeNode* root) {
if (!root) return nullptr;
stack<TreeNode*> stk;
stk.push(root);
int pre = 0;
while (!stk.empty()) {
TreeNode* cur = stk.top();
if (cur) {
stk.pop();
if (cur->left) stk.push(cur->left);
stk.push(cur); stk.push(nullptr);
if (cur->right) stk.push(cur->right);
} else {
stk.pop();
cur = stk.top(); stk.pop();
cur->val += pre, pre = cur->val;
}
}
return root;
}
};