Sum Root to Leaf Numbers 求根到叶子节点数字之和

时间:2022-10-20 16:00:38

给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。

例如,从根到叶子节点路径 1->2->3 代表数字 123

计算从根到叶子节点生成的所有数字之和。

说明: 叶子节点是指没有子节点的节点。

示例 1:

输入: [1,2,3]
    1
   / \
  2   3
输出: 25
解释:
从根到叶子节点路径 1->2 代表数字 12.
从根到叶子节点路径 1->3 代表数字 13.
因此,数字总和 = 12 + 13 = 25.

示例 2:

输入: [4,9,0,5,1]
    4
   / \
  9   0
 / \
5   1
输出: 1026
解释:
从根到叶子节点路径 4->9->5 代表数字 495.
从根到叶子节点路径 4->9->1 代表数字 491.
从根到叶子节点路径 4->0 代表数字 40.
因此,数字总和 = 495 + 491 + 40 = 1026.

思路

这道题采用前序排序,然后用vector保存中间访问过的节点,如果当前访问的是叶节点,则直接计算vector中存储的对应的数字,不断累加到res中即可。所以需要三个函数:

int getVec(vector<int> vec) //得到vector中对应的数字
void sumNumbersCore(TreeNode* root, vector<int> vec, int &res) //前序遍历访问并保存节点,如果是叶节点调用getVec累加res
int sumNumbers  //主函数

代码如下:

int getVec(vector<int> vec) {
	int result = 0;
	for (int i = 0; i < vec.size(); i++) {
		result = result * 10+vec[i];
	}
	return result;
}
void sumNumbersCore(TreeNode* root, vector<int> vec, int &res) {
	if (!root) {
		return;
	}
	vec.push_back(root->val);
	if (!root->left && !root->right) {
		res += getVec(vec);
		return;
	}
	sumNumbersCore(root->left, vec, res);	
	sumNumbersCore(root->right, vec, res);
	//vec.pop_back();
}
int sumNumbers(TreeNode* root) {
	if (!root) {
		return 0;
	}
	if (!root->left && !root->right) {
		return root->val;
	}
	vector<int> vec;
	int res = 0;
	sumNumbersCore(root,vec, res);
	return res;
}

注意vector声明的是值传递,在向上返回父节点时不会因为子函数修改了vector而发生改变,也就是说对于函数:

void sumNumbersCore(TreeNode* root, vector<int> vec, int &res) {
	if (!root) {
		return;
	}
	vec.push_back(root->val);
	if (!root->left && !root->right) {
		res += getVec(vec);
		return;
	}
	sumNumbersCore(root->left, vec, res);	
	sumNumbersCore(root->right, vec, res);
	//vec.pop_back();
}

无论子函数对vector做了增删改查或其他操作,最后执行完sumNumbersCore返回前,vector会恢复原样,所以我们无需对vector进行pop操作。如果对vector要进行引用传递,请用如下版本:

void sumNumbersCore(TreeNode* root, vector<int> &vec, int &res) {
	if (!root) {
		return;
	}
	vec.push_back(root->val);
	if (!root->left && !root->right) {
		res += getVec(vec);
		return;
	}
	if (root->left) {
		sumNumbersCore(root->left, vec, res);
		vec.pop_back();
	}
	if (root->right) {
		sumNumbersCore(root->right, vec, res);
		vec.pop_back();
	}
}

由于是引用传递,所以会保留子函数的修改记录,我们必须进行pop操作,恢复现场。

方法2

由于我们需要一个vector保存遍历的节点,空间复杂度是O(n),能不能把复杂度降为O(1)。答案是可以的。

采用递归的思想,每次先访问根节点,更新根节点为:

res = res * 10 + root->val;

然后对左右子树分别调用自身递归函数,分别相加左右子树的递归返回和。

代码如下:

int sumNumbersCore(TreeNode* root, int res) {
	if (!root) {
		return 0;
	}
	//vec.push_back(root->val);
	res = res * 10 + root->val;
	if (!root->left && !root->right) {
		return res;
	}
	return sumNumbersCore(root->left, res) + sumNumbersCore(root->right, res);
}
int sumNumbers(TreeNode* root) {
	if (!root) {
		return 0;
	}
	if (!root->left && !root->right) {
		return root->val;
	}
	int res = 0;
	return sumNumbersCore(root, res);
}

这里要注意我们的返回值res必须声明成值传递,不能是引用传递,否则:

return sumNumbersCore(root->left, res) + sumNumbersCore(root->right, res);

在递归调用左子树时,res已经被左子树修改了,再把res传到右子树去计算时已经被修改了,不是根节点传入的res。

note:递归调用时我们经常会在函数参数中声明变量是引用或者值传递,声明引用类型是保证可以随递归调用不断修改自身的值。但是对于特殊情况一定要视情况而定(比如恢复现场,对于vector的pop操作,对于stack或者queue的pop操作等。。。)