拓扑排序
注意两种不合法字母顺序
class Solution {
public:
string alienOrder(vector<string>& words) {
int n = words.size();
if(n == 1) return words[0];
bool st[26] = {0}; //标记words中出现的所有字母
vector<vector<int>> adj(26); //存储拓扑图
vector<int> in(26); //拓扑图中点的入度
string res;
for(int i = 0; i < n; i++)
for(int j = 0; j < words[i].size(); j++)
if(!st[words[i][j]-'a']) st[words[i][j]-'a'] = true;
unordered_set<int> us; //记录加入拓扑图的点
for(int i = 1; i < n; i++) {
int len = min(words[i-1].size(),words[i].size());
bool flag = false;
for(int j = 0; j < len; j++) {
int u = words[i-1][j]-'a';
int v = words[i][j]-'a';
if(u != v) {
adj[u].emplace_back(v);
in[v]++;
if(!us.count(u)) us.insert(u);
if(!us.count(v)) us.insert(v);
flag = true;
break;
}
}
//前缀相同并且前面的长度大于当前长度一定不是有效的
if(!flag && words[i-1].size() > words[i].size()) return "";
}
queue<int> que;
for(int i = 0; i < 26; i++) {
if(in[i] == 0 && st[i]) { //i为拓扑图中的点且入度为0
que.emplace(i);
st[i] = false;
}
}
while(que.size()) {
int u = que.front(); que.pop();
us.erase(u); st[u] = false;
res.push_back((char)(u+'a'));
for(int v: adj[u]) {
in[v]--;
if(in[v] == 0) que.emplace(v);
}
}
//未弹出所有的点,说明该图出现环,不能进行拓扑排序
if(us.size()) return "";
//其他未加入拓扑图的点顺序就随意了,直接插入即可
for(int i = 0; i < 26; i++)
if(st[i]) res.push_back((char)(i+'a'));
return res;
}
};
方法一、拓扑排序
class Solution {
public:
bool sequenceReconstruction(vector<int>& nums, vector<vector<int>>& sequences) {
int n = nums.size();
vector<int> in(n+1);
vector<unordered_set<int>> adj(n+1);
for(int i = 0; i < sequences.size(); i++) {
for(int j = 1; j < sequences[i].size(); j++) {
int &cur = sequences[i][j], &pre = sequences[i][j-1];
if(!adj[pre].count(cur)) {
adj[pre].insert(cur);
in[cur]++;
}
}
}
queue<int> que;
for(int i = 1; i <= n; i++) {
if(in[i] == 0) que.emplace(i);
}
if(que.size() > 1) return false;
while(que.size()) {
int u = que.front(); que.pop();
for(int v: adj[u]) {
in[v]--;
if(in[v] == 0) {
que.emplace(v);
if(que.size() > 1) return false;
}
}
}
return true;
}
};
方法二、直接检索(贪心)
Tips:我对偏序的理解:比如 a>b
属于一个偏序关系,也算是一个偏序对,>
就属于偏序,可以替换成其他二元关系函数,拓扑图就是一个非严格偏序集(可能产生多个拓扑序列),题目中求的最短超序列就是一个严格偏序集。
nums = [1,2,3], sequences = [[1,2],[1,3]]
,从方法一的拓扑序列思路中想到一种不合法的情况:拓扑图存在多个入度同时为0的点。这种情况等同于
n
u
m
s
nums
nums 的某个相邻的偏序对不存在
s
e
q
u
e
n
c
e
s
sequences
sequences 的偏序集中,并且方法一获得的最短超序列(唯一拓扑序列)的偏序集一定等同于
n
u
m
s
nums
nums 的偏序集。class Solution {
int make_key(int prev, int next){
return (prev<<14)|next;
}
public:
bool sequenceReconstruction(vector<int>& nums, vector<vector<int>>& sequences) {
int n = nums.size();
unordered_set<int> nxt;
for(auto &s:sequences){
for(int i=1, l=s.size(); i<l; ++i){
nxt.insert(make_key(s[i-1], s[i])); //s[i-1] 在 s[i] 的关系哈希
}
}
for(int i=1; i<n; ++i){
if(nxt.find(make_key(nums[i-1], nums[i]))==nxt.end()){
return false;
}
}
return true;
}
};
并查集
获得所有连通块的数量即可。
class Solution {
public:
vector<int> f;
int getf(int v) {
if(v == f[v]) return v;
else return f[v] = getf(f[v]);
}
bool merge(int a,int b) {
int ta = getf(a);
int tb = getf(b);
if(ta != tb) {
f[tb] = ta;
return false;
} else return true;
}
int findCircleNum(vector<vector<int>>& isConnected) {
int n = isConnected.size();
f.resize(n);
for(int i = 0; i < f.size(); i++) f[i] = i;
for(int i = 0; i < n; i++)
for(int j = 0; j < n; j++)
if(isConnected[i][j] == 1) {
merge(i,j);
}
int res = 0;
for(int i = 0; i < n; i++) if(f[i] == i) res++;
return res;
}
};