给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。
注意:两个节点之间的路径长度由它们之间的边数表示。
示例 1:
输入:
5 / \ 4 5 / \ \ 1 1 5
输出:
2
示例 2:
输入:
1 / \ 4 5 / \ \ 4 4 5
输出:
2
注意: 给定的二叉树不超过10000个结点。 树的高度不超过1000。
方法一:
最简单的思路就是对每一个节点都判断他的左或右节点是否和自身节点相等,如果相等就把左或右节点的边相加,否则返回0。对二叉树的每一个节点都这么判断,由于每一次判断一个节点的连续路径时最坏情况是整棵树都是相同的值,需要遍历整个树,即O(n),而对每一个节点都要做一次这样的操作,遍历一遍所有节点的时间复杂度为O(n),总的时间复杂度最坏为O(n^2),还需要注意一点,在左右节点相等像父节点返回值时,如果是非根节点则返回左右节点的最大路路径长,如果是根节点则返回左右节点路径和。比如如下情况:
2 / \ 2 2 / \ 2 2
1:判断红色2时,由于左节点返回1,右节点返回1,因为红色2不是根节点,所以返回左右节点的最大值1+1=2。
2:判断蓝色2时,由于左节点返回2,右节点返回1,因为蓝色2是根节点,所以返回左右节点的路径和:左节点2+右节点1=3。
代码如下:
int longestUnivaluePathCore(TreeNode* root, TreeNode* &lastNode, TreeNode* true_root) { if (!root) { return 0; } if (lastNode->val == root->val) { lastNode = root; int left = longestUnivaluePathCore(root->left, lastNode, true_root); int right = longestUnivaluePathCore(root->right, lastNode, true_root); if (root == true_root) { return left + right; } else { return max(left, right) + 1; } } return 0; } int longestUnivaluePath(TreeNode* root) { if (!root) { return 0; } return max(longestUnivaluePathCore(root, root, root), max(longestUnivaluePath(root->left), longestUnivaluePath(root->right))); }
这种方法对于以下这种情况时间复杂度为O(n^2),只是思路简单,以为不会过的,没想到过了,只是时间有点。。。
9 / \ 9 9 / \ 9 9
方法二:
于是我们用深度优先搜索,从叶子结点逐层向上判断,只需要遍历一次,时间复杂度为O(n),具体思路如下:
helper函数深度遍历到叶节点,如果当前节点和左子节点的值相等,则left++,否则置0,同理如果当前节点和右节点的值相等,则right++,否则置0。其中我们有一个变量res,每次记录历史最大的res和left+right的最大值,这里每次返回都返回left和right的最大值而不用考虑根节点和非根节点的情况,是因为res记录的是历史最大的res和left+right的最大值,最后一次到根节点时也记录了根节点的left+right,即把自叶向根遍历过程中每次的节点都当成根节点来统计(历史最优和当前最优中取最优)。
int helper(TreeNode* root, int &res) { if (!root) { return 0; } int l = helper(root->left, res); int r = helper(root->right, res); int lres = (root->left && root->val == root->left->val) ? l + 1 : 0; int Rres = (root->right && root->val == root->right->val) ? r + 1 : 0; res = max(res, lres + Rres); return max(lres,Rres); } int longestUnivaluePath(TreeNode* root) { if (!root) { return 0; } int res = 0; helper(root, res); return res; }