好难的题,这是看官方题解写的题解(-^-)
题意
给出数组pairs
(不会重复),问是否存在满足way的树,没有的话返回0,有一个返回1,两个或两个以上返回2。way的条件为:
- 树的所有结点都在pairs中(pairs中出现的数字都要在树上)
pair[x,y]
表示x是y的祖先或y是x的祖先,这就说明如果树上有x有y但没有pair[x,y]
的话,x和y就是被分在某个公共祖先的两个不同分支上。- 树不一定是二叉树(感觉这个条件有点多余)
思路
首先就是构图,pairs
没有明确x和y谁是祖先谁是子孙。这里用map存所有节点相关的点,而所有相关节点用集合去重。保存完后,就有第一个判定条件:需要有根节点的话,就必有出现至少一个节点能成为所有节点的祖先,代码中就表现为该节点的集合元素个数是所有节点的数量减一。
能出现根节点后,就需要判断其余节点,是否能出现祖先,代码就表现为是否允许出现父节点就可以了,如果当前节点的集合中存在自身集合元素个数大于等于当前节点的集合元素个数的话就说明可以出现父节点,而且这个父节点一定要是大于等于的前提下,最小的那个(否则就是父节点的祖先)
。如果出现等于的情况,那么树的形状就可以是多种了(ans=2)。
再然后就是看当前节点的父节点的集合是否包含当前节点的所有集合元素,这里一定要包含全部,出现不包含的情况就一定没有满足pairs的树。
(不理解或想知道证明过程的话,推荐看官方题解)
代码
class Solution {
public:
int checkWays(vector<vector<int>>& pairs) {
unordered_map<int,unordered_set<int>> adj;
for(auto &v:pairs){
adj[v[0]].emplace(v[1]);
adj[v[1]].emplace(v[0]);
}
int flag = -1;
for(auto &[node,numSet]:adj){
if(numSet.size() == adj.size()-1){
flag = node;
break;
}
}
if(flag == -1)return 0;
int ans = 1;
for(auto &[node,numSet]:adj){
if(node == flag)continue;
int parent = -1;
int parentNum = INT_MAX;
int i = adj[node].size();
for(auto &v: numSet){
int j = adj[v].size();
if(j>=i && j<parentNum){
parent = v;
parentNum = j;
}
}
if(parent == -1)return 0;
if(parentNum == i) ans = 2;
for(auto &v: numSet){
if(v == parent)continue;
if(!adj[parent].count(v))return 0;
}
}
return ans;
}
};