下面考虑了非连通图的情况,如果图是连通图,则只需从源点s出发,就可以遍历完图中的所有点了。有关于BFS和DFS的时间复杂度,对于邻接矩阵实现和邻接表的不同实现方式是不同的。对于BFS和DFS,采用邻接表:O(V + E), 邻接矩阵O(V^2)。区别在于,对于v的每个邻接点进行遍历的时候,邻接表只需访问v对应的链表就行,链表中的边的数目对于无向图来说就是2E。而邻接矩阵,每次都需要遍历一遍顶点的数目。具体分析,请参考书籍。
void MatrixGraph::Bfs() {for (int i = 0; i < vertexNum; i++) {visited[i] = false;path[i] = -1;}for (int i = 0; i < vertexNum; i++) {if (!visited[i]) {BfsStartNode(i);}}
}void MatrixGraph::BfsStartNode(int s) {visited[s] = true; //先访问该节点,再将该节点加入队列queue que;que.push(s);while (!que.empty()) {int v = que.front();que.pop();for (auto w : Adj(v)) {if (!visited[w]) {visited[w] = true;path[w] = v;que.push(w);}}}
}void MatrixGraph::Dfs() {for (int i = 0; i < vertexNum; i++) {visited[i] = false;path[i] = -1;}for (int i = 0; i < vertexNum; i++) {if (!visited[i]) {DfsStartNode(i);}}
}void MatrixGraph::DfsStartNode(int v) {visited[v] = true;for (auto w : Adj(v)) {if (!visited[w]) {DfsStartNode(w);}}
}
令集合S = {源点s + 已经确定了的到源点s最短路径的顶点Vi}
对任一未收录进入S中的顶点v,定义dist[v]为源点s到v的最短路径的长度,该最短路径所经过的点都是在集合S中的点。
即路径{ s ----- (vi 属于 S) ----- v}的最小长度。
若路径是按照递增的顺序生成的,则
1、真正的最短路径必须只经过S中的顶点。
假设下一次将v收入集合时,从集合S到v这个路径上还存在另外一个点w。这个w是在集合S以外的。那从集合S到v,一定要先从S到w,再从w到v。这时就有矛盾,s—w的距离显然小于s—v的距离。而v是下一个马上被收入集合中的顶点,而路径是按照递增序生成的,凡是比v小的点,在v之前就已经收录于集合S中了。w—s的距离小于v—s的距离,则w肯定已经在集合S中了。
2、每次从未收录的顶点中选一个dist最小的收录(贪心),但是将v收入S之中,它可能会影响其他顶点到源点s的最小长度。
3、增加一个v进入S,可能影响另一个w的dist的值,把v加入,使得s—w的dist的值更小,则v一定就在s—w的路径上。v不仅在w的路径上,并且从v到w,必定存在一条直接的边。(w一定是v的邻接点)。v加入集合后,仅能影响的就是它的邻接点。
void Dijkstra(Vertex s) {初始化dist存储从源点s到w最短距离dist[w]dist[s] = 0,源点s的邻接点的dist的值为其边的值初始化path全部为-1,刚开始没有最短路径collected数组表示已经收录于集合S中的顶点,初始化全部为falsewhile (1) {v = 未收录顶点中dist值最小者if (这样的v不存在,所有的顶点已经收录于集合S中) {break;}collected[v] = true; //标记将v收录于集合S中for (v的每个邻接点w) { //update operationif (collected[w] == false) {if (dist[v] + E < dist[w]) {dist[w] = dist[v] + E;path[w] = v;}}}
}
该算法的时间复杂度,如果是直接扫描所有未收录的顶点,判断其dist的最小的顶点,则为
O(V^2 + E)。while循环为V次,每次扫描一遍顶点也需要V次,for循环对v的每个邻接点w的处理,总共相当于遍历了一遍图中的所有的边E。
若是将dist的值存于最小堆,则获取未收录顶点中dist值最小者为O(logV),在更新dist值的时候也需要O(logV)的时间复杂度。总的时间复杂度为O(VlogV + ElogV),当为稀疏图时,近似于O(VlogV)。如果对于E不是V^2的数量级,而是V的数量级,这种方式效果要好一些。
void MatrixGraph::ShortestPathDijkstra(int s) {vector dist; //该数组存储从源点s到w的最短距离, dist[s] = 0;vector path; //该数组储存从源点s到w的路径上经过的点, s-->w path[w] = svector collected; //该数组表示当前结点是否已经收录于最短路径集合中了for (int i = 0; i < vertexNum; i++) {dist.push_back(65535);path.push_back(-1);collected.push_back(false);}dist[s] = 0;for (auto w : Adj(s)) {dist[w] = ptrGraph[s][w];}while (1) {int minValue = 65535;int v;for (int j = 0; j < vertexNum; j++) {if (!collected[j] && dist[j] < minValue) {minValue = dist[j];v = j;}}if (minValue == 65535) {break;}collected[v] = true; //update operationfor (auto w : Adj(v)) {if (collected[w] == false) {if (dist[v] + ptrGraph[v][w] < dist[w]) {dist[w] = dist[v] + ptrGraph[v][w];path[w] = v;}}}}
}
方法一:直接将单源最短路径算法调用V遍
T = O(V^3 + E*V),方法一对稀疏图效果好
方法二:Floyd算法
D^(k)[i][j] = 路径{ i — { l <= k } — j }的最小长度
D^0 D^1 … D^(v-1)[i][j]即给出了i到j的真正最短距离
最初的D^-1是什么
当D的(k-1)次方已经完成递推到D^K时:
或者k不属于最短路径{ i — { l <= k } — j },则D的(k-1)次方等于D的k次方
或者k属于最短路径{ i — { l <= k } — j },则该路径必定由两段最短路径组成:
D^(K)[i][j] = D^(k-1)[i][k] + D^(k-1)[k][j]
void Floyd() {for(i = 0; i < N; i++)for(j = 0; j < N; j++){D[i][j] = G[i][j];path[i][j] = -1;}for(k = 0; k < N; k++)for(i = 0; i < N; i++)for(j = 0; j < N; j++)if(D[i][k] + D[k][j] < D[i][j]){D[i][j] = D[i][k] + D[k][j];path[i][j] = k;}
}
void Print(int i, int j)
{if(i == j) return 0;Print(i, path[i][j]); // i --- kcout << path[i][j] << endl;Print(path[i][j], j); // k --- j
}
void Prim () {MST = {s};while (true) {v = 未收录的顶点中cost最小者,即距离现有的最小生成树cost最小的那个顶点if (这样的v不存在) break; // 1、顶点全部被收录 2、所有没收录的顶点cost都是无穷大,表明剩下的顶点到最小生成树没有边,图不连通将顶点v收录进入MST中for (v的每个邻接点w) {if (w未被收录) {if (cost(v,w) < lowcost[w]) {lowcost[w] = cost(v,w); //更新未收录集合中和v相连的顶点w到MST的最小代价parent[w] = v;}}}}if (MST集合中收录的顶点数目不到v个) ERROR(图不连通);
}
//lowcost数组存储的是一个顶点v到最小生成树集MST中所有顶点的代价最小的值,初始化为cost(s,v)或者正无穷
void Prim() {vector lowcost(vertexnum, INT_MAX); vector path(vertexnum, -1); // s->w ---- path[w] = svector collected(vertexnum, false); //将图中的顶点分为了两个集合,collected[i] == true, 表示该顶点已经收录于MST中lowcost[s] = -1; for (auto w : adj(s)) lowcost[w] = g[s][w];while (1) {int min = INT_MAX;int v;for (int j = 0; j < vertexnum; j++) {if (!collected[j] && lowcost[j] < min) {min = lowcost[j];v = j;}}// 1、顶点全部被收录 2、所有没收录的顶点cost都是无穷大,表明剩下的顶点到最小生成树没有边,图不连通if (min == INT_MAX) break; collected[v] = true;for (auto w : adj(v)) {if (!collected[w]) {if (g[v][w] < lowcost[w]) {lowcost[w] = g[v][w];path[w] = v;}}}}for (int k = 0; k < vertexnum; k++) { //MST集合中收录的顶点不到verternum个if (collected[k] == false) {//error}}
}
prim
class Solution {int minSpanTreePrim(vector>& points, int s) {int n = points.size();int result = 0;vector> g(n, vector(n, 0));//将points转换成邻接矩阵for (int i = 0; i < n; i++) {for (int j = i+1; j < n; j++) {int cost = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);g[i][j] = cost;g[j][i] = cost;}}vector lowcost(n, INT_MAX);vector collected(n, false);vector path(n, s);collected[s] = true;for (int j = 0; j < n; j++) {if (j == s) continue;lowcost[j] = g[s][j];}int flag = 1;while (true) {int min = INT_MAX;int v;for (int i = 0; i < n; i++) {if (!collected[i] && lowcost[i] < min) {min = lowcost[i];v = i;}}if (min == INT_MAX) break;collected[v] = true;if (flag) { //对第一个开始的顶点s单独续接上路径path[v] = s;flag--;}result += g[path[v]][v];for (int w = 0; w < n; w++) {if (w == v) continue;if (!collected[w]) {if (g[v][w] < lowcost[w]) {lowcost[w] = g[v][w];path[w] = v;}}} }return result;}
public:int minCostConnectPoints(vector>& points) {return minSpanTreePrim(points, 0);}
};
prim + priority_queue
class Solution {struct Edge {int a;int b;int weight;Edge(int _a, int _b, int _weight) : a(_a), b(_b), weight(_weight) {}};struct cmp {bool operator()(const Edge& e1, const Edge& e2) {return e1.weight > e2.weight; //按照value从小到大排列}}; int minSpanTreePrim(vector>& points, int s) {int n = points.size(); // vertex numberint result = 0;// 将points转化成邻接表vector> g(n);for (int i = 0; i < n; i++) {for (int j = i+1; j < n; j++) {g[i].push_back(j);g[j].push_back(i);}}//记录顶点i到最小生成树的最近距离vector lowcost(n, INT_MAX);//记录顶点i是否加入了最小生成树中vector collected(n, false);priority_queue, cmp> pq;pq.push(Edge(s,s,0));while (!pq.empty()) {Edge e = pq.top();pq.pop();if (collected[e.b]) continue; //如果该顶点已经在MinSpanTree集合了continuecollected[e.b] = true; //否则将该顶点收录进入MinSpanTree集合,并且将该顶点和它相连的上一个顶点所组成的边权值加入resultresult += e.weight;for (int k = 0; k < g[e.b].size(); k++) { //处理加入MinSpanTree顶点的邻接顶点int j = g[e.b][k];int w = abs(points[e.b][0] - points[j][0]) + abs(points[e.b][1] - points[j][1]);if (!collected[j] && lowcost[j] > w) {lowcost[j] = w;pq.push(Edge(e.b, j, w));}}}return result; }
public:int minCostConnectPoints(vector>& points) {return minSpanTreePrim(points, 0); //start from vertex index 0}
};
Kruskal算法实现思想(并查集)
实现思想:将各边按代价值排序,共执行E轮,每轮判断两个顶点是否属于同一个集合,需要O(logE),总时间复杂度O(ElogE)。
1、初始化并查集,按权值递增次序处理各条边
2、通过Find确定一条边所连接的两个顶点是否属于同一个集合,如果不属于同一个集合,则将这条边加入生成树,并将这两个顶点所属集合Union
typedef struct {int a;int b;int weight;
}Edge;
void Initial(vector s) {for (int i = 0; i < s.size(); i++) {s[i] = -1;}
}
int Find(vector s, int x) {while (s[x] >= 0) {x = s[x];}return x;
}
//用根节点的绝对值表示树的结点总数
//1、用根节点的绝对值表示树的结点总数
//2、Union操作让小树合并到大树
void Union(vector s, int root1, int root2) {if (root1 == root2) return;if (s[root1] < s[root2]) {s[root1] += s[root2];s[root2] = root1; }else {s[root2] += s[root1];s[root1] = root2;}
}
void Kruskal(MGraph G, vector vec) {int n = vertexnum;int s[n];for (int i = 0; i < n; i++) {s[i] = -1;}Edge edges[Edgenum];sort(edges);for (int i = 0; i < Edgenum; i++) {int roota = Find(s, edges[i].a);int rootb = Find(s, edges[i].b);if (roota != rootb) {vec.push(edges[i]);Union(s, roota, rootb);}}
}
class Solution {struct Edge {int a;int b;int weight;Edge(int _a, int _b, int _weight) : a(_a), b(_b), weight(_weight) {}};static bool cmp(const Edge& e1, const Edge& e2) {return e1.weight < e2.weight;}//Find优化:查询路径压缩int Find(vector& s, int x) {int root = x;while (s[root] >= 0) {root = s[root];}while (x != root) {int t = s[x];s[x] = root;x = t;}return root;}//Union优化:用根节点的绝对值表示树的结点总数,让小树合并到大树void Union(vector& s, int roota, int rootb) {if (roota == rootb) return;if (s[roota] <= s[rootb]) {s[roota] += s[rootb];s[rootb] = roota;}else{s[rootb] += s[roota];s[roota] = rootb;}}int minSpanTreeKruskal(vector>& points) {int result = 0;int n = points.size();vector edges;vector s(n, -1); // initial find union for (int i = 0; i < n; i++) {for (int j = i+1; j < n; j++) {int cost = abs(points[i][0] - points[j][0]) + abs(points[i][1] - points[j][1]);edges.push_back(Edge(i, j, cost));}}sort(edges.begin(), edges.end(), cmp);for (int i = 0; i < edges.size(); i++) {int a = edges[i].a;int b = edges[i].b;int roota = Find(s, a);int rootb = Find(s, b);if (roota != rootb) {result += edges[i].weight;Union(s, roota, rootb);}}return result;}
public:int minCostConnectPoints(vector>& points) {return minSpanTreeKruskal(points);}
};
class Solution {
public:bool Topologicalsort(vector>& g, int n) {vector indegree(n, -1);for (int j = 0; j < n; j++) {int cnt = 0;for (int i = 0; i < n; i++) {if (g[i][j] == 1) {cnt++;}}indegree[j] = cnt;}stack sck;for (int i = 0; i < n; i++) {if (indegree[i] == 0) {sck.push(i);}}int cnt = 0;while (!sck.empty()) {int v = sck.top();sck.pop();cnt++;for (int j = 0; j < n; j++) {if (g[v][j] == 0) continue;if (!(--indegree[j])) {sck.push(j);}}}if (cnt < n) return false;else return true;}bool canFinish(int numCourses, vector>& prerequisites) {int n = numCourses;vector> g(n, vector(n, 0));for (int i = 0; i < prerequisites.size(); i++) {g[prerequisites[i][1]][prerequisites[i][0]] = 1; // bi ---> ai}return Topologicalsort(g, n);}
};
其实就是深拷贝的一个实现,深拷贝就是对于所有的指针成员,不能仅仅是赋值,还有重新分配空间。
深拷贝反应在本题中就是,所有的结点需要重新new出来,而不是直接赋值。
整体的思路依然是dfs,跑遍原图中每个结点,然后根据原结点生成新结点。
要注意的地方就是,因为图存在环,所以要标记访问过的结点,避免重复形成死循环:
如果该结点已经被访问过,则不再遍历该结点,而是直接将指针值赋给自己邻接点数组----浅拷贝;
如果该结点没有被访问过,则继续dfs遍历该结点,并将产生的新结点----深拷贝;
//注意每个节点的值都和它的索引相同
class Solution {
public:Node* cloneGraph(Node* node) {vector visited(101, false);unordered_map hashMap;if (node == NULL) return node;return dfs(hashMap, visited, node);}Node* dfs(unordered_map& hashMap, vector& visited, Node* node) {Node* root = new Node(node->val);visited[root->val] = true;hashMap[root->val] = root;for (int i = 0; i < node->neighbors.size(); i++) {if (!visited[node->neighbors[i]->val]) {root->neighbors.push_back(dfs(hashMap, visited, node->neighbors[i])); //深拷贝}else {root->neighbors.push_back(hashMap[node->neighbors[i]->val]); //浅拷贝}}return root;}
};
class Solution {
public:int Find(vector s, int x) {int root = x;while (s[root] >= 0) {root = s[root];}while (x != root) {int tmp = s[x];s[x] = root;x = tmp;}return root;}void Union(vector& s, int roota, int rootb) {if (s[roota] <= s[rootb]) {s[roota] += s[rootb];s[rootb] = roota;}else{s[rootb] += s[roota];s[roota] = rootb; }}int findCircleNum(vector>& isConnected) {int n = isConnected.size();vector s(n, -1);for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (isConnected[i][j] == 1) {int rooti = Find(s, i);int rootj = Find(s, j);if (rooti != rootj) {Union(s, rooti, rootj);}}}}int cnt = 0;for (int i = 0; i < s.size(); i++) {if (s[i] < 0) {cnt++;}}return cnt;}
};
class Solution {
public:vector findMinHeightTrees(int n, vector>& edges) {// 根据边表edges 创建无向邻接表vector> g(n);for (int i = 0; i < edges.size(); i++) {int a = edges[i][0];int b = edges[i][1];g[a].push_back(b);g[b].push_back(a);}// 从每个顶点依次层序遍历,记录最小层数的顶点int minHigh = INT_MAX;vector result;for (int i = 0; i < n; i++) {int high = BFS(g, i);if (high > minHigh) continue;if (high == minHigh) {result.push_back(i);}else {result.clear();result.push_back(i);minHigh = high;}}return result;}//BFS广度优先搜索,返回最大深度int BFS(const vector>& g, int s) {int n = g.size();if (s >= n) return -1;vector visited(n, false);queue que;que.push(s); //将顶点Push进入队列后,标记visited标志位visited[s] = true;int high = 0;while (!que.empty()) {int size = que.size();high++;for (int i = 0; i < size; i++) {int v = que.front();que.pop();for (auto w : g[v]) {if (visited[w]) continue;que.push(w);visited[w] = true;}}}return high;}
};
//递归函数直接传参数进去,递归返回时,下一层的改变不会影响上一层//递归函数传递引用,递归返回时,下一层的改变会影响到上一层,如果我们需要回退状态,就不希望下一层的改变影响到上一层的结果int DFS(vector>& g, vector visited, int s) {visited[s] = true;int ret = 0;for (auto w : g[s]) {if (!visited[w]) {ret = max(ret, DFS(g, visited, w));}}return ret + 1;}
class Solution {
public:// // (v1|v2) = v1 * 1e5 + v2// v1:start v2:rootunordered_map memo;vector findMinHeightTrees(int n, vector>& edges) {// 根据边表edges 创建无向邻接表vector> g(n);for (int i = 0; i < edges.size(); i++) {int a = edges[i][0];int b = edges[i][1];g[a].push_back(b);g[b].push_back(a);}// 从每个顶点依次层序遍历,记录最小层数的顶点int minHigh = INT_MAX;vector result;for (int i = 0; i < n; i++) {int high = DFS(g, i, -1);if (high > minHigh) continue;if (high == minHigh) {result.push_back(i);}else {result.clear();result.push_back(i);minHigh = high;}}return result;}// 从start结点开始深度遍历其子树返回最长/深子树的深度,root为避免继续遍历的结点int DFS(const vector>& adjList, int start, int root) {int key = start * 1e5 + root;if (memo.count(key)) return memo[key];int ret = 0;for (auto adj : adjList[start]) {if (adj == root) continue;ret = max(ret, DFS(adjList, adj, start));}memo[key] = ret + 1;return ret + 1;}
};
class Solution {
public:vector findMinHeightTrees(int n, vector>& edges) {if (n == 1) return {0};// 根据边表edges 创建邻接表和各节点度数vector> g(n);vector degree(n, 0);for (int i = 0; i < edges.size(); i++) {int a = edges[i][0];int b = edges[i][1];g[a].push_back(b);g[b].push_back(a);degree[a]++;degree[b]++;}//将目前图中的叶子节点压入队列queue que;vector result;for (int i = 0; i < n; i++) {if (degree[i] == 1) {que.push(i);}}//从最外层向内,每次加入新的一层进入队列,消掉队列的上一层节点while (!que.empty()) {result.clear();int size = que.size();for (int i = 0; i < size; i++) {int v = que.front();que.pop();result.push_back(v);degree[v]--; //判断与结点v相连的顶点for (auto w : g[v]) {degree[w]--;if (degree[w] == 1) {que.push(w);}} }}return result;}
};
class Solution {
public:void BFS(vector>>& graph, int n, int start, int end, double& wgt) {if (start == end) {wgt = 1.0;return;}queue que;vector visited(n, false);vector weight(n, -1.0);que.push(start);weight[start] = 1.0;visited[start] = true;while (!que.empty() && !visited[end]) {int v = que.front();que.pop();for (auto ele : graph[v]) {int w = ele.first;double val = ele.second;if (visited[w]) continue;weight[w] = weight[v] * val;que.push(w);visited[w] = true;}}wgt = weight[end];return;}vector calcEquation(vector>& equations, vector& values, vector>& queries) {//构建字符结点和结点索引编号的映射关系int nodeId = 0;unordered_map hashMap;for (int i = 0; i < equations.size(); i++) {if (hashMap.find(equations[i][0]) == hashMap.end()) {hashMap[equations[i][0]] = nodeId++;} if (hashMap.find(equations[i][1]) == hashMap.end()) {hashMap[equations[i][1]] = nodeId++;}}//建图,邻接表,因为是无向带权图,每个结点记录它的连接关系的同时,还需要记录它和其他结点权重vector>> graph(nodeId);for (int i = 0; i < equations.size(); i++) {int idv = hashMap[equations[i][0]];int idw = hashMap[equations[i][1]];graph[idv].push_back({idw, values[i]});graph[idw].push_back({idv, 1 / values[i]});}//计算图中存在的两个结点之间的权值vector result;for (auto query : queries) {double answer = -1.0;if (hashMap.find(query[0]) != hashMap.end() && hashMap.find(query[1]) != hashMap.end()) {int ida = hashMap[query[0]];int idb = hashMap[query[1]];BFS(graph, nodeId, ida, idb, answer);}result.push_back(answer);}return result;}
};
class Solution {
public:void DFS(vector>>& graph, vector visited, int start, int end, double wgt, double& answer) {if (start == end) {answer = wgt;return;}visited[start] = true;for (auto ele : graph[start]) {int v = ele.first;double val = ele.second;if (visited[v]) continue;wgt = wgt * val;DFS(graph, visited, v, end, wgt, answer);wgt = wgt / val;}}vector calcEquation(vector>& equations, vector& values, vector>& queries) {//构建字符结点和结点索引编号的映射关系int nodeId = 0;unordered_map hashMap;for (int i = 0; i < equations.size(); i++) {if (hashMap.find(equations[i][0]) == hashMap.end()) {hashMap[equations[i][0]] = nodeId++;} if (hashMap.find(equations[i][1]) == hashMap.end()) {hashMap[equations[i][1]] = nodeId++;}}//建图,邻接表,因为是无向带权图,每个结点记录它的连接关系的同时,还需要记录它和其他结点权重vector>> graph(nodeId);for (int i = 0; i < equations.size(); i++) {int idv = hashMap[equations[i][0]];int idw = hashMap[equations[i][1]];graph[idv].push_back({idw, values[i]});graph[idw].push_back({idv, 1 / values[i]});}//计算图中存在的两个结点之间的权值vector result;for (auto query : queries) {double answer = -1.0;if (hashMap.find(query[0]) != hashMap.end() && hashMap.find(query[1]) != hashMap.end()) {int ida = hashMap[query[0]];int idb = hashMap[query[1]];vector visited(nodeId, false);DFS(graph, visited, ida, idb, 1.0 ,answer);}result.push_back(answer);}return result;}
};
class Solution {
public:void floydFunc(vector>& graph, int n) {for (int k = 0; k < n; k++) {for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) {if (graph[i][k] > 0 && graph[k][j] > 0) { //i-->k k--->j connectedgraph[i][j] = graph[i][k] * graph[k][j];}}}}}vector calcEquation(vector>& equations, vector& values, vector>& queries) {//构建字符结点和结点索引编号的映射关系int nodeId = 0;unordered_map hashMap;for (int i = 0; i < equations.size(); i++) {if (hashMap.find(equations[i][0]) == hashMap.end()) {hashMap[equations[i][0]] = nodeId++;} if (hashMap.find(equations[i][1]) == hashMap.end()) {hashMap[equations[i][1]] = nodeId++;}}//建图,邻接矩阵vector> graph(nodeId, vector(nodeId, -1.0));for (int i = 0; i < equations.size(); i++) {int idv = hashMap[equations[i][0]];int idw = hashMap[equations[i][1]];graph[idv][idw] = values[i];graph[idw][idv] = 1 / values[i];}floydFunc(graph, nodeId);//计算图中存在的两个结点之间的权值vector result;for (auto query : queries) {double answer = -1.0;if (hashMap.find(query[0]) != hashMap.end() && hashMap.find(query[1]) != hashMap.end()) {int ida = hashMap[query[0]];int idb = hashMap[query[1]];if (graph[ida][idb] > 0) answer = graph[ida][idb];}result.push_back(answer);}return result;}
};
class Solution {
public:int Find(vector& s, int x) {int root = x;while (s[root] >= 0) {root = s[root];}while (x != root) {int t = s[x];s[x] = root;x = t;}return root;}void Union(vector& s, int roota, int rootb) {if (roota <= rootb) {s[roota] += s[rootb];s[rootb] = roota;}else {s[rootb] += s[roota];s[roota] = rootb;}}bool equationsPossible(vector& equations) {int n = 26;vector> graph(n, vector(n, 0));for (auto str : equations) {if (str[1] == '=') {int v = str[0] - 'a';int w = str[3] - 'a';graph[v][w] = 1;graph[w][v] = 1;}}vector s(n, -1); for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {if (graph[i][j] == 1) {int rooti = Find(s, i);int rootj = Find(s, j);if (rooti != rootj) Union(s, rooti, rootj);}}}for (auto str : equations) {if (str[1] == '!') {int a = str[0] - 'a';int b = str[3] - 'a';int roota = Find(s, a);int rootb = Find(s, b);if (roota == rootb) {return false;}}}return true;}
};
class Solution {
public:int Find(vector& s, int x) {int root = x;while (s[root] >= 0) {root = s[root];}while (x != root) {int t = s[x];s[x] = root;x = t;}return root;}void Union(vector& s, int a, int b) {int roota = Find(s, a);int rootb = Find(s, b);if (roota == rootb) return;if (s[roota] <= s[rootb]) {s[roota] += s[rootb];s[rootb] = roota;}else {s[rootb] += s[roota];s[roota] = rootb;}}vector findRedundantConnection(vector>& edges) {vector> result;vector s(1001, -1);for (auto edge : edges) {int v = edge[0];int w = edge[1];if (Find(s, v) == Find(s, w)) {result.push_back(edge);}else {Union(s, v, w);}}return result.back();}
};