法官是出度为0,入度为n-1的结点,且是唯一一个
class Solution {
public:
int findJudge(int N, vector<vector<int>>& trust) {
// 法官是出度为0,入度为n-1的结点,且是唯一一个
vector<int> inDegree(N+1, 0);
vector<int> outDegree(N+1, 0);
for(int i = 0; i < trust.size(); i++){
int start = trust[i][0];
int end = trust[i][1];
inDegree[end]++;
outDegree[start]++;
}
for(int i = 1; i <= N; i++){
if(outDegree[i] == 0 && inDegree[i] == N-1){
return i;
}
}
return -1;
}
};
邻接表,初始化,然后默认染色为0, 依次删除邻接表中的颜色即可
class Solution {
public:
vector<int> gardenNoAdj(int N, vector<vector<int>>& paths) {
// 建立邻接表
vector<int> G[N];
for(int i = 0; i < paths.size(); i++){
G[paths[i][0]-1].push_back(paths[i][1]-1);
G[paths[i][1]-1].push_back(paths[i][0]-1);
}
vector<int> ans(N, 0); // 默认所有花园不染色,染0
for(int i = 0; i < N; i++){
set<int> color{1, 2, 3, 4}; // 从第一个花园开始走,把与它邻接的花园的颜色从color{1,2,3,4}这个颜色集中删除;
for(int j = 0; j < G[i].size();j++){ // 遍历邻接表,去除染过色的颜色
color.erase(ans[G[i][j]]); // 把已染过色的去除
}
ans[i] = *(color.begin()); // 染色,删完了所有与它相邻的颜色,就可以把集合中剩下的颜色随机选一个给它了,为了简单,将集合中的第一个颜色赋给当前花园;
}
return ans;
}
};
DFS
class Solution {
public:
Node* cloneGraph(Node* node) {
// DFS
if(node == NULL){
return NULL;
}
unordered_map<Node*, Node*> visited;
Node* node_clone = dfs(node, visited);
return node_clone;
}
Node* dfs(Node* node, unordered_map<Node*, Node*>& visited){
if(visited.find(node) != visited.end()){
// 找到节点,已经访问过了
return visited[node];
}
Node* node_clone = new Node(node->val); // 复制当前节点
visited[node] = node_clone; // 该节点已经被访问过了
for(int i = 0; i < node->neighbors.size(); i++){
// 访问邻居
Node* node_neighbour_clone = dfs(node->neighbors[i], visited); // 访问所有相邻的节点
node_clone->neighbors.push_back(node_neighbour_clone); // 将结果存入邻居中
}
return node_clone;
}
};
BFS
class Solution {
public:
Node* cloneGraph(Node* node) {
// BFS
if(node == NULL){
return NULL;
}
unordered_map<Node*, Node*> mmap; // 记录是否被访问过
Node* head = new Node(node->val);
mmap[node] = head;
queue<Node*> q;
q.push(node);
while(!q.empty()){
Node* temp = q.front(); // 取队列首节点
q.pop(); // 出队列
for(auto neighbor_node : temp->neighbors){ // 遍历邻居节点
if(mmap.find(neighbor_node) == mmap.end()){
// mmap中不存在,没有被访问过
mmap[neighbor_node] = new Node(neighbor_node->val); // 将该节点放入mmap中
q.push(neighbor_node); // 放入队列
mmap[temp]->neighbors.push_back(mmap[neighbor_node]); // 放入temp节点的邻居中
}
}
return head;
}
};
BFS
class Solution {
public:
bool findWhetherExistsPath(int n, vector<vector<int>>& graph, int start, int target) {
// BFS
vector<bool> visited(n);
visited[start] = true; // 初始化 访问过起点
vector<vector<int>> map(n);
// 初始化邻接表
for(int i = 0; i < graph.size(); i++){
map[graph[i][0]].push_back(graph[i][1]);
}
// 队列
queue<int> q;
q.push(start);
while(!q.empty()){
int temp = q.front();
q.pop();
if(temp == target){
// 到达终点了
return true;
}
for(int i = 0; i < map[temp].size(); i++){
if(!visited[map[temp][i]]){
// 尚未访问过 放入队列
q.push(map[temp][i]);
visited[map[temp][i]] = true; // 标记访问过
}
}
}
return false;
}
};
DFS
class Solution {
public:
bool findWhetherExistsPath(int n, vector<vector<int>>& graph, int start, int target) {
// DFS
vector<int> visited(n, false);
vector<vector<int>> map(n);
// 邻接表初始化
for(int i = 0; i < graph.size(); i++){
map[graph[i][0]].push_back(graph[i][1]);
}
bool found = false;
dfs(map, start, target, found, visited);
return found;
}
void dfs(vector<vector<int>>& map, int start, int target, bool& found, vector<int>& visited){
if(found){
return;
}
if(start == target){
found = true;
}
for(int i = 0; i < map[start].size(); i++){
if(!visited[map[start][i]]){
// 未被访问过
visited[map[start][i]] = true;
dfs(map, map[start][i], target, found, visited);
visited[map[start][i]] = false;
}
}
}
};
染色法,DFS
class Solution {
public:
bool isBipartite(vector<vector<int>>& graph) {
int size = graph.size();
if(size == 0){
return false;
}
// 染色数组,每个顶点的染色情况:0 表示未被染色, 1表示染成黑色 2表示染成白色。
vector<int> color(size, 0);
for(int i = 0; i < size; i++){
if(!dfs(graph, color, i, 0)){
return false;
}
}
return true;
}
// 先找到一个没被染色的节点u,把它染上一种颜色,之后遍历所有与它相连的节点v,如果节点v已被染色并且颜色和节点u一样,那么就不是二分图。
// 如果这个节点v没有被染色,先把它染成与节点u不同颜色的颜色,然后遍历所有与节点v相连的节点...如此递归下去。
bool dfs(vector<vector<int>>& graph, vector<int>& color, int i, int lastColor){
if(color[i] != 0){
// 已经被染色了
return color[i] != lastColor; // 必须要和之前节点颜色,相同颜色和节点u一样就不是二分图
}
// 未被染色时,染色 若lastColor为1 就染2
color[i] = ( (lastColor == 1) ? 2 : 1);
for(int j = 0; j < graph[i].size(); j++){
if(!dfs(graph, color, graph[i][j], color[i])){
return false;
}
}
return true;
}
};
BFS
class Solution {
public:
bool isBipartite(vector<vector<int>>& graph) {
int size = graph.size();
if(size == 0){
return false;
}
// 染色数组,每个顶点的染色情况:0 表示未被染色, 1表示染成黑色 2表示染成白色。
vector<int> color(size, 0);
for(int i = 0; i < size; i++){
if(color[i] == 0){
if(!bfs(graph, color, i)){
return false;
}
}
}
return true;
}
bool bfs(vector<vector<int>>& graph, vector<int>& color, int i){
queue<int> q;
q.push(i);
int lastColor = 0;
while(!q.empty()){
int temp = q.front();
q.pop();
lastColor = color[temp];
for(int j = 0; j < graph[temp].size(); j++){// 邻接节点
// 如果未被染色 放入queue
// 注意这里要用graph获取邻接节点(我脑子抽了直接用j。。。)
int v = graph[temp][j];
if(color[v] == 0){
q.push(v);
// 染色, 与上一次节点的color不一致
color[v] = (lastColor == 1) ? 2 : 1;
}else if(color[v] == lastColor){
// 已经被染色了,且染的颜色和顶点相同的
return false;
}
}
}
return true;
}
};
双百分,有向图求两点间是否存在路径,DFS
class Solution {
public:
// 邻接图
unordered_map<string, vector<pair<string, double>>> m;
// 答案
vector<double> ans;
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
// 初始化邻接图
for(int i = 0; i < equations.size(); i++){
m[equations[i][0]].push_back(pair<string, double>(equations[i][1], values[i]));
m[equations[i][1]].push_back(pair<string, double>(equations[i][0], 1/values[i]));
}
// 遍历
for(auto x: queries){
if(x[0] == x[1]){
// 除数和被除数一样
if(m.find(x[0]) != m.end()){
// 数是存在的
ans.push_back(1.0);
}else{
// 该数在equation中是不存在的,在图中也不存在
ans.push_back(-1.0);
}
}else{
// 两数不相等
unordered_set<string> visited; // 记录访问过的节点
visited.insert(x[0]);
// dfs 有向图两点是否存在路径
if(dfs(x[0], x[1], 1.0, visited)){
// 存在路径
continue;
}else{
// 不存在路径
ans.push_back(-1.0);
}
}
}
return ans;
}
bool dfs(string start, string end, double curAns, unordered_set<string>& visited){
if(start == end){
// 有节点
ans.push_back(curAns);
return true;
}
// 还没找到end的时候
visited.insert(start);
for(auto x: m[start]){
if(visited.find(x.first) != visited.end()){
// 以及访问过了x节点
continue;
}else{
if(dfs(x.first, end, curAns * x.second, visited)){
// 如果有路径
return true;
}
}
}
return false;
}
};
BFS
class Solution {
public:
// 邻接图
unordered_map<string, vector<pair<string, double>>> m;
// 答案
vector<double> ans;
vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
// 初始化邻接图
for(int i = 0; i < equations.size(); i++){
m[equations[i][0]].push_back(pair<string, double>(equations[i][1], values[i]));
m[equations[i][1]].push_back(pair<string, double>(equations[i][0], 1/values[i]));
}
// 遍历
for(auto x: queries){
if(x[0] == x[1]){
// 除数和被除数一样
if(m.find(x[0]) != m.end()){
// 数是存在的
ans.push_back(1.0);
}else{
// 该数在equation中是不存在的,在图中也不存在
ans.push_back(-1.0);
}
}else{
// dfs 有向图两点是否存在路径
if(bfs(x[0], x[1])){
// 存在路径
continue;
}else{
// 不存在路径
ans.push_back(-1.0);
}
}
}
return ans;
}
bool bfs(string start, string end){
// bfs
unordered_set<string> visited;
queue<pair<string, double>> q;
q.push(pair<string, double>(start, 1.0));
visited.insert(start);
while(!q.empty()){
auto temp = q.front();
q.pop();
if(temp.first == end){
// 找到了一条路径
ans.push_back(temp.second);
return true;
}
for(auto x: m[temp.first]){
if(visited.find(x.first) != visited.end()){
// 如果出现在visited中了,之前已经访问过了该节点,跳过
continue;
}else{
// 之前没有出现
q.push(pair<string, double>(x.first, x.second * temp.second));
visited.insert(x.first);
}
}
}
return false;
}
};
拓扑排序
class Solution {
public:
vector<int> eventualSafeNodes(vector<vector<int>>& graph) {
int n = graph.size();
vector<int> outDegree(n, 0); // out degree
vector<vector<int>> revGraph(n, vector<int>{}); // reverse graph
vector<int> ans;
// init outDegree and revGraph
for(int i = 0; i < n; i++){
outDegree[i] = graph[i].size();
for(auto end: graph[i]){
revGraph[end].push_back(i); // reverse graph
}
}
// 将出度为0的点放入队列
queue<int> q;
for(int i = 0; i < n; i++){
if(outDegree[i] == 0){
q.push(i);
}
}
//
while(!q.empty()){
auto temp = q.front();
q.pop();
// 找到出度为0的顶点,这些点是安全的点
ans.push_back(temp);
// 逆向删除以出度为0的顶点为弧头的边,弧尾的出度减一
for(auto start: revGraph[temp]){
outDegree[start]--;
if(outDegree[start] == 0){
q.push(start);
}
}
}
sort(ans.begin(), ans.end());
return ans;
}
};
分层拓扑排序 小组间 小组内
class Solution {
public:
vector<int> sortItems(int n, int m, vector<int>& group, vector<vector<int>>& beforeItems) {
// 构造小组的依赖关系和组内项目的依赖关系。
// 判断小组间是否有冲突,没冲突计算出小组的拓扑排序。
// 判断每个小组内项目是否有冲突,没冲突计算出小组内项目的拓扑排序。
// n个项目,m个小组
// 拓扑排序
int maxGroup = m;
for(int i = 0; i < group.size(); i++){
if(group[i] == -1){
// 这个项目目前无人接手
group[i]= maxGroup++;
}
}
vector<set<int>> groupItem(maxGroup); // 小组内包含的项目
vector<int> groupIndegree(maxGroup, 0); // 小组入度
vector<set<int>> groupGraph(maxGroup); // 小组-图
vector<int> itemIndegree(n, 0); // 每个小组内的项目入度
vector<set<int>> itemGraph(n); // 每个小组内的项目图
// 初始化groupItem,每个小组内包含了哪些任务
for(int i = 0; i < group.size(); i++){
groupItem[group[i]].insert(i);
}
// 初始化 小组入度
for(int i = 0; i < beforeItems.size(); i++){
for(auto bi: beforeItems[i]){
if(group[i] == group[bi]){
// 属于同一个小组的时候
itemIndegree[i]++;
itemGraph[bi].insert(i); // bi->i
}else{
// 不属于同一个小组的时候 同样的两组之间不重复添加边
if(groupGraph[group[bi]].count(group[i]) == 0){
// bi->i bi所在的组
groupIndegree[group[i]]++; // i所在的组入度+1
groupGraph[group[bi]].insert(group[i]); // 构造图
}
}
}
}
// 小组 拓扑排序
vector<int> ans;
queue<int> q;
// 入度为0 放入队列
for(int i = 0; i < maxGroup; i++){
if(groupIndegree[i] == 0){
q.push(i);
}
}
while(!q.empty()){
auto temp = q.front();
q.pop();
ans.push_back(temp);
for(auto& i: groupGraph[temp]){
groupIndegree[i]--;
if(groupIndegree[i] == 0){
q.push(i);
}
}
}
if(ans.size() != maxGroup){
return vector<int>();
}
// 小组内排序
vector<int> res;
for(int i = 0; i < ans.size(); i++){
for(auto& gi: groupItem[ans[i]]){
if(itemIndegree[gi] == 0){
q.push(gi);
}
}
int count = 0;
while(!q.empty()){
auto temp = q.front();
q.pop();
count++;
res.push_back(temp);
for(auto& ig: itemGraph[temp]){
itemIndegree[ig]--;
if(itemIndegree[ig] == 0){
q.push(ig);
}
}
}
if(count != groupItem[ans[i]].size()){
return vector<int>();
}
}
return res;
}
};
并查集
class Solution {
public:
vector<int> findRedundantConnection(vector<vector<int>>& edges) {
vector<int> rp(1001);
int size = edges.size();
// 初始化
for(int i = 0; i < size; i++){
rp[i] = i;
}
for(int i = 0; i < size; i++){
int set1 = find(edges[i][0], rp);
int set2 = find(edges[i][1], rp);
if(set1 == set2){
// 两个代表节点相同 出现了环,返回答案
return edges[i];
}else{
// 两个集合独立,合并合集
rp[set1] = set2;
}
}
return {0, 0};
}
// 查找路径并返回代表节点,实际上就是给定当前节点,返回该节点所在集合的代表节点
int find(int n, vector<int>& rp){
int temp = n;
while(rp[temp] != temp){
temp = rp[temp];
}
return temp;
}
};