687. Longest Univalue Path

687. Longest Univalue Path

题目描述:在二叉树中寻找任意一条路径,路径上的节点的值都相等,求路径包含的边数最大值

第一次解法

灵感来源自 path sum 的递归计数,只不过我们每次都要取最大值,比较麻烦的 corner case 在于:如果路径是子节点到子节点的话,只能单独进行比较,不能作为递归函数的返回值,而其中一个父节点到子节点的边数计算是可以递归的

int longestUnivaluePath(TreeNode* root) {
    if (!root) return 0;
    int m=0;
    int lr = DFS(root, root->val, 0, m);
	// 递归选择起点,m为子子节点间的边数,lr为父子节点间的边数
    return max(max(lr, m), longestUnivaluePath(root->left), longestUnivaluePath(root->right));
}

int DFS(TreeNode* root, int val, int depth, int &m) {
	// 触底或值不再相等的时候返回边数
    if (!root || root->val!=val) return depth-1;
	// 深搜左子树和右子树
    int l = DFS(root->left, val, depth+1, m);
    int r = DFS(root->right, val, depth+1, m);
	// 总是保存最大的子子路径边数
    m = max(m, l+r-2*depth);
	// 父子路径返回大的
    return max(l, r);
}

自底向上进行递归计数

从一个节点出发,我们是否能够通过分割出合理的子问题使得我们不需要分父到子以及子到子这两种情况呢?答案是肯定的,如果我们单单看一个节点的话,它只有子子路径这一种情况,这一种情况的计算是通过左边数加上右边数的方式计算得到,当右边数为0是,左边数即为从当前节点向左下方出发所能达到的最大边数,右边数在左边数为0的时候同理,如果从底向上递归计数的话也能够将所有的节点都作为起始节点,如果两边都不为0,那么当前节点触发的最大值就应该是两边计数的总和

int longestUnivaluePath(TreeNode* root) {
    if (!root) return 0;
    int m=0;
    DFS(root, m);
    return m;
}

int DFS(TreeNode* root, int &m) {
	// 递归搜索左子树的边数和右子树的边数
    int l=root->left ? DFS(root->left, m) : 0;
    int r=root->right ? DFS(root->right, m) : 0;
	// 值相等,这条边就可以纳入计数
    l = (root->left && root->left->val == root->val) ? l+1 : 0;
    r = (root->right && root->right->val == root->val) ? r+1 : 0;
	// 当前节点出发,左子树边数和右子树边数之和总是最大的,不能纳入递归计数中
    m = max(m, l+r);
	// 左右计数只返回大的
    return max(l, r);
}

反思

刚开始的解法起始并不是很好,重复遍历的多次,第二种解法深搜的时候并不需要将父节点的值传到下一层,递归判断相等来自底向上进行计数更优

收获

DFS模板:

  1. 函数体内首先递归调用进行搜索
  2. 做题目要求的操作:判断或者计数或者记录等等
  3. 递归的最小子问题的结果作为返回值

#递归 #DFS #tree