687 最长同值路径——Leetcode 天天刷(2022.9.2)【DFS】
文章目录
前言
今天的题目emmm,怎么说呢,其实算法上比较简单,但是细节还是比较多的,也可以说是自己考虑的不够足够。
算法上采用的DFS,即深度优先搜索。
题目描述
简述一下:就是给你一个二叉树,需要返回 最长的 同值路径
的长度,就是路径上的每个节点都同值。
路径的长度用 边数 来表示。
示例
输入:root = [5,4,5,1,1,5]
输出:2
输入:root = [1,4,5,4,4,5]
输出:2
提示信息
- 树的节点数的范围是
[0, 104]
-1000 <= Node.val <= 1000
- 树的深度将不超过
1000
本地调试运行
说实话,树之类的构造,尤其是指针类型的树构建还是比较麻烦的,从样例来看就是输入用BFS
顺序或者说是 层次遍历
的顺序来构建二叉树。
输入格式
我们还是可以假定多组输入,每组第一行输入整数n,然后第二行输入n个字符串,字符串直接用 空格 隔开,其中
null
表示空节点,数字 就是表示节点值。
输出格式
每行输出一个整数,表示最大同值路径长度。
输入样例
6
5 4 5 1 1 5
6
1 4 5 4 4 5
8
1 null 1 1 1 1 1 1
输出样例
2
2
4
层次遍历构造二叉树
其实就是创建二叉树的一种方式,大家如果看不懂最后的代码的构建二叉树的部分,可以看看下面的这篇的blog:
解法——DFS
首先,我们对于 节点路径长度,考虑的是以当前节点值作为数值的同值路径长度。因此,我们可以先递归计算以左右子节点的节点路径长度,然后根据左右子节点值和当前节点值的是否相同来计算当前节点的节点路径长度,同时需要返回当前的最大单侧节点路径长度。
细节
- 首先是用边数来表示长度,所以对于长度的最小值为0,空节点返回-1。
- 如果子节点和当前节点值相同,我们可以将子节点的节点路径长度 + 1, 1表示当前当前节点和子节点的边。
- 返回路径长度的时候,如果是当前节点路径长度是两侧的,返回需要只返回单侧长度较大的。
算法分析+解题效率
时间复杂度 O ( n ) O(n) O(n),n为节点数量。
空间复杂度 O ( n ) O(n) O(n), DFS递归n层。
还行。
Code(C++)
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<sstream>
using namespace std;
// 定义二叉树节点
struct TreeNode
{
int val;
TreeNode* left;
TreeNode* right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode* left, TreeNode* right) : val(x), left(left), right(right) {}
};
// 构造一下二叉树
class BinaryTree
{
public:
TreeNode* root; // 根节点
/// <summary>
/// BFS——根据数组构建二叉树
/// 数组中节点的顺序表示的就是BFS的顺序
/// 所以使用BFS 完成搜索
/// </summary>au
/// <param name="nodes"></param>
BinaryTree(vector<string> nodes)
{
// 下标索引
int i = 0;
// 数组尺寸
int n = nodes.size();
// 先判断根节点是否是空
if (nodes[0] == "null" && n == 1)
{
root = nullptr;
return;
}
// 非空则新建根节点
stringstream ss;
ss << nodes[0];
int val;
ss >> val;
root = new TreeNode(val);
i++;
// 节点对列
queue<TreeNode*> que;
// 先将根结点入队
que.push(root);
// 当对列不空且未遍历完时
while (!que.empty() && i < n)
{
// 获取节点并出队
TreeNode* node = que.front();
que.pop();
// 然后给左右指针
for (int j = 1; j <= 2 && i < n; ++j, ++i)
{
// 若空则置空
if (nodes[i] == "null" && j == 1)
{
node->left == nullptr;
}
else if (nodes[i] == "null" && j == 2)
{
node->right = nullptr;
}
// 非空则生成节点并入队
else if (j == 1)
{
node->left = new TreeNode(stoi(nodes[i]));
que.push(node->left);
}
else
{
node->right = new TreeNode(stoi(nodes[i]));
que.push(node->right);
}
}
}
}
/// <summary>
/// 展示树的结构
/// 为了能够清晰展示,还是使用BFS
/// </summary>
void display()
{
// 若根节点为空
if (root == nullptr)
{
return;
}
// 非空入队
queue<TreeNode*> que;
que.push(root);
// 每一层节点的数量,包括空节点
int t = 0, num = 1;
while (!que.empty())
{
TreeNode* node = que.front();
que.pop();
cout << node->val << " ";
num--;
for (int i = 0; i < 2; ++i)
{
TreeNode* child;
if (i == 0)
{
child = node->left;
}
else
{
child = node->right;
}
if (child != nullptr)
{
que.push(child);
t++;
}
}
// 当num = 0 时,说明当层遍历完,需要换行,进入下一层
if (num == 0)
{
cout << endl;
num = t;
t = 0;
}
}
}
};
class Solution
{
private:
int len;
public:
// DFS
// 先计算出子树中以子节点的数值为路径的长度
// 然后根据子节点数值和当前节点是否相同,若相同则加上对应长度
// 最后每次递归和答案长度比较动态更新
int longestUnivaluePath(TreeNode* root)
{
len = 0;
dfs(root);
return len;
}
int dfs(TreeNode* node)
{
// 空节点,返回-1
if (!node)
{
return -1;
}
int l1, l2; // 分别以左右子节点值为路径的长度
int len0 = 0; // 以当前节点值为路径的长度
l1 = dfs(node->left); // 计算左子树
l2 = dfs(node->right); // 计算右子树
int len1 = 0, len2 = 0; // 计算以当前节点的左右路径
// 如果左节点同值,计算当前路径
if (node->left && node->left->val == node->val)
{
len0 += l1 + 1;
len1 = l1 + 1;
}
// 同理
if (node->right && node->right->val == node->val)
{
len0 += l2 + 1;
len2 = l2 + 1;
}
// 动态更新最大路径
len = max(len, len0);
return max(len1, len2); // 返回单路径的最大值
}
};
int main(int agrc, char** argv)
{
int n;
Solution * sol = new Solution();
while (~scanf("%d", &n))
{
vector<string> nodes(n);
for (int i = 0; i < n; ++i)
{
cin >> nodes[i];
}
BinaryTree* bt = new BinaryTree(nodes);
printf("%d\n", sol->longestUnivaluePath(bt->root));
delete bt;
}
delete sol;
return 0;
}