本系列总计六篇文章,是 基于STL实现的笔试题常考七大基本数据结构 该文章在《代码随想录》和《labuladong的算法笔记》题目中的具体实践,每篇的布局是这样的:开头是该数据结构的总结,然后是在不同场景的应用,以及不同的算法技巧。本文是系列最后一篇,第六篇,介绍了树的相关题目,重点是要掌握二叉树、多叉树的构造、遍历(递归、非递归、层次),以及二叉树、二叉搜索树的属性,体会递归算法的本质是二叉树。
下面文章是在《代码随想录》和《labuladong的算法笔记》题目中的具体实践:
【笔记】数组
【笔记】链表
【笔记】哈希表
【笔记】字符串
【笔记】栈与队列
【笔记】二叉树
力扣上如何自己构造二叉树输入用例? | 代码随想录 (programmercarl.com)
1、把输入的 int 数组,先转化为二叉树节点数组,切记root绑定到新数组首元素
2、遍历,根据规则给左、右孩子赋值。节点 i 的左孩子下标 2 * i + 1,右孩子下标2 * i + 2。for中条件 i * 2 + 1 < vec.size(),不然会漏掉 i * 2 + 2节点值
3、层序遍历,打印
#include
using namespace std;
struct TreeNode {
/* data */
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
TreeNode* ConstructBinaryTree (const vector<int>& vec) {
if (vec.size() == 0) return nullptr;
vector<TreeNode*> vecTree (vec.size(), nullptr);
TreeNode* root = nullptr;
// 把输入的 int 数组,先转化为二叉树节点数组
for (int i = 0; i < vec.size(); i++) {
TreeNode* node = nullptr;
if (vec[i] != -1) {
node = new TreeNode(vec[i]);
}
vecTree[i] = node;
if (i == 0) {
root = node;
}
// cout << vec[i] << " ";
}
// cout << endl;
// 遍历,根据规则给左、右孩子赋值
for (int i = 0; i * 2 + 1 < vec.size(); i++) {
if (vecTree[i] != nullptr) {
vecTree[i]->left = vecTree[i * 2 + 1];
if (i * 2 + 2 < vec.size())
vecTree[i]->right = vecTree[i * 2 + 2];
}
}
return root;
}
// 层序遍历,按每层打印输出
void PrintBinaryTree (TreeNode* root) {
// 这一句多余了,遇见 -1 就直接返回了
// if (root = nullptr) return;
// 只有非空节点才入队列
queue<TreeNode*> que;
if (root != nullptr) que.push(root);
// 总结果集
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
// 每层的结果集
vector<int> level;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node != nullptr) {
level.push_back(node->val);
que.push(node->left);
que.push(node->right);
} else {
level.push_back(-1);
}
}
result.push_back(level);
}
for (int i = 0; i < result.size(); i++) {
for (int j = 0; j < result[i].size(); j++) {
cout << result[i][j] << " ";
}
cout << endl;
}
}
int main() {
// 用 -1 来表示nullptr
vector<int> vec = {4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8};
TreeNode* root = ConstructBinaryTree(vec);
PrintBinaryTree(root);
}
// 输入结果如下
// 4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8
// 打印结果如下
// 4
// 1 6
// 0 2 5 7
// -1 -1 -1 3 -1 -1 -1 8
// -1 -1 -1 -1 这四个多余的 -1 是叶子节点3和8的左右孩子
借助队列,见上段代码的 PrintBinaryTree()
函数
学会二叉树的层序遍历,可以一口气打完以下十题:
144. 二叉树的前序遍历 - 力扣(LeetCode)
94. 二叉树的中序遍历 - 力扣(LeetCode)
145. 二叉树的后序遍历 - 力扣(LeetCode)
589. N 叉树的前序遍历 - 力扣(LeetCode)
590. N 叉树的后序遍历 - 力扣(LeetCode)
根->左->右
/**
* 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) {
vector<int> result;
preorder(root, result);
return result;
}
// 要修改原本的vec需要传入引用,否则只是对复制的vec进行修改,原本的vec依旧为空
void preorder(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
// 前序位置
vec.push_back(root->val);
preorder(root->left, vec);
// 中序位置
preorder(root->right, vec);
// 后序位置
}
};
class Solution {
private:
vector<int> result;
public:
vector<int> preorder(Node* root) {
if (root == NULL) return {};
result.clear();
traverse(root);
return result;
}
void traverse(Node* root) {
if (root == NULL) return;
result.push_back(root->val);
for (Node* node : root->children) {
traverse(node);
}
}
};
左->根->右
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
inorder(root, result);
return result;
}
void inorder(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
inorder(cur->left, vec);
// 中序位置
vec.push_back(cur->val);
inorder(cur->right, vec);
}
};
左->右->根
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
postorder(root, result);
return result;
}
void postorder(TreeNode* cur, vector<int>& vec) {
if (cur == nullptr) return;
postorder(cur->left, vec);
postorder(cur->right, vec);
// 后序位置
vec.push_back(cur->val);
}
};
计算机处理递归用函数调用栈,因此理论上任何一个递归程序都可以用栈来迭代处理
会了前序就会后序(reverse),中序引入了指针,必须掌握。
先根入栈,然后注意入栈顺序是先右后左
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
if (root == nullptr) return{};
vector<int> result;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
// 根
TreeNode* node = st.top();
result.push_back(node->val);
st.pop();
// 入栈是右左,才能保证输出是左右
if (node->right) st.push(node->right);
if (node->left) st.push(node->left);
}
return result;
}
};
与前后续遍历的逻辑不同,注意指针先一路向左,然后处理中,然后向右。
这是因为前序遍历中访问节点(遍历节点)和处理节点(将元素放进result数组中)可以同步处理,但是中序就无法做到同步!
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
if (root == nullptr) return {};
stack<TreeNode*> st;
vector<int> result;
// 引入指针处理当前要输出的元素,栈则保存已遍历过的元素
TreeNode* cur = root;
while (cur != nullptr || !st.empty()) {
if (cur != nullptr) {
st.push(cur);
cur = cur->left; // 左
} else {
cur = st.top();
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
};
小trick,并不是真正左右根,而是借助先序遍历非递归根左右入栈,出栈结果是根右左,然后reverse成为左右根
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if (root == nullptr) return{};
vector<int> result;
stack<TreeNode*> st;
st.push(root);
while (!st.empty()) {
// 根
TreeNode* node = st.top();
result.push_back(node->val);
st.pop();
// 入栈是根左右,才能保证输出是根右左
if (node->left) st.push(node->left);
if (node->right) st.push(node->right);
}
reverse(result.begin(), result.end());
return result;
}
};
左右孩子节点不再空,分别指向其前驱和后继。知道了“前驱”和“后继”信息,就可以把二叉树看作一个链表结构,从而可以像遍历链表那样来遍历二叉树,进而提高效率。
这里例举了中序线索二叉树的方法
一个二叉树通过如下的方法“串起来”:
所有原本为空的右(孩子)指针改为指向该节点在中序序列中的后继,所有原本为空的左(孩子)指针改为指向该节点的中序序列的前驱。
同理,通过指向父节点的指针,就可以把二叉树看作一个链表结构,从而可以像遍历链表那样来遍历二叉树,进而提高效率。
101. 对称二叉树 - 力扣(LeetCode)
这道题目的本质是要比较两个树(这两个树是根节点的左右子树),遍历两棵树而且要比较内侧和外侧节点,所以准确的来说是一个树的遍历顺序是左右中,一个树的遍历顺序是右左中。
同类型题目:
compare()
同时操作两棵树进行比较,先给出所有分支判断的详细版本class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == nullptr) return true;
return compare(root->left, root->right);
}
bool compare(TreeNode* left, TreeNode* right) {
// 排除一空,一非空的情况
if (left == nullptr && right != nullptr) return false;
else if (left != nullptr && right == nullptr) return false;
// 全空返回true
else if (left == nullptr && right == nullptr) return true;
// 都不空,但数值不同
else if (left->val != right->val) return false;
// 此时就是:左右节点都不为空,且数值相同的情况
return compare(left->left, right->right) && compare(left->right, right->left);
}
};
class Solution {
public:
bool isSymmetric(TreeNode* root) {
if (root == nullptr) return true;
queue<TreeNode*> que;
que.push(root->left);
que.push(root->right);
while (!que.empty()) {
TreeNode* leftNode = que.front();
que.pop();
TreeNode* rightNode = que.front();
que.pop();
// 左节点为空、右节点为空,此时说明是对称的
if (!leftNode && !rightNode) continue;
// 一个空另一个不空,或者都不空但节点值不相同,说明不对称
if ((!leftNode || !rightNode || (leftNode->val != rightNode->val))) return false;
que.push(leftNode->left); // 加入左节点左孩子
que.push(rightNode->right); // 加入右节点右孩子
que.push(leftNode->right); // 加入左节点右孩子
que.push(rightNode->left); // 加入右节点左孩子
}
return true;
}
};
104. 二叉树的最大深度 - 力扣(LeetCode)
同类型题目:
class Solution {
public:
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
};
也可层序遍历,逐层累加
也可通过前序遍历真正求深度
class Solution {
public:
int result = 0;
void getdepth(TreeNode* node, int depth) {
result = depth > result ? depth : result; // 中
if (node->left == NULL && node->right == NULL) return ;
if (node->left) { // 左
depth++; // 深度+1
getdepth(node->left, depth);
depth--; // 回溯,深度-1
}
if (node->right) { // 右
depth++; // 深度+1
getdepth(node->right, depth);
depth--; // 回溯,深度-1
}
return ;
}
int maxDepth(TreeNode* root) {
if (root == NULL) return result;
getdepth(root, 1);
return result;
}
};
111. 二叉树的最小深度 - 力扣(LeetCode)
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == NULL) return 0;
int leftDepth = minDepth(root->left); // 左
int rightDepth = minDepth(root->right); // 右
// 中
// 当一个左子树为空,右不为空,这时并不是最低点
if (root->left == NULL && root->right != NULL) {
return 1 + rightDepth;
}
// 当一个右子树为空,左不为空,这时并不是最低点
if (root->left != NULL && root->right == NULL) {
return 1 + leftDepth;
}
int result = 1 + min(leftDepth, rightDepth);
return result;
}
};
class Solution {
public:
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
queue<TreeNode*> que;
que.push(root);
int result = 0;
while (!que.empty()) {
int size = que.size();
result++;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (!node->left && !node->right) return result;
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
222. 完全二叉树的节点个数 - 力扣(LeetCode)
同类题目:
class Solution {
public:
int countNodes(TreeNode* root) {
// 后序遍历
if (root == nullptr) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
};
110. 平衡二叉树 - 力扣(LeetCode)
相似题目:
class Solution {
public:
bool isBalanced(TreeNode* root) {
if (root == nullptr) return true;
return (abs(depth(root->left) - depth(root->right)) <= 1) && isBalanced(root->left) && isBalanced(root->right);
}
int depth(TreeNode* root) {
if (root == nullptr) return 0;
return 1 + max(depth(root->left), depth(root->right));
}
};
257. 二叉树的所有路径 - 力扣(LeetCode)
回溯法,先序遍历。vectorpath记录所有路径,用int的理由是放方便回溯,不管每 路径收集的数字是几位数,总之一定是int,所以就一次 pop_back就可以回溯。如果到叶子节点了,将int转化为string之后,结果存入result。
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
if (root == nullptr) return {};
vector<int> path;// 存放所有路径
vector<string> result;// 存放符合要求的路径
backtracking(root, path, result);
return result;
}
void backtracking(TreeNode* cur, vector<int>& path, vector<string>& result) {
// 根
path.push_back(cur->val);
// 到达叶子节点时,将path转换后存入result
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]);
result.push_back(sPath);
return;
}
// 左
if (cur->left) {
backtracking(cur->left, path, result);
path.pop_back();
}
// 右
if (cur->right) {
backtracking(cur->right, path, result);
path.pop_back();
}
}
};
404. 左叶子之和 - 力扣(LeetCode)
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (root == nullptr) return 0;
if (root->left == nullptr && root->right == nullptr) return 0;
return traverse(root);
}
int traverse(TreeNode* cur) {
int sum = 0;
if (cur->left != nullptr) sum += traverse(cur->left);
if (cur->right != nullptr) sum += traverse(cur->right);
if (cur->left != nullptr && cur->left->left == nullptr && cur->left->right == nullptr) sum += cur->left->val;
return sum;
}
};
513. 找树左下角的值 - 力扣(LeetCode)
class Solution {
public:
int findBottomLeftValue(TreeNode* root) {
if (root == nullptr) return 0;
int result = 0;
queue<TreeNode*> que;
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) result = node->val; // 不断覆盖每行第一个元素,最后留下的是最后一行第一个元素
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
}
return result;
}
};
112. 路径总和 - 力扣(LeetCode)
class Solution {
private:
bool traversal(TreeNode* cur, int count) {
if (!cur->left && !cur->right && count == 0) return true; // 遇到叶子节点,并且计数为0
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;
}
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == NULL) return false;
// 已经加入了根节点
return traversal(root, sum - root->val);
}
};
class Solution {
public:
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == nullptr) return false;
// 到达叶子节点并且路径和满足要求
if (!root->left && !root->right && targetSum == root->val) {
return true;
}
// 回溯隐藏在传入的参数中,因为把count - cur->left->val 直接作为参数传进去,函数结束,count的数值没有改变
return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
}
};
113. 路径总和 II - 力扣(LeetCode)
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
public:
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
result.clear();
path.clear();
if (root == nullptr) return {};
path.push_back(root->val);
backtracking(root, targetSum - root->val);
return result;
}
void backtracking(TreeNode* cur, int sum) {
if (!cur->left && !cur->right && sum == 0) {
result.push_back(path);
return; // 注意返回
}
if (!cur->left && !cur->right) return; // 遇到叶子节点而没有找到合适的边,直接返回
if (cur->left) {
sum -= cur->left->val;
path.push_back(cur->left->val);
backtracking(cur->left, sum);
path.pop_back();
sum += cur->left->val;
}
if (cur->right) {
sum -= cur->right->val;
path.push_back(cur->right->val);
backtracking(cur->right, sum);
path.pop_back();
sum += cur->right->val;
}
}
};
226. 翻转二叉树 - 力扣(LeetCode)
前序遍历,迭代法
层序遍历,迭代法
递归,前序遍历
翻转当前节点的左右子树,递归地翻转以当前节点的左右孩子为根节点的子树
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if (root == nullptr) return nullptr;
TreeNode* tmp;
tmp = root->left;
root->left = root->right;
root->right = tmp; // 可用swap函数
root->left = invertTree(root->left);
root->right = invertTree(root->right);
return root;
}
};
106. 从中序与后序遍历序列构造二叉树 - 力扣(LeetCode)
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return nullptr;
return build(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1);
}
TreeNode* build(vector<int>& inorder, int inorderStart, int inorderEnd, vector<int>& postorder, int postorderStart, int postorderEnd) {
if (postorderStart > postorderEnd) return nullptr;
// 先在后序遍历最末尾找根节点
int pivot = postorder[postorderEnd];
TreeNode* root = new TreeNode(pivot);
// 采用左闭右闭区间,若左子数只有一个元素直接返回
if (postorderStart == postorderEnd) return root;
// 然后在中序遍历位置找分割点
int index = 0;
for (int i = inorderStart; i <= inorderEnd; i++) { //由于采用左闭右闭区间,这里一定要加=
if (pivot == inorder[i])
index = i;
}
// 记录分割点左边的元素个数
int leftSize = index - inorderStart;
root->left = build(inorder, inorderStart, index - 1, postorder, postorderStart, postorderStart + leftSize - 1);
root->right = build(inorder, index + 1, inorderEnd, postorder, postorderStart + leftSize, postorderEnd - 1);
return root;
}
};
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)
class Solution {
public:
TreeNode* build(vector<int>& preorder, int preorderBegin, int preorderEnd, vector<int>& inorder, int inorderBegin, int inorderEnd) {
if (preorderBegin > preorderEnd) return nullptr;
int rootValue = preorder[preorderBegin];
TreeNode* root = new TreeNode(rootValue);
if (preorderBegin == preorderEnd) return root;
// 找中序分割点,一定要注意添加=
int index;
for(index = 0; index <= inorderEnd; index++) {
if (inorder[index] == rootValue) break;
}
int leftSize = index - inorderBegin;
// 递归
root->left = build(preorder, preorderBegin + 1, preorderBegin + leftSize, inorder, inorderBegin, index - 1);
root->right = build(preorder, preorderBegin + leftSize + 1, preorderEnd, inorder, index + 1, inorderEnd);
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
if (preorder.size() == 0 || inorder.size() == 0) return nullptr;
return build(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
}
};
654. 最大二叉树 - 力扣(LeetCode)
class Solution {
public:
TreeNode* constructMaximumBinaryTree(vector<int>& nums) {
if (nums.size() == 0) return nullptr;
return build(nums, 0, nums.size() - 1);
}
TreeNode* build(vector<int>& nums, int start, int end) {
if (start > end) return nullptr;
int index = findMax(nums, start, end);
TreeNode* root = new TreeNode(nums[index]);
if (start == end) return root;
root->left = build(nums, start, index - 1);
root->right = build(nums, index + 1, end);
return root;
}
int findMax(vector<int>& nums, int start, int end) {
int max = INT_MIN;
int index = start;
for (int i = index; i <= end; i++) {
if (max < nums[i]) {
index = i;
max = nums[i];
}
}
return index;
}
};
617. 合并二叉树 - 力扣(LeetCode)
class Solution {
public:
TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {
if (root1 == nullptr && root2 == nullptr) return nullptr;
if (root1 == nullptr) return root2;
if (root2 == nullptr) 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;
}
};
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
if (root == NULL) return "";
string result;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
TreeNode* node = que.front();
que.pop();
// 空节点返回 # ,非空节点返回数值
if (node == NULL) {
result.push_back('#');
result += ',';
} else {
result += to_string(node->val);
result += ',';
que.push(node->left);
que.push(node->right);
}
}
return result;
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
if (data.size() == 0) return NULL;
vector<TreeNode*> vec;
int j = 0;
while (j < data.size()) {
string stmp = "";
// 防止漏掉多位的int,比如123,12,3431
while (data[j] != ',') {
stmp += data[j];
j++;
}
// 把 string data 转化为 vector
if (stmp == "#") {
vec.push_back(nullptr);
} else {
int tmp = atoi(stmp.c_str());
TreeNode* node = new TreeNode(tmp);
vec.push_back(node);
}
j++;
}
// 根据vec构建二叉树,注意与 “根据输入的数组构造二叉树” 的区别
int pos = 1;
for (int i = 0; i < vec.size(); ++i) {
// 空节点不可能有孩子,直接跳过,进入下一循环
if (vec[i] == nullptr) continue;
vec[i]->left = vec[pos++];
vec[i]->right = vec[pos++];
}
return vec[0];
}
};
700. 二叉搜索树中的搜索 - 力扣(LeetCode)
递归
1、root为空或是找到,就要返回
2、注意return searchBST,递归带返回值,因为是找到了就返回
迭代,写法也很简单
class Solution {
public:
TreeNode* searchBST(TreeNode* root, int val) {
if (root == nullptr) return nullptr;
if (root->val < val) return searchBST(root->right, val);
if (root->val > val) return searchBST(root->left, val);
if (root->val == val) return root;
return nullptr;
}
};
98. 验证二叉搜索树 - 力扣(LeetCode)
class Solution {
public:
// 用来记录中序遍历的前一个节点
TreeNode* pre = nullptr;
bool isValidBST(TreeNode* root) {
if (root == nullptr) return true;
bool left = isValidBST(root->left);
if (pre != NULL && pre->val >= root->val) return false;
pre = root; // 注意不要漏这一行代码
bool right = isValidBST(root->right);
return left && right;
}
};
530. 二叉搜索树的最小绝对差 - 力扣(LeetCode)
class Solution {
private:
int result = INT_MAX;
TreeNode* pre = NULL;
void traversal(TreeNode* cur) {
if (cur == NULL) return;
traversal(cur->left); // 左
if (pre != NULL) { // 中
result = min(result, cur->val - pre->val);
}
pre = cur; // 记录前一个,注意不要漏这一行代码
traversal(cur->right); // 右
}
public:
int getMinimumDifference(TreeNode* root) {
traversal(root);
return result;
}
};
501. 二叉搜索树中的众数 - 力扣(LeetCode)
class Solution {
private:
TreeNode* pre = nullptr;
vector<int> result;
int maxCount = 0;
int count = 0;
public:
vector<int> findMode(TreeNode* root) {
result.clear();
traverse(root);
return result;
}
void traverse(TreeNode* cur) {
if (cur == nullptr) return;
traverse(cur->left);
if (pre == NULL) { // 第一个节点
count = 1; // 频率为1
} else if (pre->val == cur->val) { // 与前一个节点数值相同
count++;
} else { // 与前一个节点数值不同
count = 1;
}
pre = cur; // 更新上一个节点
// 如果和最大值相同,则加入
if (count == maxCount) result.push_back(cur->val);
// 如果计数大于最大值频率
if (count > maxCount) {
maxCount = count; // 更新最大频率
result.clear(); // 很关键的一步,不要忘记清空result,之前result里的元素都失效了
result.push_back(cur->val);
}
traverse(cur->right);
// return;
}
};
538. 把二叉搜索树转换为累加树 - 力扣(LeetCode)
class Solution {
public:
TreeNode* pre = nullptr;
TreeNode* convertBST(TreeNode* root) {
traverse(root);
return root;
}
void traverse(TreeNode* cur) {
if (cur == nullptr) return;
traverse(cur->right);
if (pre != nullptr) {
cur->val += pre->val;
}
pre = cur;
traverse(cur->left);
}
};
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL) return NULL;
if (root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root->left, p, q);
TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left != NULL && right != NULL) return root;
if (left == NULL && right != NULL) return right;
else if (left != NULL && right == NULL) return left;
else return NULL; // 左、右都为空
}
};
235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode)
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if (root == NULL || root == p || root == q) return root;
if (root->val > max(p->val, q->val)) {
return lowestCommonAncestor(root->left, p, q);
}
if (root->val < min(p->val, q->val)) {
return lowestCommonAncestor(root->right, p, q);
}
return root;
}
};
701. 二叉搜索树中的插入操作 - 力扣(LeetCode)
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
TreeNode* node = new TreeNode(val);
if (root == nullptr) return node;
if (root->val > val)
root->left = insertIntoBST(root->left, val);
if (root->val < val)
root->right = insertIntoBST(root->right, val);
return root;
}
};
450. 删除二叉搜索树中的节点 - 力扣(LeetCode)
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if (root == nullptr) return root;
// 寻找过程
if (root->val > key) root->left = deleteNode(root->left, key);
if (root->val < key) root->right = deleteNode(root->right, key);
// 找到
if (root->val == key) {
// 被删除的是叶子节点,直接删
if (root->left == nullptr && root->right == nullptr) {
delete root;
return nullptr;
}
// 被删除的节点只有左或右子树,直接代替
else if (root->left == nullptr) {
TreeNode* node = root->right;
delete root;
return node;
}
else if (root->right == nullptr) {
TreeNode* node = root->left;
delete root;
return node;
}
// 左右都不空,则将删除节点的左子树头结点(左孩子)放到删除节点的右子树的最左面节点的左孩子上,返回删除节点右孩子为新的根节点
else {
// 找右子树最左面节点
TreeNode* cur = root->right;
while (cur->left != nullptr) cur = cur->left;
// 被删除节点的左孩子接到cur的左子树
cur->left = root->left;
// 删除root,返回其右孩子
TreeNode* tmp = root->right;
delete root;
return tmp;
}
}
// 未找到
return root;
}
};
669. 修剪二叉搜索树 - 力扣(LeetCode)
针对下图中二叉树的情况:
如下代码相当于把节点0的右孩子(节点2)返回给上一层,
if (root->val < low)
return trimBST(root->right, low, high); // 寻找符合区间[low, high]的节点
然后如下代码相当于用节点3的左孩子 把下一层返回的 节点0的右孩子(节点2) 接住。
root->left = trimBST(root->left, low, high);
此时节点3的左孩子就变成了节点2,将节点0从二叉树中移除了。
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (root == nullptr) return root;
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;
}
};
108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)
class Solution {
public:
TreeNode* sortedArrayToBST(vector<int>& nums) {
if (nums.size() == 0) return nullptr;
return build(nums, 0, nums.size() - 1);
}
TreeNode* build(vector<int>& nums, int start, int end) {
if (start > end) return nullptr;
int mid = (end - start) / 2 + start;
TreeNode* node = new TreeNode(nums[mid]);
if (start == end)
return node;
node->left = build(nums, start, mid - 1);
node->right = build(nums, mid + 1, end);
return node;
}
};