104.二叉树的最大深度 (优先掌握递归)
这道题其实昨天写了,当时说是层序遍历做,但是层序和递归我都做了
1.层序遍历
int maxDepth(TreeNode* root) {
int depth = 0;
queue<TreeNode*> q;
if (root) q.push(root);
while (!q.empty())
{
depth++;
int size = q.size();
while (size--)
{
TreeNode* cur = q.front();
q.pop();
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
}
}
return depth;
}
2.递归
显然,深度等于 max(左子树深度,右子树深度) + 1
int maxDepth(TreeNode* root) {
if (root == nullptr) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1;
}
3.回溯法
回溯法我不怎么熟,所以还是写一遍。
这个方法就有点像是dfs的写法,其实我觉得代码随想录树的这一部分有点过于追求套路化,倒也不必非要去把问题都套路到前中后序遍历和层序遍历上去。
回溯法就是遍历所有可能性,在每个可能性中找到最符合要求的那个。回溯相比于递归,更像是一种暴力的搜索,把所有情况都检索一遍;而递归更像是把大问题拆解成**[子问题+常量级问题]**的格式
通常套路是这样:
typename 最终结果;
void 回溯函数(参数)
{
修改最终结果
if (可能性1)
{
维护目标++;
回溯函数(维护目标);
维护目标--;
}
}
就是在你进入可能性时要修改那个参数,退出递归时要改回去。
举个例子,从根节点开始,高度为1,现在进入左子树,当前高度就会被修改到1+左子树高,等到你返回回来以后,高度又得改回1,然后再进入右子树,去计算1+右子树高度。
我们就以这道题目为例子
int res; //最终结果
void getDepth(TreeNode* root, int depth) //depth表示到该点目前的深度
{
//如果depth超过res,那么更新res
if (depth > res) res = depth;
//如果已经没有子节点,直接返回
if (root->left == nullptr && root->right == nullptr) return;
//尝试加上左子树深度
if (root->left)
{
depth++;
getDepth(root->left, depth);
depth--;
}
//尝试加上右子树深度
if (root->right)
{
depth++;
getDepth(root->right, depth);
depth--;
}
}
int maxDepth(TreeNode* root) {
res = 0;
if (root == nullptr) return 0;
getDepth(root, 1);
return res;
}
111.二叉树的最小深度 (优先掌握递归)
同昨天做过了,其实难点在于如果只有左子树或者只有右子树,那么不能把没有的一方当成高度0.
1.层序
int minDepth(TreeNode* root) {
int depth = 0;
queue<TreeNode*> q;
if (root) q.push(root);
while (!q.empty())
{
depth++;
int size = q.size();
while (size--)
{
TreeNode* cur = q.front();
q.pop();
if (cur->left) q.push(cur->left);
if (cur->right) q.push(cur->right);
if (cur->left == nullptr && cur->right == nullptr)
return depth;
}
}
return depth;
}
2.递归
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
if (root->left == nullptr && root->right == nullptr) return 1;
if (root->left == nullptr) return minDepth(root->right) + 1;
if (root->right == nullptr) return minDepth(root->left) + 1;
return min(minDepth(root->left), minDepth(root->right)) + 1;
}
3.回溯
int res;
void getDepth(TreeNode* root, int depth)
{
if (root->left == nullptr && root->right == nullptr)
{
res = res < depth ? res : depth;
}
if (root->left)
{
depth++;
getDepth(root->left, depth);
depth--;
}
if (root->right)
{
depth++;
getDepth(root->right, depth);
depth--;
}
}
int minDepth(TreeNode* root) {
if (root == nullptr) return 0;
res = INT_MAX;
getDepth(root, 1);
return res;
}
222.完全二叉树的节点个数(优先掌握递归)
1.递归
明显,树结点数目 = 左子树结点数目 + 右子树节点数目 + 1
递归其实就是方程 + 终止条件,搞明白这两个就很简单(弄明白这两个自然你知道参数放什么)
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
return 1 + countNodes(root->left) + countNodes(root->right);
}
2.迭代
随便一种遍历方式,只要碰见新结点就加一,很容易。
我这里各写一个深度优先,层序也就是套模板,不写了(摆烂
int countNodes(TreeNode* root) {
stack<TreeNode*> st;
int res = 0;
if (root) st.push(root);
while (!st.empty())
{
TreeNode* cur = st.top();
st.pop();
if (cur != nullptr)
{
st.push(cur);
st.push(nullptr);
if (cur->left) st.push(cur->left);
if (cur->right) st.push(cur->right);
}
else
{
st.pop();
res++;
}
}
return res;
}
3.完全二叉树的性质
其实如果知道是完全二叉树,完全可以根据树的性质
如果是一颗满二叉树,它的大小就是和高度h有关,为 2 h − 1 2^h -1 2h−1
一棵向左遍历和向右遍历深度一样的完全二叉树一定是满二叉树,因此,我们借由这个性质,找到子树是满二叉树即可返回,大大缩减了递归深度。
只有一个点也是满二叉树
int countNodes(TreeNode* root) {
if (root == nullptr) return 0;
int left = 0, right = 0;
TreeNode* cur = root;
while (cur->left) {left++;cur = cur->left;}
cur = root;
while (cur->right) {right++;cur = cur->right;}
if (left == right) return (2 << left) - 1;
return countNodes(root->left) + countNodes(root->right) + 1;
}