前序遍历: 先遍历根节点,然后再分别遍历左节点和右节点。(根左右
)
中序遍历: 先遍历左节点,然后再遍历根节点,最后遍历右节点。(左根右
)
后序遍历: 先遍历左节点,然后再遍历右节点,最后遍历根节点。(左右根
)
层次遍历:一层一层
从左至右遍历数据。
前序遍历代码(C++):
void preOrder(TreeNode* root, vector<int>& res)
{
if (root != nullptr)
{
res.push_back(root->val); // 处理节点
preOrder(root->leftchild);
preOrder(root->rightchild);
}
}
中序遍历代码(C++):
void inOrder(TreeNode* root, vector<int>& res)
{
if (root != nullptr)
{
inOrder(root->leftchild);
res.push_back(root->val); // 处理节点
inOrder(root->rightchild);
}
}
后序遍历代码(C++):
void postOrder(TreeNode* root, vector<int>& res)
{
if (root != nullptr)
{
postOrder(root->leftchild);
postOrder(root->rightchild);
res.push_back(root->val); // 处理节点
}
}
前序遍历代码(C++):
vector<int> preOrder(TreeNode* root)
{
vector<int> res;
if (!root) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
res.push_back(node->val); // 处理节点
if (node->right) stk.push(node->right);
if (node->left) stk.push(node->left);
}
return res;
}
中序遍历代码(C++):
vector<int> inOrder(TreeNode* root)
{
stack<TreeNode*> stk;
vector<int> res;
while (root || !stk.empty()) {
if (root) {
stk.push(root);
root = root->left;
} else {
root = stk.top();
stk.pop();
res.push_back(root->val); // 处理节点
root = root->right;
}
}
return res;
}
后序遍历代码(C++):
vector<int> postOrder(TreeNode* root)
{
vector<int> res;
if (!root) return res;
stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
res.push_back(node->val); // 处理节点
if (node->left) stk.push(node->left);
if (node->right) stk.push(node->right);
}
reverse(res.begin(), res.end());
return res;
}
层序遍历代码(C++):
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> que;
que.push(root);
vector<int> tmp;
while (!que.empty()) {
tmp.clear();
int size = que.size();
while (size > 0) {
TreeNode* node = que.front();
que.pop();
tmp.push_back(node->val); // 处理节点
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
size--;
}
res.push_back(tmp);
}
return res;
}
前序遍历代码(C++):
vector<int> preOrder(TreeNode* root)
{
vector<int> res;
TreeNode *p1 = root, *p2 = nullptr;
while (p1) {
if (p1->left) {
p2 = p1->left;
while (p2->right && p2->right != p1) {
p2 = p2->right;
}
if (p2->right == nullptr) {
res.push_back(p1->val);
p2->right = p1;
p1 = p1->left;
continue;
}
p2->right = nullptr;
} else {
res.push_back(p1->val);
}
p1 = p1->right;
}
return res;
}
中序遍历代码(C++):
vector<int> inOrder(TreeNode* root)
{
vector<int> res;
TreeNode *p1 = root, *p2 = nullptr;
while (p1) {
if (p1->left) {
p2 = p1->left;
while (p2->right && p2->right != p1) {
p2 = p2->right;
}
if (p2->right == nullptr) {
p2->right = p1;
p1 = p1->left;
continue;
}
p2->right = nullptr;
res.push_back(p1->val);
} else {
res.push_back(p1->val);
}
p1 = p1->right;
}
return res;
}
后序遍历代码(C++):
void addPath(vector<int> &vec, TreeNode *root) {
int count = 0;
while (root != nullptr) {
++count;
vec.push_back(root->val);
root = root->right;
}
reverse(vec.end() - count, vec.end());
}
vector<int> postOrder(TreeNode *root) {
vector<int> res;
TreeNode *p1 = root, *p2 = nullptr;
while (p1) {
if (p1->left) {
p2 = p1->left;
while (p2->right && p2->right != p1) {
p2 = p2->right;
}
if (p2->right == nullptr) {
p2->right = p1;
p1 = p1->left;
continue;
}
p2->right = nullptr;
addPath(res, p1->left);
}
p1 = p1->right;
}
addPath(res, root);
return res;
}
难度: 简单
题目表述:
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
代码(C++):
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr) return root;
TreeNode* left = invertTree(root->left);
TreeNode* right = invertTree(root->right);
root->left = right;
root->right = left;
return root;
}
};
题解: 递归
难度: 简单
题目表述:
给你一个二叉树的根节点 root , 检查它是否轴对称。
代码(C++):
class Solution {
public:
// 迭代
bool check(TreeNode* p, TreeNode* q) {
queue<TreeNode*> que; //stack 也可
que.push(p);
que.push(q);
while (!que.empty()) {
p = que.front(); que.pop();
q = que.front(); que.pop();
if (!p && !q) continue;
if (!p || !q || p->val != q->val) return false;
que.push(p->left);
que.push(q->right);
que.push(p->right);
que.push(q->left);
}
return true;
}
// 递归
bool check(TreeNode* p, TreeNode* q) {
if (!p && !q) return true;
if (!p || !q) return false;
return p->val == q->val && check(p->left, q->right) && check(p->right, q->left);
}
bool isSymmetric(TreeNode* root) {
return check(root, root);
}
};
题解:
左子树的左孩子 == 右子树的右孩子
左子树的右孩子 == 右子树的左孩子
难度: 中等
题目表述:
给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:
创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树 。
代码(C++):
class Solution {
public:
// 递归
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
return construct(nums, 0, nums.size() - 1);
}
TreeNode* construct(vector<int>& nums, int left, int right) {
if (left > right) return nullptr;
int best = left;
for (int i = left + 1; i <= right; i++) {
if (nums[i] > nums[best]) {
best = i;
}
}
TreeNode* node = new TreeNode(nums[best]);
node->left = construct(nums, left, best - 1);
node->right = construct(nums, best + 1, right);
return node;
}
// 单调栈
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
vector<TreeNode*> tree(nums.size());
vector<int> stk;
for (int i = 0; i < nums.size(); i++) {
tree[i] = new TreeNode(nums[i]);
while (!stk.empty() && nums[stk.back()] < nums[i]) {
tree[i]->left = tree[stk.back()];
stk.pop_back();
}
if (!stk.empty()) {
tree[stk.back()]->right = tree[i];
}
stk.push_back(i);
}
return tree[stk.front()];
}
};
题解: 递归 / 单调栈
难度: 简单
题目表述:
给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
代码(C++):
class Solution {
public:
// 自顶向下的递归 前序 求深度
bool isBalanced(TreeNode* root) {
if (!root) return true;
return abs(height(root->left) - height(root->right)) <= 1 && isBalanced(root->left) && isBalanced(root->right);
}
int height(TreeNode* root) {
if (!root) return 0;
return max(height(root->left), height(root->right)) + 1;
}
// 自底向上的递归 后序 求高度
bool isBalanced(TreeNode* root) {
return height(root) >= 0;
}
int height(TreeNode* root) {
if (!root) return 0;
int leftHeight = height(root->left);
int rightHeight = height(root->right);
if (leftHeight == -1 || rightHeight == -1 || abs(leftHeight - rightHeight) > 1)
return -1;
else
return max(leftHeight, rightHeight) + 1;
}
};
题解:
自顶向下的递归(前序 求深度),类似于二叉树的前序遍历,从根开始。对于同一个节点,函数
height 会被height和isBalanced重复调用,时间复杂度高O(n^2)。
自底向上的递归(后序 求高度),类似于二叉树的后序遍历,到根结束。每个节点的计算高度和判断是否平衡都只需要处理一次,时间复杂度O(n)。
max(left, right)+1记录高度
难度: 简单
题目表述:
给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
代码(C++):
class Solution {
public:
// 深度优先搜索
int minDepth(TreeNode* root) {
if (!root) {
return 0;
}
int left = minDepth(root->left);
int right = minDepth(root->right);
return (!left || !right) ? (left + right) + 1 : min(left, right) + 1;
}
// 广度优先搜索
int minDepth(TreeNode* root) {
if (!root) {
return 0;
}
queue<pair<TreeNode*, int>> que;
que.emplace(root, 1);
while (!que.empty()) {
TreeNode* node = que.front().first;
int depth = que.front().second;
que.pop();
if (!node->left && !node->right) return depth;
if (node->left) que.emplace(node->left, depth + 1);
if (node->right) que.emplace(node->right, depth + 1);
}
return 0;
}
};
题解:
深度优先搜索:时间复杂度O(N),空间复杂度O(H) ,平均情况下树的高度与节点数的对数正相关 即O(logN)。用栈递归实现。
广度优先搜索:时间复杂度O(N),空间复杂度O(N)。一层一层遍历的性质保证了最先搜索到的叶子节点的深度一定最小。用队列循环实现。
难度: 简单
题目表述:
给定一个二叉树,找出其最大深度。二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。
代码(C++):
class Solution {
public:
// 深度优先搜索
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
// 广度优先搜索
int maxDepth(TreeNode* root) {
if (!root) return 0;
queue<TreeNode*> que;
que.push(root);
int depth = 0;
while (!que.empty()) {
int size = que.size();
while (size > 0) {
TreeNode* node = que.front();
que.pop();
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
size--;
}
depth++;
}
return depth;
}
};
题解:
深度优先搜索:时间复杂度O(N),空间复杂度O(H) ,平均情况下树的高度与节点数的对数正相关 即O(logN)。用栈递归实现。
广度优先搜索:时间复杂度O(N),空间复杂度O(N)。一次拿出一层所有节点。用队列循环实现。
难度: 中等
题目表述:
给你一棵 完全二叉树 的根节点 root ,求出该树的节点个数。
代码(C++):
class Solution {
public:
// 递归 后序遍历
int countNodes(TreeNode* root) {
if (!root) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
// 迭代 层序遍历
int countNodes(TreeNode* root) {
queue<TreeNode*> q;
if (root) q.push(root);
int count = 0;
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
count++;
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
return count;
}
// 完全二叉树特性 满二叉树:2^depth-1
int countNodes(TreeNode* root) {
if (!root) return 0;
TreeNode* left = root->left;
TreeNode* right = root->right;
int lDepth = 0, rDepth = 0;
while (left) {
left = left->left;
lDepth++;
}
while (right) {
right = right->right;
rDepth++;
}
if (lDepth == rDepth) return (2 << lDepth) - 1;
return countNodes(root->left) + countNodes(root->right) + 1;
}
};
题解:
完全二叉树只有两种情况,1:就是满二叉树,2:最后一层叶子节点没有满。
1.可以直接用 2^depth - 1 来计算,注意这里根节点深度为1。
2.分别递归左孩子和右孩子,递归到某一深度一定会有左孩子或者右孩子为满二叉树,然后依然可以按照情况1来计算。
时间复杂度:logn*logn | 层数(while循环找深度) * 每次缩减一半(只遍历了不满的那一半边)
空间复杂度:logn | 递归深度
难度: 简单
题目表述:
给你一个二叉树的根节点 root ,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
代码(C++):
class Solution {
public:
void dfs(TreeNode* root, string path, vector<string>& paths) {
if (!root) {
return;
}
path += to_string(root->val);
if (!root->left && !root->right) {
paths.push_back(path);
return;
}
dfs(root->left, path + "->", paths);
dfs(root->right, path + "->", paths);
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> paths;
dfs(root, "", paths);
return paths;
}
};
题解:
难度: 简单
题目表述:
给定二叉树的根节点 root ,返回所有左叶子之和。
代码(C++):
class Solution {
public:
// 递归
int sumOfLeftLeaves(TreeNode* root) {
if (!root) return 0;
int sum = 0;
if (root->left && !root->left->left && !root->left->right)
sum = root->left->val;
return sum + sumOfLeftLeaves(root->left) + sumOfLeftLeaves(root->right);
}
// 迭代
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL) return 0;
st.push(root);
int result = 0;
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
result += node->left->val;
}
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
}
return result;
}
};
题解:
平时我们解二叉树的题目时,已经习惯了通过节点的左右孩子判断本节点的属性,而本题我们要通过节点的父节点判断本节点的属性。
难度: 中等
题目表述:
给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
代码(C++):
class Solution {
public:
int ans = 0;
int maxDepth = 0;
// 递归
void dfs(TreeNode* root, int depth) {
if (!root) return;
if (!root->left && !root->right) {
if (depth > maxDepth) {
maxDepth = depth;
ans = root->val;
}
return;
}
dfs(root->left, depth + 1);
dfs(root->right, depth + 1);
}
int findBottomLeftValue(TreeNode* root) {
dfs(root, 1);
return ans;
}
// 迭代 层序
int findBottomLeftValue(TreeNode* root) {
int ans;
queue<TreeNode*> que;
if (root) que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (i == 0) ans = node->val;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return ans;
}
};
题解:
遍历顺序无所谓,因为本题没有 中间节点的处理逻辑,只要左优先就行,然后记录深度最大的叶子节点,此时就是树的最后一行最左边的值。
难度: 简单
题目表述:
给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
代码(C++):
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (!root) return false;
if (!root->left && !root->right) {
return root->val == targetSum;
}
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
题解:
难度: 中等
题目表述:
给定两个整数数组 preorder 和 inorder ,其中 preorder 是二叉树的先序遍历, inorder 是同一棵树的中序遍历,请构造二叉树并返回其根节点。
代码(C++):
class Solution {
public:
// 递归
unordered_map<int, int> index;
TreeNode* build(vector<int>& preorder, int pre_l, int pre_r, vector<int>& inorder, int in_l, int in_r) {
if (pre_l > pre_r) return nullptr;
int root_val = preorder[pre_l];
int in_root = index[root_val];
TreeNode* root = new TreeNode(root_val);
int left_subtree_size = in_root - in_l;
root->left = build(preorder, pre_l + 1, pre_l + left_subtree_size, inorder, in_l, in_root - 1);
root->right = build(preorder, pre_l + left_subtree_size + 1, pre_r, inorder, in_root + 1, in_r);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = preorder.size();
for (int i = 0; i < n; i++) {
index[inorder[i]] = i;
}
return build(preorder, 0, n - 1, inorder, 0, n - 1);
}
// 迭代
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (!preorder.size()) return nullptr;
stack<TreeNode*> stk;
TreeNode* root = new TreeNode(preorder[0]);
stk.push(root);
int j = 0;
for (int i = 1; i < preorder.size(); i++) {
TreeNode* node = stk.top();
if (node->val != inorder[j]) {
node->left = new TreeNode(preorder[i]);
stk.push(node->left);
} else {
while (!stk.empty() && stk.top()->val == inorder[j]) {
node = stk.top();
stk.pop();
j++;
}
node->right = new TreeNode(preorder[i]);
stk.push(node->right);
}
}
return root;
}
};
题解: 递归 / 迭代
迭代(用栈实现):在前序遍历的顺序中,只有左节点的情况下,这些节点的顺序和它们在中序遍历中出现的顺序一定是相反的。
难度: 中等
题目表述:
给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。
代码(C++):
class Solution {
public:
unordered_map<int, int> map;
TreeNode* tree(vector<int>& inorder, vector<int>& postorder, int in_l, int in_r, int po_l, int po_r) {
if (in_l > in_r || po_l > po_r) return nullptr;
int root_val = postorder[po_r];
int root_idx = map[root_val];
int left_len = root_idx - in_l;
TreeNode* root = new TreeNode(root_val);
root->left = tree(inorder, postorder, in_l, root_idx - 1, po_l, po_l + left_len - 1);
root->right = tree(inorder, postorder, root_idx + 1, in_r, po_l + left_len, po_r - 1);
return root;
}
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int n = inorder.size();
for (int i = 0; i < n; i++) {
map[inorder[i]] = i;
}
return tree(inorder, postorder, 0, n - 1, 0, n - 1);
}
};
题解:
难度: 简单
题目表述:
给你两棵二叉树: root1 和 root2 。合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
代码(C++):
class Solution {
public:
// 递归 前序
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
TreeNode* root = new TreeNode(root1->val + root2->val);
root->left = mergeTrees(root1->left, root2->left);
root->right = mergeTrees(root1->right, root2->right);
return root;
}
// 迭代 层序
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (!root1) return root2;
if (!root2) return root1;
queue<TreeNode*> q;
q.push(root1);
q.push(root2);
while (!q.empty()) {
TreeNode* node1 = q.front(); q.pop();
TreeNode* node2 = q.front(); q.pop();
node1->val += node2->val;
if (node1->left && node2->left) {
q.push(node1->left);
q.push(node2->left);
}
if (node1->right && node2->right) {
q.push(node1->right);
q.push(node2->right);
}
if (!node1->left && node2->left) {
node1->left = node2->left;
}
if (!node1->right && node2->right) {
node1->right = node2->right;
}
}
return root1;
}
};
题解:
难度: 简单
题目表述:
给定二叉搜索树(BST)的根节点 root 和一个整数值 val。你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。
代码(C++):
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (!root) return nullptr;
if (root->val == val) {
return root;
}
return searchBST(val < root->val ? root->left : root->right, val);
}
TreeNode *searchBST(TreeNode *root, int val) {
while (root) {
if (val == root->val) {
return root;
}
root = val < root->val ? root->left : root->right;
}
return nullptr;
}
};
题解: 二叉搜索树是一个有序树
若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
它的左、右子树也分别为二叉搜索树
一般二叉树,递归过程中还有回溯的过程,例如走一个左方向的分支走到头了,那么要调头,再走右分支。而对于二叉搜索树,不需要回溯的过程,因为节点的有序性就帮我们确定了搜索的方向。
难度: 中等
题目表述:
有效 二叉搜索树定义如下:节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。
代码(C++):
class Solution {
public:
// 1.验证二叉搜索树,相当于判断 中序 遍历是否是递增的
long long maxVal = LONG_MIN;
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left);
if (root->val <= maxVal) return false;
maxVal = root->val;
bool right = isValidBST(root->right);
return left && right;
}
// 如果测试数据中有 longlong的最小值,可以取前一节点的数值来比较
TreeNode* pre = nullptr;
bool isValidBST(TreeNode* root) {
if (!root) return true;
bool left = isValidBST(root->left);
if (pre && root->val <= pre->val) return false;
pre = root;
bool right = isValidBST(root->right);
return left && right;
}
// 2.迭代 中序
bool isValidBST(TreeNode* root) {
TreeNode* pre = nullptr;
stack<TreeNode*> stk;
while (root || !stk.empty()) {
if (root) {
stk.push(root);
root = root->left;
} else {
root = stk.top(); stk.pop();
if (pre && root->val <= pre->val) return false;
pre = root;
root = root->right;
}
}
return true;
}
};
题解:
陷阱一:不能单纯的比较左节点小于中间节点,右节点大于中间节点就完事了,而是左子树都小于中间节点,右子树都大于中间节点。
陷阱二:在一个有序序列求最值的时候,最值可能就是int 或者 longlong的最小值。因此可以取前一节点的数值来比较 。
难度: 简单
题目表述:
给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。
代码(C++):
class Solution {
public:
int minDiff = INT_MAX;
int pre = -1; // or TreeNode* pre = nullptr;
void dfs(TreeNode* root) {
if (!root) return;
dfs(root->left);
if (pre != -1 && root->val - pre < minDiff) {
minDiff = root->val - pre;
}
pre = root->val;
dfs(root->right);
}
int getMinimumDifference(TreeNode* root) {
dfs(root);
return minDiff;
}
};
题解: 中序遍历二叉搜索树等于遍历有序数组
在有序数组求任意两数最小值差等价于相邻两数的最小值差。
需要用pre节点记录cur节点的前一个节点!!!
难度: 简单
题目表述:
给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
代码(C++):
class Solution {
public:
vector<int> res;
int max_count = 0;
int count = 1;
TreeNode* pre = nullptr;
void dfs(TreeNode* root) {
if (!root) return;
dfs(root->left);
if (pre != nullptr) {
if (pre->val == root->val) {
count++;
} else {
count = 1;
}
}
pre = root;
if (count > max_count) {
max_count = count;
res.clear();
res.push_back(root->val);
}
if (count == max_count) {
res.push_back(root->val);
}
dfs(root->right);
}
vector<int> findMode(TreeNode* root) {
dfs(root);
return res;
}
};
题解: 中序遍历二叉搜索树等于遍历有序数组
适时清空结果集
需要用pre节点记录cur节点的前一个节点!!!
难度: 中等
题目表述:
给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
代码(C++):
class Solution {
public:
// 1.递归 返回值TreeNode*
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) return left;
return right;
}
// 2.递归 返回值bool
TreeNode* ans;
bool dfs(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root) return false;
bool left = dfs(root->left, p, q);
bool right = dfs(root->right, p, q);
if (left && right || (root == p || root == q) && (left || right)){
ans = root;
}
return left || right || root == p || root == q;
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
dfs(root, p, q);
return ans;
}
// 3.哈希表
unordered_map<int, TreeNode*> fa;
unordered_map<int, bool> vis;
void dfs(TreeNode* root) {
if (root->left) {
fa[root->left->val] = root;
dfs(root->left);
}
if (root->right) {
fa[root->right->val] = root;
dfs(root->right);
}
}
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (!root) return NULL;
fa[root->val] = NULL;
dfs(root);
while (p) {
vis[p->val] = true;
p = fa[p->val];
}
while (q) {
if (vis[q->val]) return q;
q = fa[q->val];
}
return NULL;
}
};
题解: 递归 / 哈希表
后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
递归:左右子树分别去判断是否包含p or q,左右子树都包含就找到了
哈希表存储父节点
难度: 中等
题目表述:
给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
代码(C++):
class Solution {
public:
// 递归
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == p || root == q || !root) return root;
TreeNode* left = NULL, *right = NULL;
if (root->val > p->val && root->val > q->val)
return lowestCommonAncestor(root->left, p, q);
if (root->val < p->val && root->val < q->val)
return lowestCommonAncestor(root->right, p, q);
return root;
}
// 迭代
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 NULL;
}
};
题解:
不用使用回溯,二叉搜索树自带方向性,可以方便的从上向下查找目标区间,遇到目标区间内的节点,直接返回。
难度: 中等
题目表述:
给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。
代码(C++):
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if (!root) return new TreeNode(val);
if (root->val > val) {
root->left = insertIntoBST(root->left, val);
} else {
root->right = insertIntoBST(root->right, val);
}
return root;
}
};
题解:
搜索树是有方向的,可以根据插入元素的数值,决定递归方向。
二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整,而删除节点操作涉及到结构的调整。
难度: 中等
题目表述:
给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
代码(C++):
class Solution {
public:
// 递归
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return nullptr;
if (root->val == key) {
if (!root->right) return root->left;
TreeNode* node = root->right;
while (node->left) {
node = node->left;
}
node->left = root->left;
return root->right;
}
if (root->val > key) root->left = deleteNode(root->left, key);
if (root->val < key) root->right = deleteNode(root->right, key);
return root;
}
// 迭代
TreeNode* deleteOneNode(TreeNode* cur) {
if (!cur) return nullptr;
if (!cur->right) return cur->left;
TreeNode* node = cur->right;
while (node->left) {
node = node->left;
}
node->left = cur->left;
return cur->right;
}
TreeNode* deleteNode(TreeNode* root, int key) {
if (!root) return root;
TreeNode* cur = root;
TreeNode* pre = nullptr;
while (cur) {
if (cur->val == key) break;
pre = cur;
if (cur->val > key) cur = cur->left;
else cur = cur->right;
}
if (pre == nullptr) 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;
}
};
题解:
二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整,而删除节点操作涉及到结构的调整。
难度: 中等
题目表述:
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。
代码(C++):
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;
}
// 迭代
TreeNode* trimBST(TreeNode* root, int low, int high) {
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;
}
};
题解:
因为二叉搜索树的有序性,不需要使用栈模拟递归的过程。
难度: 简单
题目表述:
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
代码(C++):
class Solution {
public:
TreeNode* dfs(vector<int>& nums, int l, int r) {
if (l > r) return nullptr;
int mid = l + ((r - l) / 2);
TreeNode* root = new TreeNode(nums[mid]);
root->left = dfs(nums, l, mid - 1);
root->right = dfs(nums, mid + 1, r);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return dfs(nums, 0, nums.size() - 1);
}
// 迭代
TreeNode* sortedArrayToBST(vector<int>& nums) {
queue<TreeNode*> nq;
queue<int> lq, rq;
TreeNode* root = new TreeNode(0);
nq.push(root);
lq.push(0);
rq.push(nums.size() - 1);
while (!nq.empty()) {
TreeNode* node = nq.front(); nq.pop();
int l = lq.front(); lq.pop();
int r = rq.front(); rq.pop();
int mid = (l + r) / 2;
node->val = nums[mid];
if (l <= mid - 1) {
node->left = new TreeNode(0);
nq.push(node->left);
lq.push(l);
rq.push(mid - 1);
}
if (r >= mid + 1) {
node->right = new TreeNode(0);
nq.push(node->right);
lq.push(mid + 1);
rq.push(r);
}
}
return root;
}
};
题解:
通过递归函数的返回值来增删二叉树
难度: 中等
题目表述:
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。
代码(C++):
class Solution {
public:
int sum = 0;
// 递归
TreeNode* convertBST(TreeNode* root) {
if (!root) return nullptr;
convertBST(root->right);
root->val += sum;
sum = root->val;
convertBST(root->left);
return root;
}
// 迭代
TreeNode* convertBST(TreeNode* root) {
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) {
st.push(cur);
cur = cur->right;
} else {
cur = st.top();
st.pop();
cur->val += sum;
sum = cur->val;
cur = cur->left;
}
}
return root;
}
};
题解:
二叉搜索树可看作递增数组,采用反中序遍历(右->中->左)即可从后向前累加sum。
二叉树节点的深度: 指从根节点到该节点的最长简单路径边的条数。自顶向下 前序
二叉树节点的高度: 指从该节点到叶子节点的最长简单路径边的条数。自底向上 后序
最大二叉树用:递归 / 两侧单调递减栈
平衡二叉树的高度用:max(left, right)+1
寻找最小公共祖先:从底向上遍历,只能通过后序遍历,后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑。
/ 哈希存储父节点。
二叉搜索树:中序遍历二叉搜索树等于遍历有序数组
,建议使用两个前后指针作比较,即用pre节点记录cur节点的前一个节点!!!
TreeNode* pre = nullptr;
void dfs(TreeNode* root) {
if (!root) return;
dfs(root->left);
// 处理节点root
...
pre = root; // 当前节点赋值给pre
dfs(root->right);
}
二叉搜索树添加节点只需要在叶子上添加就可以的,不涉及到结构的调整,而删除节点操作涉及到结构的调整。
深度优先搜索 DFS:用 递归 / 栈迭代 实现。
广度优先搜索 BFS:用 队列 循环实现。
递归函数是否需要返回值:
left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中
if (递归函数(root->left)) return ;
if (递归函数(root->right)) return ;
注:在递归函数有返回值的情况下:如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)。
选择什么遍历顺序:
前序
,先构造中节点。一般是后序
,一般要通过递归函数的返回值再做中间节点的计算。中序
,因中序有序性。玩转 LeetCode 高频 100 题
https://blog.csdn.net/qq_45829112/article/details/123565427
LeetCode 刷题攻略