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

110.平衡二叉树

思路的重点是判断完一个子树是否是平衡二叉树时如何返回结果。开始想的是把递归的返回值类型设置为bool,但是这样就无法返回子节点的高度信息,最后还是没想明白。

思路:不断获取子节点的高度,如果两侧节点高度差大于1,说明该节点代表的子树是非平衡二叉树,返回-1即可。最后递归的结果如果是-1,说明是非平衡二叉树。

// 计算当前节点高度,
// 如果左右子树的高度差大于1说明当前子树是非平衡二叉树,直接返回-1
int getDepth(TreeNode* cur) {
	if (!cur)
		return 0;
	int leftDepth = getDepth(cur->left);
	int rightDepth = getDepth(cur->right);
	if (leftDepth == -1 || rightDepth == -1)
		return -1;
	int diff = abs(leftDepth - rightDepth);
	return (diff > 1) ? -1 : std::max(leftDepth, rightDepth) + 1;
}

bool isBalanced(TreeNode* root) {
	return getDepth(root) > -1;
}

 257. 二叉树的所有路径

最开始想到的是用前序遍历,但是返回值类型与参数没有想明白,最开始的声明形式为vector getPaths(TreeNode* cur, string path),想着用返回值来记录结果,怎么写都不对劲,最后看解析是用引用来维护一个结果数组,一下子就写顺畅了。

递归写法:

思路:父节点不断更新当前路径,并将路径传递给子节点,当子节点为叶节点时向结果数组中添加当前路径

· 返回值类型:void,不需要返回值,将结果记录在引用维护的结果数组中即可

· 传入参数:

        TreeNode* cur:当前节点的指针

        string path:父节点的路径

        vector &ans:结果数组,受引用维护,在全部的递归过程中是唯一的

· 单层递归逻辑——前序遍历:

        中:更新当前节点,判断当前节点是否是叶子节点,如果是则将当前路径加入结果数组

        左:递归访问左孩子

        右:递归访问右孩子

void getPaths(TreeNode* cur, string path, vector &ans) {
	if (!cur)
		return;
	// 中,更新path,如果当前节点是叶子节点将path加入结果数组并返回
	// 更新path
	if (path == "")
		// 根节点的path字符串需要特殊处理
		path = std::to_string(cur->val);
	else
		path = path + "->" + std::to_string(cur->val);
	if (!cur->left && !cur->right) {
		ans.push_back(path);
		return;
	}
	
	// 左
	getPaths(cur->left, path, ans);
	// 右
	getPaths(cur->right, path, ans);
	return;
}

vector binaryTreePaths0(TreeNode* root) {
	vector ans;
	getPaths(root, "", ans);
	return ans;
}

迭代写法:

 由于需要获取节点对应的路径,所以要多建一个栈,同步保存对应节点的路径

vector binaryTreePaths(TreeNode* root) {
	vector ans;
	stack st;
	stack pathSt;		// 路径也要使用栈保存,从而与节点同步
	TreeNode* cur;
	string path;

	if (root) {
		st.push(root);
		pathSt.push(std::to_string(root->val));
	}
	while (!st.empty()) {
		cur = st.top();
		path = pathSt.top();
		st.pop();
		pathSt.pop();
		// 中
		if (!cur->left && !cur->right) {
			ans.push_back(path);
		}
		// 左
		if (cur->left) {
			st.push(cur->left);
			pathSt.push(path + "->" + std::to_string(cur->left->val));
		}
		// 右
		if (cur->right) {
			st.push(cur->right);
			pathSt.push(path + "->" + std::to_string(cur->right->val));
		}
	}
	return ans;
}

404.左叶子之和

(感觉卡哥的写法有些繁琐了,虽然节省了一个参数需要的空间,但不是很好理解)

思路:与257有点类似,使用一个引用维护结果,当节点是左叶子时使用节点值对结果进行更新

· 返回值类型:void,不需要返回值,将结果使用一个引用进行维护即可

· 传入参数:

        TreeNode* cur:当前节点的指针

        bool isLeft:判断该节点是否为左节点的布尔值

        int &ans:所有左叶子的和,受引用维护,在全部递归过程中是唯一的

· 单层递归逻辑——前序遍历:

        中:判断当前节点是否是左叶子,如果是则更新ans的值

        左:递归访问左孩子,isLeft传入true

        右:递归访问右孩子,isLeft传入false

void traversal(TreeNode* cur, bool isLeft, int& sum) {
	if (!cur)
		return;

	// 中
	if (isLeft && !cur->left && !cur->right) {
		sum += cur->val;
		return;
	}
	// 左
	traversal(cur->left, true, sum);
	// 右
	traversal(cur->right, false, sum);
}

int sumOfLeftLeaves(TreeNode* root) {
	int sum = 0;
	traversal(root, false, sum);
	return sum;
}

一些收获

 递归:

1、对于需要在递归过程中保持唯一性的变量(如最后的结果),可以使用引用维护(效果与使用一个全局变量维护相同)

2、关于递归顺序:

        · 当子节点需要父节点信息的时候使用前序遍历,如:

                257子节点需要父节点路径

                404子节点需要父节点判断该子节点是不是左节点

        · 当父节点需要子节点信息时使用后序遍历,如:

                104求最大深度,父节点需要子节点的高度信息

迭代:

当需要该节点除成员属性以外的信息(如节点路径,是否是左节点)时,可以多建几个栈同步保持节点信息。

你可能感兴趣的:(算法,数据结构)