#define MAX(a, b) (((a)>(b)) ? (a) : (b))
int result;
void dfs(struct TreeNode* root, int count)
{
if (root == NULL) return;
count++; /* 递归层数 */
result = MAX(count, result); /* 记录最深递归层数 */
dfs(root->left, count); /* 理解返回的含义 */
dfs(root->right, count);
}
int maxDepth(struct TreeNode* root)
{
result = 0;
dfs(root, 0);
return result;
}
二叉树递归练习
二叉树最小深度(leetcode 111)
思路:递归遍历二叉树,同时判断是否为子叶,使用一个全局变量记录最小深度。
#define MIN(a, b) (((a)<(b)) ? (a) : (b))
int min;
void dfs(struct TreeNode* root, int count)
{
if (root == NULL) return;
count++;
if ( root->left == NULL && root->right == NULL)
min = MIN(min, count);
dfs(root->left, count);
dfs(root->right, count);
}
int minDepth(struct TreeNode* root) {
min = INT_MAX;
dfs(root, 0);
return (min == INT_MAX) ? 0 : min;
}
路径总和 leetcode 112
关于递归函数参数与返回值的思考
int target;
bool dfs(struct TreeNode* root, int sum)
{
if ( root == NULL)
return false;
sum += root->val;
if ( root->left == NULL && root->right == NULL)
{
if ( sum == target ) return true;
else return false;
}
return dfs(root->left, sum) || dfs(root->right, sum);
}
bool hasPathSum(struct TreeNode* root, int targetSum) {
target = targetSum;
return dfs(root, 0);
}
求根节点到叶节点数字之和 leetcode 129
统计二叉树中好节点的数目 leetcode 1448
思路:传入参数中有一个到当前层的最大值,然后维护一个全局变量记录符合要求的节点数。
int count;
void dfs(struct TreeNode* root, int max)
{
if (root == NULL) return;
if (root->val >= max)
{
max = root->val;
count++;
}
dfs(root->left, max);
dfs(root->right, max);
}
int goodNodes(struct TreeNode* root){
count = 0;
dfs(root, INT_MIN);
return count;
}
不使用全局变量?使用 return 返回数量呢?
int dfs(struct TreeNode* root, int max)
{
if (root == NULL) return 0;
int ret = 0;
if (root->val >= max)
{
max = root->val;
ret = 1;
}
max = root->val >= max ? root->val : max;
return ret + dfs(root->left, max) + dfs(root->right, max);
}
int goodNodes(struct TreeNode* root){
return dfs(root, INT_MIN);
}
二叉树的垂序遍历 987
二叉树相同
判断相同二叉树(leetcode 100)
判断相同二叉树(leetcode 100)。根节点相同,然后左子树要相同,右子树也要相同。
如何判断根节点相同?节点的值相同的前提条件是两个节点都不是空节点,因此判空在先。如果两个节点都是空节点,那么返回 true 即相同,一个编程技巧,判断空节点时可以使用 p == q 来判断,只有都为 NULL 才相同,其他的地址是不可能相同的。这也是一个比较巧妙的边界条件。
bool dfs(struct TreeNode* root, long long int l, long long int r)
{
if (root == NULL)
return true;
return (root->val > l && root->val < r) && dfs(root->left, l, root->val) && dfs(root->right, root->val, r);
}
bool isValidBST(struct TreeNode* root) {
return dfs(root, LLONG_MIN, LLONG_MAX);
}
在 dfs 函数中,return 后面的表达,隐含了先访问根节点,然后遍历左子树,遍历右子树。
时间复杂度为 O(n), 空间复杂度为 O(n)。
方法二:中序遍历
按照递归左子树,访问节点值,递归右子树这个顺序遍历,这就是中序遍历(左中右)。
二叉搜索树中序遍历的优秀性质:中序遍历访问节点值是严格递增的。
long long int pre = LLONG_MIN;
bool isValidBST(struct TreeNode* root) {
if (root == NULL)
return true;
if (isValidBST(root->left) == false)
return false;
if (root->val <= pre )
return false;
pre = root->val;
return isValidBST(root->right);
}
存在的问题:leetcode OJ 可能会在同一个进程里重复调用 isValidBST 函数,但是 pre 变量还没更新。同时使用 3 个测试用例,都只包含一个节点为0,但是第一个用例通过了,后面两个没过。
long long int pre;
bool dfs(struct TreeNode* root)
{
if (root == NULL)
return true;
if (dfs(root->left) == false)
return false;
if (root->val <= pre )
return false;
pre = root->val;
return dfs(root->right);
}
bool isValidBST(struct TreeNode* root) {
pre = LLONG_MIN;
return dfs(root);
}
方法三
前序遍历的思路是把区间向下传递,还有一个思路是把节点的范围往上传。
最近公共祖先
struct TreeNode* lowestCommonAncestor(struct TreeNode* root, struct TreeNode* p, struct TreeNode* q) {
if ( root == NULL || root == p || root == q )
return root;
struct TreeNode* left = lowestCommonAncestor(root->left, p, q);
struct TreeNode* right = lowestCommonAncestor(root->right, p, q);
if (left && right)
return root;
if (left)
return left;
return right;
}
层序遍历
广度优先搜索。递归从根节点出发一直往下走,层序遍历则是一行行遍历。
如何从根节点出发得到每层的值?
初始化一个 current 数组,把根节点放进去,然后开始循环。
还需要一个 next 数组,遍历 current,把左右儿子都记录到 next 中。遍历 current 的同时,记录节点值,遍历结束后添加答案。