代码随想录算法训练营第17天 | 110.平衡二叉树 257. 二叉树的所有路径 404.左叶子之和

平衡二叉树

Alt
注意这道题是用高度来判断,因此递归实现的后序遍历就是最直接的。但是要注意递归函数的参数和返回值,我们需要对已经不平衡的情况进行标记(-1这种不会出现的值),同时还需要返回树高。

class Solution{
public:
	int getHeight(TreeNode* node){
		if(!node)  return 0;
		int leftheight = getHeight(node->left);  // 递归得到左子树高度
		if(leftheight == -1)  return -1;  // 如果返回-1,说明左子树已经不满足条件,继续返回-1即可
		int rightheight = getHeight(node->right);
		if(rightheight == -1)  return -1;
		if(abs(leftheight - rightheight) > 1)  return -1;  // 两边子树的高度差大于1,不满足平衡条件,返回-1
		return 1 + max(leftheight, rightheight);
	}
	bool isBalanced(TreeNode* root){
		return getHeight(root) != -1;  // 如果返回的不是-1,说明是平衡树
	}
};

本题也可以用迭代法做,即在每一个节点处比较其左右子树的高度差,但这种做法重复运算太多。。。就不在这里实现了

二叉树的所有路径

Alt

递归法

这道题很好的体现了递归与回溯的共生关系,我们也可以在递归函数的输入形参上做文章,实现更简洁的代码,隐式的回溯。
显式回溯的代码:

class Solution{
public:
	void traversal(TreeNode* node, vector<int>& path, vector<string>& result){
		path.push_back(node->val);  // 前序遍历,先处理遍历到的节点,中间节点,将节点加入路径
		if(!node->left && !node->right){  // 如果到达叶子节点,处理出一条路径加入结果数组
			string sPath;
			for(int i = 0; i < path.size() - 1; i++){
				sPath += path[i];
				sPath += "->";
			}
			sPath += path[path.size() - 1];
			result.push_back(sPath);
		}
		if(node->left){
			traversal(node->left, path, result);  // 左子树递归
			path.pop_back();  // 回溯
		}
		if(node->right){
			traversal(node->right, path, result);
			path.pop_back();
		}
	}
	vector<string> binaryTreePaths(TreeNode* root){
		vector<string> result;
		vector<int> path;
		traversal(root, path, result);
		return result;
	}
};

更简洁的版本:

class Solution{
public:
	void traversal(TreeNode* node, string path, vector<string>& result){
		path += to_string(node->val);  // 将当前节点加入路径中
		if(!node->right && !node->left){
			result.push_back(path);
			return;
		}
		if(node->left)  traversal(node->left, path + "->", result);
		if(node->right)  traversal(node->right, path + "->", result);
	}
	vector<string> binaryTreePaths(TreeNode* root){
		vector<string> result;
		string path;
		traversal(root, path, result);
		return result;
	}
};

表面上以上代码是没用回溯的,其实回溯隐藏在形参path + "->"的处理上,传入的参数是包含"->"的,但递归返回时 path 其实没有改变,这里暗含了回溯的思想。相当于如下的处理:

if(node->left){
	path += "->";
	traversal(node->left, path, result);
	path.pop_back();  // 回溯“>”
	path.pop_back();  // 回溯“-”
}

另外,为什么前者需要 pop_back 路径中的节点后者不需要呢?这里涉及 C++ 中的引用&。在第一个实现中形参 path 就是一个引用,也就是递归调用过程会一直改变 path 本身,所以需要在每次递归调用后回溯节点,但第二个实现中 path 就是一个普通形参,递归调用不会改变这一层递归函数的 path 变量本身。

迭代法

递归法中用的基本是标准的前序遍历,那么我们应该能同样使用借助栈的迭代法来平替递归法。
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径

class Solution{
public:
	vector<string> binaryTreePaths(TreeNode* root){
		vector<string> result;
		stack<TreeNode*> tree_st;
		stack<string> path_st;
		tree_st.push(root);
		path_st.push(to_string(root->val));
		while(!tree_st.empty()){
			TreeNode* node = tree_st.top();
			tree_st.pop();
			string path = path_st.top();
			path_st.pop();
			if(!node->left && !node->right){  // 遇到叶子节点了,加入一条路径
				result.push_back(path);
			}
			if(node->right){
				tree_st.push(node->right);
				path_st.push(path + "->" + to_string(node->right->val));
			}
			if(node->left){
				tree_st.push(node->left);
				path_st.push(path + "->" + to_string(node->left->val));  // 左子节点的值加入路径
			}
		}
		return result;
	}
};

左叶子之和

Alt
本题是求左叶子节点值之和,然而仅凭借当前节点的信息是无法判断其是否是左叶子节点的,因此可以传入一个标志位表达父节点的信息来说明当前是否是左节点。

class Solution{
public:
	int sum;
	void traversal(TreeNode* node, bool is_left){
		// 如果左右节点都是空,同时标志位说明是左节点,那么加入结果和中
		if(!node->left && !node->right && is_left){
			sum += node->val;
			return;
		}
		if(node->left)  traversal(node->left, true);  // 遍历左节点
		if(node->right)  traversal(node->right, false);  // 遍历右节点
	}
	int sumOfLeftLeaves(TreeNode* root){
		sum = 0;
		traversal(root, false);
		return sum;
	}
};

我们只是求左叶子节点的和,将问题分解成当前节点左子树的结果加上右子树的结果,就是以当前节点为根节点的树的左叶子节点之和。但是必须注意仅凭当前节点的信息是没法判断是否是左叶子的,所以要在父节点的位置做判断

class Solution{
public:
	int sumOfLeftLeaves(TreeNode* root){
		if(!root)  return 0;  // 空节点没有左叶子,返回0
		int leftvalue = 0;
		if(root->left && !root->left->left && !root->left->right){  
			// 此时需要判断一下当前节点的左子树是否是左叶子(在父节点的位置做判断)
			leftvalue = root->left->val;
		}
		else{
			leftvalue = sumOfLeftLeaves(root->left);  // 左子树不是左叶子,继续遍历左子树
		}
		int rightvalue = sumOfLeftLeaves(root->right);
		return leftvalue + rightvalue;
	}
};

迭代法

class Solution{
public:
	int sumOfLeftLeaves(TreeNode* root){
		stack<TreeNode*> st;
		st.push(root);
		int result = 0;
		while(!st.empty()){
			TreeNode* node = st.top();
			st.pop();
			// 判断当前节点的左子树是不是左叶子
			if(node->left && !node->left->left && !node->left->right){
				result += node->left->val;
			}
			if(node->right)  st.push(node->right);
			if(node->left)  st.push(node->left);
		}
		return result;
	}
};
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值