好久没做题目了,感觉战斗力下降不少,不过有点弯弯绕绕的题目整出来还是很有成就感的~
该学习新东西啦,脚踏实地,总会离我的星辰更进一步~
题目
给你一棵树(即,一个连通的无环无向图),这棵树由编号从 0 到 n - 1 的 n 个节点组成,且恰好有 n - 1 条 edges 。树的根节点为节点 0 ,树上的每一个节点都有一个标签,也就是字符串 labels 中的一个小写字符(编号为 i 的 节点的标签就是 labels[i] )
边数组 edges 以 edges[i] = [ai, bi] 的形式给出,该格式表示节点 ai 和 bi 之间存在一条边。
返回一个大小为 n 的数组,其中 ans[i] 表示第 i 个节点的子树中与节点 i 标签相同的节点数。
树 T 中的子树是由 T 中的某个节点及其所有后代节点组成的树。
示例 1:
树的构造图点击链接看好啦
输入:n = 7, edges = [[0,1],[0,2],[1,4],[1,5],[2,3],[2,6]], labels = “abaedcd”
输出:[2,1,1,1,1,1,1]
解释:节点 0 的标签为 ‘a’ ,以 ‘a’ 为根节点的子树中,节点 2 的标签也是 ‘a’ ,因此答案为 2 。注意树中的每个节点都是这棵子树的一部分。
节点 1 的标签为 ‘b’ ,节点 1 的子树包含节点 1、4 和 5,但是节点 4、5 的标签与节点 1 不同,故而答案为 1(即,该节点本身)。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/number-of-nodes-in-the-sub-tree-with-the-same-label
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
- 根据边的关系构造这棵树,然后计算每个节点的标签在其子树中的出现次数
- 优化:这棵树不需要用node建出来,了解边的关系,可以通过dfs计算子树中小写字母出现的次数即可
- 时间复杂度n+e
代码1
class Solution {
public:
vector<vector<int>> rls;
vector<vector<int>> counts;
vector<int> mark;
string treeLabels;
vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
//其实这棵树完全可以不构造出来,只需要了解其边的关系即可
//因为每个节点的字符是a-z,所以每个节点统计a-z出现的次数
for (int i = 0; i < n; i++) {
vector<int> temp;
rls.push_back(temp);
}
//优化2
counts.assign(n, vector<int>(26));
mark.assign(n, 0);
/*for (int i = 0; i < edges.size(); i++) {
rls[edges[i][0]].push_back(edges[i][1]);
rls[edges[i][1]].push_back(edges[i][0]);
}*/
//优化1
for (const auto &edge: edges) {
rls[edge[0]].push_back(edge[1]);
rls[edge[1]].push_back(edge[0]);
}
treeLabels = labels;
dfs(0);
vector<int> res;
for (int i = 0; i < n; i++) {
res.push_back(counts[i][labels[i]-'a']);
}
return res;
}
void dfs(int nodeIndex) {
//cout << nodeIndex << " dfs" << endl;
mark[nodeIndex] = 1;
vector<int> childs = rls[nodeIndex];
for (int i = 0; i < childs.size(); i++) {
//cout << "---- " << childs[i] << " child" << endl;
if (mark[childs[i]] == 0) {
dfs(childs[i]);
for (int j = 0; j < 26; j++) {
counts[nodeIndex][j] += counts[childs[i]][j];
}
}
}
counts[nodeIndex][treeLabels[nodeIndex]-'a']++;
}
};
代码2
struct TreeNode1 {
char c;
vector<TreeNode1*> children;
int nums;
};
class Solution {
public:
vector<int> countSubTrees(int n, vector<vector<int>>& edges, string labels) {
unordered_map<int, TreeNode1*> tree;
unordered_map<int, vector<int>> rls;
for (int i = 0; i < n; i++) {
rls[i] = vector<int>();
TreeNode1* t = new TreeNode1();
t->c = labels[i];
vector<TreeNode1*> tChilds;
t->children = tChilds;
t->nums = 0;
tree.insert(make_pair(i, t));
}
for (int i = 0; i < edges.size(); i++) {
vector<int> one = edges[i];
rls[one[0]].push_back(one[1]);
rls[one[1]].push_back(one[0]);
}
int* mark = new int[n];
for (int i = 0; i < n; i++)
mark[i] = 0;
queue<int> nodes;
nodes.push(0);
mark[0] = 1;
while(!nodes.empty()) {
int first = nodes.front();
nodes.pop();
vector<int> chs = rls[first];
for (int i = 0; i < chs.size(); i++){
if (mark[chs[i]] == 0) {
tree[first]->children.push_back(tree[chs[i]]);
nodes.push(chs[i]);
mark[chs[i]] = 1;
}
}
}
vector<int> res;
/*for (int i = 0; i < n; i++) {
res.push_back(findNum(tree[i], tree[i]->c));
}*/
getNum(tree[0]);
for (int i = 0; i < n; i++) {
res.push_back(tree[i]->nums);
}
return res;
}
int findNum(TreeNode1* root, char key) {
int res = 0;
if (root == NULL)
return 0;
queue<TreeNode1*> nodes;
nodes.push(root);
while(!nodes.empty()) {
TreeNode1* first = nodes.front();
nodes.pop();
if (first->c == key)
res++;
vector<TreeNode1*> chs = first->children;
for (int i = 0; i < chs.size(); i++){
nodes.push(chs[i]);
}
}
return res;
}
//优化了计算label
vector<int> getNum(TreeNode1* root) {
vector<int> res;
for (int i = 0; i < 26; i++) {
res.push_back(0);
}
if (root == NULL) {
return res;
}
vector<TreeNode1*> chs = root->children;
for (int i = 0; i < chs.size(); i++) {
vector<int> t = getNum(chs[i]);
for (int i = 0; i < 26; i++) {
res[i] += t[i];
}
}
res[root->c-'a']++;
root->nums = res[root->c-'a'];
return res;
}
};
坑
- 我明明优化了整个大框架的思路,还是没A,最后用了auto以及vector的assignA掉了它,对leetcode的时间边界感到绝望
- 不用构建树的,我实在的构建出来一棵树,好大好占空间啊