• LeetCode 【算法专栏】 【图】


    有关BFS和DFS

    下面考虑了非连通图的情况,如果图是连通图,则只需从源点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<int> 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);
    		}
    	}
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    求有权图的单源最短路径算法(Dijkstra算法)

    令集合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中的顶点,初始化全部为false
    	while (1) {
    		v = 未收录顶点中dist值最小者
    		if (这样的v不存在,所有的顶点已经收录于集合S中) {
    			break;
    		}
    		collected[v] = true;                    //标记将v收录于集合S中
    		for (v的每个邻接点w) {                   //update operation
    		if (collected[w] == false) {
    			if (dist[v] + E<v, w> < dist[w]) {
    				dist[w] = dist[v] + E<v, w>;
    				path[w] = v;
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    该算法的时间复杂度,如果是直接扫描所有未收录的顶点,判断其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<int> dist;        //该数组存储从源点s到w的最短距离, dist[s] = 0;
    	vector<int> path;        //该数组储存从源点s到w的路径上经过的点, s-->w path[w] = s
    	vector<bool> 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 operation
    		for (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;
    				}
    			}
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    求有权图的多源最短路径算法(Floyd算法)

    方法一:直接将单源最短路径算法调用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 --- k
    	cout << path[i][j] << endl;
    	Print(path[i][j], j);     // k --- j
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    最小生成树—Prim算法

    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(图不连通);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    //lowcost数组存储的是一个顶点v到最小生成树集MST中所有顶点的代价最小的值,初始化为cost(s,v)或者正无穷
    void Prim() {
        vector<int> lowcost(vertexnum, INT_MAX);   
        vector<int> path(vertexnum, -1);             // s->w  ---- path[w] = s
        vector<bool> 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
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    leetcode 1584 连接所有点的最小费用(prim+priority_queue)

    prim

    class Solution {
        int minSpanTreePrim(vector<vector<int>>& points, int s) {
            int n = points.size();
            int result = 0;
            vector<vector<int>> g(n, vector<int>(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<int>  lowcost(n, INT_MAX);
            vector<bool> collected(n, false);
            vector<int>  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<vector<int>>& points) {
            return minSpanTreePrim(points, 0);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    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<vector<int>>& points, int s) {
            int n = points.size();      // vertex number
            int result = 0;
            // 将points转化成邻接表
            vector<vector<int>> 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<int> lowcost(n, INT_MAX);
            //记录顶点i是否加入了最小生成树中
            vector<bool> collected(n, false);
            priority_queue<Edge, vector<Edge>, cmp> pq;
            pq.push(Edge(s,s,0));
    
            while (!pq.empty()) {
                Edge e = pq.top();
                pq.pop();
                if (collected[e.b]) continue; //如果该顶点已经在MinSpanTree集合了continue
                collected[e.b] = true;        //否则将该顶点收录进入MinSpanTree集合,并且将该顶点和它相连的上一个顶点所组成的边权值加入result
                result += 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<vector<int>>& points) {
            return minSpanTreePrim(points, 0);   //start from vertex index 0
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    Kruskal算法实现思想(并查集)

    Kruskal算法实现思想(并查集)
    实现思想:将各边按代价值排序,共执行E轮,每轮判断两个顶点是否属于同一个集合,需要O(logE),总时间复杂度O(ElogE)。
    1、初始化并查集,按权值递增次序处理各条边
    2、通过Find确定一条边所连接的两个顶点是否属于同一个集合,如果不属于同一个集合,则将这条边加入生成树,并将这两个顶点所属集合Union

    typedef struct {
        int a;
        int b;
        int weight;
    }Edge;
    void Initial(vector<int> s) {
        for (int i = 0; i < s.size(); i++) {
            s[i] = -1;
        }
    }
    int Find(vector<int> s, int x) {
        while (s[x] >= 0) {
            x = s[x];
        }
        return x;
    }
    //用根节点的绝对值表示树的结点总数
    //1、用根节点的绝对值表示树的结点总数
    //2、Union操作让小树合并到大树
    void Union(vector<int> 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<Edge> 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);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    leetcode 1584 连接所有点的最小费用(Kruskal UnionFind)

    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<int>& 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<int>& 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<vector<int>>& points) {
            int result = 0;
            int n = points.size();
            vector<Edge> edges;
            vector<int> 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<vector<int>>& points) {
            return minSpanTreeKruskal(points);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65

    拓扑排序Topologicalsort

    class Solution {
    public:
        bool Topologicalsort(vector<vector<int>>& g, int n) {
            vector<int> 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<int> 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<vector<int>>& prerequisites) {
            int n = numCourses;
            vector<vector<int>> g(n, vector<int>(n, 0));
            for (int i = 0; i < prerequisites.size(); i++) {
                g[prerequisites[i][1]][prerequisites[i][0]] = 1;     // bi ---> ai
            }
            return Topologicalsort(g, n);
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    leetcode 133 克隆图(DFS + DeepCopy + ShadowCopy)

    其实就是深拷贝的一个实现,深拷贝就是对于所有的指针成员,不能仅仅是赋值,还有重新分配空间。
    深拷贝反应在本题中就是,所有的结点需要重新new出来,而不是直接赋值。
    整体的思路依然是dfs,跑遍原图中每个结点,然后根据原结点生成新结点。
    要注意的地方就是,因为图存在环,所以要标记访问过的结点,避免重复形成死循环:
    如果该结点已经被访问过,则不再遍历该结点,而是直接将指针值赋给自己邻接点数组----浅拷贝;
    如果该结点没有被访问过,则继续dfs遍历该结点,并将产生的新结点----深拷贝;

    //注意每个节点的值都和它的索引相同
    class Solution {
    public:
        Node* cloneGraph(Node* node) {
            vector<bool> visited(101, false);
            unordered_map<int, Node*> hashMap;
            if (node == NULL) return node;
            return dfs(hashMap, visited, node);
        }
        Node* dfs(unordered_map<int, Node*>& hashMap, vector<bool>& 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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    leetcode 547 省份数量(UnionFind)

    
    class Solution {
    public:
        int Find(vector<int> 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<int>& 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<vector<int>>& isConnected) {
            int n = isConnected.size();
            vector<int> 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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    leetcode 310 最小高度树(BFS DFS Topologicalsort)

    class Solution {
    public:
    	vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
    		// 根据边表edges 创建无向邻接表
    		vector<vector<int>> 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<int> 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<vector<int>>& g, int s) {
    		int n = g.size();
    		if (s >= n) return -1;
    		vector<bool> visited(n, false);
    		queue<int> 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;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
        //递归函数直接传参数进去,递归返回时,下一层的改变不会影响上一层
        //递归函数传递引用,递归返回时,下一层的改变会影响到上一层,如果我们需要回退状态,就不希望下一层的改变影响到上一层的结果
        int DFS(vector<vector<int>>& g, vector<bool> 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;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    class Solution {
    public:
        //  
        // (v1|v2) = v1 * 1e5 + v2
        // v1:start  v2:root
        unordered_map<int, int> memo;
    
    	vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
    		// 根据边表edges 创建无向邻接表
    		vector<vector<int>> 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<int> 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<vector<int>>& 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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    class Solution {
    public:
    	vector<int> findMinHeightTrees(int n, vector<vector<int>>& edges) {
    		if (n == 1) return {0};
            // 根据边表edges 创建邻接表和各节点度数
    		vector<vector<int>> g(n);
            vector<int> 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<int> que;
            vector<int> 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;
    	}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    leetcode 399 除法求值(BFS DFS Floyd)

    class Solution {
    public:
        void BFS(vector<vector<pair<int, double>>>& graph, int n, int start, int end, double& wgt) {
            if (start == end) {
                wgt = 1.0;
                return;
            }
            queue<int> que;
            vector<bool> visited(n, false);
            vector<double> 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<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
            //构建字符结点和结点索引编号的映射关系
            int nodeId = 0;
            unordered_map<string, int> 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<vector<pair<int, double>>> 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<double> 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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    class Solution {
    public:
        void DFS(vector<vector<pair<int, double>>>& graph, vector<bool> 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<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
            //构建字符结点和结点索引编号的映射关系
            int nodeId = 0;
            unordered_map<string, int> 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<vector<pair<int, double>>> 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<double> 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<bool> visited(nodeId, false);
                    DFS(graph, visited, ida, idb, 1.0 ,answer);
                }
                result.push_back(answer);
            }
            return result;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    class Solution {
    public:
        void floydFunc(vector<vector<double>>& 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 connected
                            graph[i][j] = graph[i][k] * graph[k][j];
                        }
                    }
                }
            }
        }
        vector<double> calcEquation(vector<vector<string>>& equations, vector<double>& values, vector<vector<string>>& queries) {
            //构建字符结点和结点索引编号的映射关系
            int nodeId = 0;
            unordered_map<string, int> 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<vector<double>> graph(nodeId, vector<double>(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<double> 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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    leetcode 990 等式方程的可满足性(UnionFind)

    class Solution {
    public:
        int Find(vector<int>& 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<int>& 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<string>& equations) {
            int n = 26;
            vector<vector<int>> graph(n, vector<int>(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<int> 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;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59

    leetcode 684 冗余连接(UnionFind)

    class Solution {
    public:
        int Find(vector<int>& 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<int>& 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<int> findRedundantConnection(vector<vector<int>>& edges) {
            vector<vector<int>> result;
            vector<int> 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();
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
  • 相关阅读:
    【前端寻宝之路】学习和总结CSS的字体属性设置
    竞赛 行人重识别(person reid) - 机器视觉 深度学习 opencv python
    预约按摩app小程序开发搭建;
    Java 代码和使用steam流(List对象使用流操作示例,Java正则匹配,获取当前操作系统)
    windows版本-idea中下载的java版本在哪
    HBASE集群主节点迁移割接手动操作步骤
    C++ - std::string字符串格式化方法总结
    大比拼:讯飞星火大模型将超越ChatGPT?
    艾瑞泽5汽车电子控制单元CAN通信数据读写车辆网络系统交互接口
    深入解析kubernetes中的选举机制
  • 原文地址:https://blog.csdn.net/CLZHIT/article/details/122687671