一、平衡二叉树
1.题目
Leetcode:第 110 题
给定一个二叉树,判断它是否是 平衡二叉树
示例 1:
输入:root = [3,9,20,null,null,15,7] 输出:true
示例 2:
输入:root = [1,2,2,3,3,null,null,4,4] 输出:false
示例 3:
输入:root = [] 输出:true
2.解题思路
使用递归法和迭代法遍历二叉树,对于每个节点,计算其左右子树的深度差。如果深度差大于1,则树不平衡,返回false。如果遍历结束,没有发现深度差大于1的情况,则树是平衡的,返回true。
3.实现代码
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
using namespace std;
// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
int val; // 存储节点的值。
TreeNode* left; // 指向该节点左子树的指针。
TreeNode* right; // 指向该节点右子树的指针。
// TreeNode的构造函数,用于创建一个TreeNode实例。
// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 一、判断平衡二叉树(递归法)
class Solution1 {
public:
// 定义名为getHeight的辅助函数,用于计算二叉树节点的高度。
// 同时,该函数会检查子树的高度差是否不超过1,以此判断树是否平衡。
// 函数接受一个参数:指向二叉树节点的指针node。
int getHeight(TreeNode* node) {
if (node == NULL) return 0; // 如果传入的节点为空,说明是一棵空树,返回高度0。
int leftHeiht = getHeight(node->left); // 递归地计算左子树节点的高度。
if (leftHeiht == -1) return -1; // 如果左子树高度计算结果为-1,说明左子树不平衡,直接返回-1。
int rightHeiht = getHeight(node->right);// 递归地计算右子树节点的高度。
if (rightHeiht == -1) return -1;// 如果右子树高度计算结果为-1,说明右子树不平衡,直接返回-1。
int heightDiff = abs(leftHeiht - rightHeiht);// 计算左右子树的高度差。
if (heightDiff > 1) return -1;// 如果高度差大于1,说明当前节点的子树不平衡,返回-1。
// 如果子树平衡,返回当前节点的高度,为左右子树的最大高度加1(当前节点本身的高度)。
return 1 + max(leftHeiht, rightHeiht);
}
// 定义名为isBalanced的成员函数,用于判断二叉树是否平衡。
// 函数接受一个参数:指向二叉树根节点的指针root。
bool isBalanced(TreeNode* root) {
// 调用getHeight函数计算根节点的高度,如果返回值为-1,说明树不平衡,返回false。
// 否则,返回true,表示树是平衡的。
return getHeight(root) == -1 ? false : true;
}
};
// 二、判断平衡二叉树(迭代法)
class Solution2 {
public:
// getDepth函数用于计算二叉树的深度。
// 函数接受一个参数:指向二叉树节点的指针cur。
int getDepth(TreeNode* cur) {
stack<TreeNode*> st; // 创建一个栈st,用于存储遍历过程中的节点。
if (cur != NULL) st.push(cur); // 如果当前节点cur不为空,则将其压入栈中。
int depth = 0; // 初始化深度计数器为0。
int result = 0; // 初始化最大深度计数器为0。
while (!st.empty()) {// 使用while循环遍历栈不为空时的所有节点。
TreeNode* node = st.top(); // 获取栈顶节点。
if (node != NULL) {// 如果节点不为空,则执行以下操作:
st.pop(); // 将当前节点从栈中弹出。
st.push(node); // 将当前节点压入栈中,以便后续处理。
st.push(NULL);// 将一个空指针压入栈中,作为左右子树遍历的分隔符。
depth++;// 深度计数器加1。
if (node->right) st.push(node->right); // 如果节点有右子节点,将其压入栈中。
if (node->left) st.push(node->left);// 如果节点有左子节点,将其压入栈中。
}
else { // 如果节点为空,说明已经到达了叶子节点的下方,执行以下操作:
st.pop();// 弹出空指针
node = st.top();//获取节点
st.pop();//弹出节点
depth--;// 节点计数器减1。
}
// 更新最大深度计数器,取当前最大值。
result = result > depth ? result : depth;
}
return result;// 返回最大深度。
}
// isBalanced函数用于判断二叉树是否平衡。
// 函数接受一个参数:指向二叉树根节点的指针root。
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st; // 创建一个栈st,用于存储遍历过程中的节点。
if (root == NULL) return true; // 如果根节点root为空,说明是一棵空树,返回true。
st.push(root); // 将根节点压入栈中。
while (!st.empty()) { // 使用while循环遍历栈不为空时的所有节点。
TreeNode* node = st.top(); // 获取栈顶节点
st.pop();// 弹出当前节点。
// 计算当前节点的左右子树深度差。
int depthDiff = abs(getDepth(node->left) - getDepth(node->right));
if (depthDiff > 1) { // 如果左右子树深度差大于1,说明树不平衡,返回false。
return false;
}
if (node->left) st.push(node->left); // 如果栈顶节点有左子节点,将其压入栈中。
if (node->right) st.push(node->right);// 如果栈顶节点有右子节点,将其压入栈中。
}
return true; // 如果遍历结束,说明树是平衡的,返回true。
}
};
//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
return new TreeNode(value);
}
// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
if (values.empty()) return NULL;
TreeNode* root = createNode(values[0]);
queue<TreeNode*> queueNode;
queueNode.push(root);
int i = 1;
while (!queueNode.empty()) {
TreeNode* node = queueNode.front();
queueNode.pop();
if (i < values.size()) {
node->left = createNode(values[i++]);
queueNode.push(node->left);
}
if (i < values.size()) {
node->right = createNode(values[i++]);
queueNode.push(node->right);
}
}
return root;
}
// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
for (int value : vec) {
cout << value << " ";
}
cout << endl;
}
// 主函数
int main() {
vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
TreeNode* root = buildTree(treeValues); // 构建二叉树
Solution1 s1;// 创建Solution类的实例
Solution2 s2;
int result1 = s1.isBalanced(root);// 传入二叉树的根节点
int result2 = s2.isBalanced(root);
cout << "判断平衡二叉树(递归法)结果是: " << result1 << endl;
cout << endl;
cout << "判断平衡二叉树(迭代法)结果是:" << result2 << endl;
cout << endl;
return 0;
}
二、二叉树的所有路径
1.题目
Leetcode:第 257 题
给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
示例 1:
输入:root = [1,2,3,null,5] 输出:["1->2->5","1->3"]
示例 2:
输入:root = [1] 输出:["1"]
2.解题思路
使用递归法和迭代法遍历二叉树所有节点,记录遍历的路径。
3.实现代码
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <string>
using namespace std;
// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
int val; // 存储节点的值。
TreeNode* left; // 指向该节点左子树的指针。
TreeNode* right; // 指向该节点右子树的指针。
// TreeNode的构造函数,用于创建一个TreeNode实例。
// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 一、二叉树的所有路径(递归法)
class Solution1 {
public:
// traversal函数用于递归遍历二叉树并收集所有从根到叶子节点的路径。
// cur是当前遍历到的节点,path是用于记录当前路径的容器,result是存储所有路径的容器。
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result) {
path.push_back(cur->val); // 将当前节点的值添加到路径容器的末尾,这样做是为了确保最后一个节点也会被加入到路径中。
if (cur->left == NULL && cur->right == NULL) { // 检查当前节点是否为叶子节点(没有左右子节点)。
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) {
traversal(cur->left, path, result); // 如果当前节点有左子节点,递归遍历左子树。
path.pop_back();// 在回溯时,从路径中移除当前节点,以便探索其他分支。
}
if (cur->right) {
traversal(cur->right, path, result); // 如果当前节点有右子节点,递归遍历右子树。
path.pop_back();// 同样,在回溯时从路径中移除当前节点。
}
}
// binaryTreePaths函数用于返回二叉树的所有根到叶子节点的路径。
// root是二叉树的根节点。
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;// 初始化一个空的字符串容器,用于存储所有路径的字符串表示。
vector<int> path;// 初始化一个空的整数容器,用于记录当前路径。
if (root == NULL) return result;// 如果根节点为空,则直接返回空的路径列表。
traversal(root, path, result);// 调用traversal函数开始递归遍历,传入根节点、路径容器和结果容器。
return result;// 返回包含所有路径的字符串容器。
}
};
// 一、二叉树的所有路径(迭代法)
class Solution2 {
public:
// binaryTreePaths函数用于返回二叉树的所有路径。
// root是二叉树的根节点。
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> treeSt; // 创建一个栈treeSt,用于保存树的遍历节点。
stack<string> pathSt;// 创建一个栈pathSt,用于保存遍历路径的节点。
vector<string> result;// 创建一个字符串向量result,用于保存最终路径集合。
if (root == NULL) return result;// 如果根节点为空,直接返回空的路径列表。
treeSt.push(root);// 将根节点入treeSt栈,同时将根节点的值入pathSt栈。
pathSt.push(to_string(root->val));
while (!treeSt.empty()) {// 使用while循环遍历栈不为空时的所有节点。
// 取出treeSt栈顶的节点,
TreeNode* node = treeSt.top();
treeSt.pop();
// 取出pathSt栈顶的路径字符串。
string path = pathSt.top();
pathSt.pop();
// 如果当前节点是叶子节点,将路径字符串添加到结果列表中。
if (node->left == NULL && node->right == NULL) {
result.push_back(path);
}
// 如果当前节点有右子节点,将其入treeSt栈,并更新对应的路径。
if (node->right) {
treeSt.push(node->right);
pathSt.push(path + "->" + to_string(node->right->val));
}
// 如果当前节点有左子节点,将其入treeSt栈,并更新对应的路径。
if (node->left) {
treeSt.push(node->left);
pathSt.push(path + "->" + to_string(node->left->val));
}
}
// 返回包含所有路径的字符串向量。
return result;
}
};
//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
return new TreeNode(value);
}
// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
if (values.empty()) return NULL;
TreeNode* root = createNode(values[0]);
queue<TreeNode*> queueNode;
queueNode.push(root);
int i = 1;
while (!queueNode.empty()) {
TreeNode* node = queueNode.front();
queueNode.pop();
if (i < values.size()) {
node->left = createNode(values[i++]);
queueNode.push(node->left);
}
if (i < values.size()) {
node->right = createNode(values[i++]);
queueNode.push(node->right);
}
}
return root;
}
// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
for (int value : vec) {
cout << value << " ";
}
cout << endl;
}
// 主函数
int main() {
vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
TreeNode* root = buildTree(treeValues); // 构建二叉树
Solution1 s1;// 创建Solution类的实例
Solution2 s2;
vector<string>result1 = s1.binaryTreePaths(root);// 传入二叉树的根节点
vector<string>result2 =s2.binaryTreePaths(root);
cout << "二叉树所有左子叶之和(递归法)是: " <<endl;
for (auto & s : result1) {
cout << s << endl;
}
cout << endl;
cout << "二叉树所有左子叶之和(迭代法)是: " <<endl;
for (auto& s : result1) {
cout << s << endl;
}
cout << endl;
return 0;
}
三、二叉树所有左子叶之和
1.题目
Leetcode:第 404 题
给定二叉树的根节点 root
,返回所有左叶子之和。
示例 1:
输入: root = [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24
示例 2:
输入: root = [1] 输出: 0
2.解题思路
使用递归法和迭代法遍历二叉树所有节点,找到所有左子叶并求和。
3.实现代码
#include <iostream>
#include <vector>
#include <stack>
#include <queue>
using namespace std;
// 定义一个结构体TreeNode,用于表示二叉树的节点。
struct TreeNode {
int val; // 存储节点的值。
TreeNode* left; // 指向该节点左子树的指针。
TreeNode* right; // 指向该节点右子树的指针。
// TreeNode的构造函数,用于创建一个TreeNode实例。
// 参数x是节点的值,left和right默认为NULL,表示没有左右子节点。
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
// 一、计算二叉树所有左子叶之和(递归法)
class Solution1 {
public:
// sumOfLeftLeaves函数用于计算二叉树中所有左叶子节点的值的和。
// 函数接受一个参数:指向二叉树根节点的指针root。
int sumOfLeftLeaves(TreeNode* root) {
if (root == NULL) return 0; // 如果根节点为空,返回0,因为没有任何叶子节点。
// 如果根节点的左右子节点都为空,说明根节点是叶子节点,但不是左叶子节点,返回0。
if (root->left == NULL && root->right == NULL) return 0;
// 递归地计算左子树中所有左叶子节点的值的和。
int leftValue = sumOfLeftLeaves(root->left);
// 检查当前节点的左子节点是否是叶子节点(没有左右子节点)。
// 如果是,更新leftValue为当前节点左子节点的值。
if (root->left && !root->left->left && !root->left->right) {
leftValue = root->left->val;
}
int rightValue = sumOfLeftLeaves(root->right);// 递归地计算右子树中所有左叶子节点的值的和。
int sum = leftValue + rightValue; // 计算左右子树中左叶子节点的值的和。
return sum;// 返回计算得到的和。
}
};
// 二、计算二叉树所有左子叶之和(迭代法)
class Solution2 {
public:
// sumOfLeftLeaves函数用于计算二叉树中所有左叶子节点的值的和。
// 函数接受一个参数:指向二叉树根节点的指针root。
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;// 创建一个栈st,用于在遍历过程中存储节点。
if (root == NULL) return 0;// 如果根节点为空,直接返回0,因为没有任何叶子节点。
st.push(root);// 将根节点入栈。
int result = 0; // 初始化结果变量为0。
while (!st.empty()) {// 使用while循环遍历栈不为空时的所有节点。
TreeNode* node = st.top(); // 取出栈顶的节点。
st.pop();// 弹出栈顶的节点。
// 如果当前节点的左子节点存在,并且左子节点没有左右子节点,即左子节点是叶子节点,
// 则将左子节点的值加到结果中。
if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
result += node->left->val;
}
if (node->left) st.push(node->left); // 如果当前节点有左子节点,将其入栈。
if (node->right) st.push(node->right);// 如果当前节点有右子节点,将其入栈。
}
// 返回计算得到的左叶子节点的值的和。
return result;
}
};
//测试
// 辅助函数,用于创建一个新的TreeNode
TreeNode* createNode(int value) {
return new TreeNode(value);
}
// 辅助函数,用于构建二叉树
TreeNode* buildTree(vector<int>& values) {
if (values.empty()) return NULL;
TreeNode* root = createNode(values[0]);
queue<TreeNode*> queueNode;
queueNode.push(root);
int i = 1;
while (!queueNode.empty()) {
TreeNode* node = queueNode.front();
queueNode.pop();
if (i < values.size()) {
node->left = createNode(values[i++]);
queueNode.push(node->left);
}
if (i < values.size()) {
node->right = createNode(values[i++]);
queueNode.push(node->right);
}
}
return root;
}
// 打印容器中的所有元素,用于验证测试结果
void printVector(const vector<int>& vec) {
for (int value : vec) {
cout << value << " ";
}
cout << endl;
}
// 主函数
int main() {
vector<int> treeValues = { 1, 2, 3, 4, 5, 6, 7 };// 定义二叉树的层序遍历结果,用于构建二叉树
TreeNode* root = buildTree(treeValues); // 构建二叉树
Solution1 s1;// 创建Solution类的实例
Solution2 s2;
int result1 = s1.sumOfLeftLeaves(root);// 传入二叉树的根节点
int result2 = s2.sumOfLeftLeaves(root);
cout << "二叉树所有左子叶之和(递归法)是: " << result1 << endl;
cout << endl;
cout << "二叉树所有左子叶之和(迭代法)是: " << result2 << endl;
cout << endl;
return 0;
}
ps:以上皆是本人在探索算法旅途中的浅薄见解,诚挚地希望得到各位的宝贵意见与悉心指导,若有不足或谬误之处,还请多多指教。