目录
LeetCode222.完全二叉树的结点个数
给你一棵 完全二叉树 的根节点
root
,求出该树的节点个数。完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第
h
层,则该层包含1~ 2h
个节点。
思路:首先要明白什么是完全二叉树,简单来说,除了最后一层之外,其余层均为满的,并且最后一层的结点是从左往右以此排列的, 最后一层的结点个数是1~2^(深度)-1。
本题大体上说有三种解法,递归法、迭代法以及公式法。
1. 递归法
采用后序遍历的顺序依次扫描二叉树,最后统计总数即可。比较简单
int getSum(TreeNode* cur){
if(cur == NULL) return 0;
int leftSum = getSum(cur -> left);//左
int rightSum = getSum(cur -> right);//右
int result = 1 + leftSum + rightSum;//中
return result;
}
int countNodes(TreeNode* root) {
return getSum(root);
}
下面是精化后的代码,更加简短,但是理解可能有些抽象,如果不熟悉可以参考上面的代码。
int countNodes(TreeNode* root) {
if(root == NULL) return 0;
return 1 + countNodes(root -> left) + countNodes(root -> right);
}
2. 迭代法
迭代法采用了层序遍历的方法,在每层遍历元素的时候,count渐增,最后返回统计结果即可。
int countNodes(TreeNode* root) {
queue<TreeNode*> que;
int count = 0;
if(root != NULL) que.push(root);
while(!que.empty()){
int size = que.size();
for(int i = 0; i < size; i ++){
TreeNode* node = que.front();
que.pop();
count ++; //记录节点数量
if(node -> left) que.push(node -> left);
if(node -> right) que.push(node -> right);
}
}
return count;
}
3. 公式法
使用这种方法要明白,在完全二叉树中是存在满二叉树的影子的,当一棵树的左节点的深度和右节点的深度相等时,表示该树是棵完全二叉树,因此,只需要在完全二叉树中找寻满二叉树的子树,对于满二叉树的子树返回其个数为2^深度 - 1,其余的在递归过程中求得总的子结果返回得到总的结果即可。
int countNodes(TreeNode* root) {
if(root == NULL) return 0;
TreeNode* leftnode = root -> left;
TreeNode* rightnode = root -> right;
int left = 0, right = 0;
while(leftnode){//求左子树深度
leftnode = leftnode -> left;
left ++;
}
while(rightnode){//求右子树深度
rightnode = rightnode -> right;
right ++;
}
if(left == right){
return (2 << left) - 1;//2<<left相当于2^(left+1),用于求满二叉树的个数
}
return 1 + countNodes(root -> left) + countNodes(root -> right);
}
LeetCode110.平衡二叉树
给定一个二叉树,判断它是否是平衡二叉树
思路:平衡二叉树在本题中是指左右子树高度的绝对值不超过1的二叉树。关键就是在于求高度。
前面我们说过,求深度最好是使用前序遍历,求高度最好使用后序遍历,因此本题我们采用后序遍历来求解,分为递归法和迭代法。
1. 递归法
在递归过程中当遇到某一棵子树不满足平衡二叉树的条件时,标记为-1,立即返回,而左右子树的子树满足条件时,会返回左右子树的高度,最后再比较一下左右子树的高度差绝对值即可返回最终结果。
int getHeight(TreeNode* cur){
if(cur == NULL) return 0;
int leftHeight = getHeight(cur -> left);//左
if(leftHeight == -1) return -1;//左子树中存在了不满足题目中平衡二叉树的要求
int rightHeight = getHeight(cur -> right);//右
if(rightHeight == -1) return -1;//右子树中存在了不满足题目中平衡二叉树的要求
int result;//设置结果统计量
if(abs(leftHeight - rightHeight) > 1){
result = -1;
}else{
result = 1 + max(leftHeight, rightHeight);
}
return result;
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
下面的代码是对上面代码的精化。
int getHeight(TreeNode* cur){
if(cur == NULL) return 0;
int leftHeight = getHeight(cur -> left);//左
if(leftHeight == -1) return -1;//左子树中存在了不满足题目中平衡二叉树的要求
int rightHeight = getHeight(cur -> right);//右
if(rightHeight == -1) return -1;//右子树中存在了不满足题目中平衡二叉树的要求
return abs(leftHeight - rightHeight) > 1 ? -1 : 1 + max(leftHeight, rightHeight);
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
2. 迭代法
在迭代法中我们同样采用了后序遍历的方法。另外构造了一个函数专门求所给结点的最大深度,其实可以说就是求的高度,然后在另一个函数中遍历二叉树结点, 判断所给结点的左右子树是否满足平衡二叉树的条件。
int getDepth(TreeNode* cur){
stack<TreeNode*> st;
if(cur != NULL) st.push(cur);
int depth = 0; //记录深度
int result = 0;
while(!st.empty()){
TreeNode* node = st.top();
if(node != NULL){
st.pop();
st.push(node);
st.push(NULL); //中
depth ++;
if(node -> right) st.push(node -> right);//右
if(node -> left) st.push(node -> left);//左
}else{
st.pop();
node = st.top();
st.pop();
depth --;
}
result = result > depth ? result : depth;
}
return result;
}
bool isBalanced(TreeNode* root) {
if(root == NULL) return true;
stack<TreeNode*> st;
st.push(root);
while(!st.empty()){
TreeNode* node = st.top(); //中
st.pop();
if(abs(getDepth(node -> left) - getDepth(node -> right)) > 1) return false;
if(node -> right) st.push(node -> right); //右
if(node -> left) st.push(node -> left);//左
}
return true;
}
LeetCode257.二叉树的所有路径
给你一个二叉树的根节点
root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。
思路:这道题涉及到了回溯的概念,为了方便统计从根节点到叶子结点的路径,采用了前序遍历的方法。下面分为递归法以及迭代法。
1. 递归法
采用递归法的时候,需要考虑三要素:函数返回类型以及参数类型、终止条件、单次递归逻辑。
这里涉及了所传的二叉树结点,以及一个vector,string类型的容器来暂存路径,最后一个vector,string类型的result来存放所有路径。
首先是当递归到叶子结点时,需要将path中的所有元素取出,组合成完整路径,放入result中;完成后返回,进行左右子树的递归,这里注意在进入递归出来后需要回溯一下,将path中放入的元素删掉,否则会出错。
void traversal(TreeNode* cur, vector<int>& path, vector<string>& result){
path.push_back(cur -> val);//将cur的值放入路径中
if(cur -> left == NULL && cur -> right == NULL){//到达了叶子结点
string str;
for(int i = 0; i < path.size() - 1; i ++){
str += to_string(path[i]);
str += "->";
}
str += to_string(path[path.size() - 1]);//将最后的叶子结点的值加入路径中
result.push_back(str);
return;
}
if(cur -> left){
traversal(cur -> left, path, result);//左
path.pop_back();//回溯
}
if(cur -> right){
traversal(cur -> right, path, result);//右
path.pop_back();//回溯
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
if(root == NULL) return result;
traversal(root, path, result);
return result;
}
下面的代码是对上面代码的一个精化,path直接采用了string类型的数据,这里注意在进入递归的时候采用的是path+ “->”,并没有实际改变path的值,隐藏了回溯的过程,而且调用的时候是复制赋值,并不是引用,需要多体会体会,额外注意。
void traversal(TreeNode* cur, string path, vector<string>& result){
path += to_string(cur -> val);//将cur的值放入路径中
if(cur -> left == NULL && cur -> right == NULL){//到达了叶子结点
result.push_back(path);
return;
}
if(cur -> left){
traversal(cur -> left, path + "->", result);//左
}
if(cur -> right){
traversal(cur -> right, path + "->", result);//右
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
string path;
if(root == NULL) return result;
traversal(root, path, result);
return result;
}
2. 迭代法
这里的迭代法采用了两个栈来分别存放遍历结点以及暂时的路径,采用了前序遍历的方法,注意何时需要将路径加入result。
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> st;//存放结点
stack<string> sPath;//存放路径
vector<string> result;//存放最终结果
if(root == NULL) return result;
st.push(root);
sPath.push(to_string(root -> val));
while(!st.empty()){
TreeNode* node = st.top(); st.pop();
string path = sPath.top(); sPath.pop();//中
if(node -> left == NULL && node -> right == NULL){//遇到了叶子结点
result.push_back(path);
}
if(node -> left){//左
st.push(node -> left);
sPath.push(path + "->" + to_string(node -> left -> val));
}
if(node -> right){//右
st.push(node -> right);
sPath.push(path + "->" + to_string(node -> right -> val));
}
}
return result;
}
LeetCode404.左叶子之和
给定二叉树的根节点
root
,返回所有左叶子之和。
思路:求左叶子的和,首先需要注意的是只能通过结点的父节点来判断该结点是否为左叶子结点。
这里采用了后序遍历的递归方法以及前序遍历的迭代方法进行讲解(其实对于迭代方法,前中后序都可以)。
1. 递归法
当结点为空或者本身是叶子结点时,直接返回,然后求得左子树中的左叶子结点的和,再求得右子树中的左叶子结点的和,最后返回总和即可,这里需要注意判断一个结点是否为左叶子结点的条件。
int sumOfLeftLeaves(TreeNode* root) {
//排除空树以及只有根节点的情况
if(root == NULL) return 0;
if(root -> left == NULL && root -> right == NULL) return 0;
int leftSum = sumOfLeftLeaves(root -> left);//左
if(root -> left != NULL && root -> left -> left == NULL && root -> left -> right == NULL){
leftSum = root -> left -> val;
}
int rightSum = sumOfLeftLeaves(root -> right);//右
return leftSum + rightSum;//中
}
下面的代码是对上面代码的精化。
int sumOfLeftLeaves(TreeNode* root) {
//排除空树以及只有根节点的情况
if(root == NULL) return 0;
int leftSum = 0;
if(root -> left != NULL && root -> left -> left == NULL && root -> left -> right == NULL){
leftSum = root -> left -> val;
}
return leftSum + sumOfLeftLeaves(root -> left) + sumOfLeftLeaves(root -> right);
}
2. 迭代法
采用前序遍历的方法,在遍历每一个结点的时候进行相应的条件判断,最后返回总结果即可。
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;
int result = 0;
if(root != NULL) st.push(root);
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;
}
感谢你的阅读,希望我的文章能够给你帮助,如果有帮助,麻烦点赞加收藏,或者点点关注,非常感谢。
如果有什么问题欢迎评论区讨论!