图的相关算法(更新中)

基本分类:有向图、无向图、加权图

本节所有的算法都是针对于图的基本用法阐述的,关于其他的延伸的比如DFS的回溯和剪枝在此不做讲解

并查集

此处介绍的是一种数据结构

解决问题:图的连通性问题

最终版本(find quick、union quick、按秩合并、基于路径压缩我就不在此一一介绍了,直接给出最终的优化版本吧)

class UnionFind {
	public:
        void UnionFind(int size) {
            root = new int[size];
            rank = new int[size];
            for (int i = 0; i < size; i++) {
                root[i] = i;
                rank[i] = 1; 
            }
        }
        int find(int x) {
            if (x == root[x]) {
                return x;
            }
            return root[x] = find(root[x]);
        }
        void union(int x, int y) {
            int rootX = find(x);
            int rootY = find(y);
            if (rootX != rootY) {
                if (rank[rootX] > rank[rootY]) {
                    root[rootY] = rootX;
                } else if (rank[rootX] < rank[rootY]) {
                    root[rootX] = rootY;
                } else {
                    root[rootY] = rootX;
                    rank[rootX] += 1;
                }
            }
        };
        bool connected(int x, int y) {
            return find(x) == find(y);
        }
    private:
    	int* root;
    	int* rank;
}

leetcode547

class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        if (isConnected.size() == 0) {
            return 0;
        }
        int n = isConnected.size();
        UnionFind* uf = new UnionFind(n);
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                if (isConnected[i][j] == 1) {
                    uf->unionFunc(i, j);
                }
            }
        }
        return uf->getCount();
    }
    class UnionFind {
        public:
            UnionFind(int size) {
                root = new int[size];
                rank = new int[size];
                count = size;
                for (int i = 0; i < size; i++) {
                    root[i] = i;
                    rank[i] = 1;
                }
            }
            int find(int x) {
                if (x == root[x]) {
                    return x;
                }
                return root[x] = find(root[x]);
            }
            void unionFunc(int x, int y) {
                int rootX = find(x);
                int rootY = find(y);
                if (rootX != rootY) {
                    if (rank[rootX] > rank[rootY]) {
                        root[rootY] = rootX;
                    } else if (rank[rootX] < rank[rootY]) {
                        root[rootX] = rootY;
                    } else {
                        root[rootY] = rootX;
                        rank[rootX] += 1;
                    }
                    count--;
                }
            };
            int getCount() {
                return count;
            }
        private:
            int* root;
            int* rank;
            int count;
    };
};

DFS(deep first search)

BFS(breath first search)

应用:最短路径、遍历图

遍历

用队列(queue),不做概述

最短路径

找出两点间的最短路径的前提条件:

  • 权重相等且为正数
  • 最短路径(第一次取到的目的数肯定是最短路径)

DFS也可以找出最短路径,不过其必须递归完所有的路径后才能找出最短路径,相比之倒BFS倒显得上乘了

来看一个BFS求所有路径的题目leetcode797

class Solution
{
public:
    vector<vector<int>> allPathsSourceTarget(vector<vector<int>>& graph)
    {
        vector<vector<int>> res;
        queue<vector<int>> q;
        q.push({ 0 });
        while (!q.empty()) {
            vector<int> con = q.front();
            q.pop();
            for (int i = 0; i < graph[con[con.size() - 1]].size(); i++) {
                con.push_back(graph[con[con.size() - 1]][i]);
                if (con[con.size() - 1] == graph.size() - 1) {
                    res.push_back(con);
                }
                q.push(con);
                con.pop_back();
            }
        }
        return res;
    }
};

最小生成树

Kruskal

适用场景:加权无向图,求最少权重的全部顶点问题

算法思想:

  • 将所有边一次排序
  • 依次加入最小生成树,有环则跳过
  • 查找选择n-1条边为止

leetcode1584

class Solution {
public:
    static bool cmp(const vector<int>& m, const vector<int>& n) {
        return m[2] < n[2];
    }
    int minCostConnectPoints(vector<vector<int>>& points) {
        if (points.size() == 0) {
            return 0;
        }
        vector<vector<int>> con;
        for (int i = 0; i < points.size(); i++) {
            for (int j = i + 1; j < points.size(); j++) {
                vector<int> v = {
                    i, j, (int)fabs(points[i][0] - points[j][0]) + (int)fabs(points[i][1] - points[j][1])
                };
                con.push_back(v);
            }
        }
        sort(con.begin(), con.end(), cmp);
        int ans = 0;
        int count = points.size() - 1;
        UnionFind* uf = new UnionFind(count + 1);
        int i = 0;
        while (count > 0 && con.size() > 0) {
            if (!uf->isConnected(con[i][0], con[i][1])) {
                uf->unionFunc(con[i][0], con[i][1]);
                ans += con[i][2];
                count--;
            }
            i++;
        }
        return ans;
    }
    class UnionFind {
        public:
            UnionFind(int n) {
                root = new int[n];
                rank = new int[n];
                for (int i = 0; i < n; i++) {
                    root[i] = i;
                    rank[i] = 1;
                }
            }
            int find(int x) {
                if (root[x] == x) {
                    return x;
                }
                return root[x] = find(root[x]);
            }
            void unionFunc(int x, int y) {
                int rootX = find(x);
                int rootY = find(y);
                if (rootX != rootY) {
                    if (root[rootX] > root[rootY]) {
                        root[rootY] = rootX;
                    }
                    else if (root[rootY] > root[rootX]) {
                        root[rootX] = rootY;
                    }
                    else {
                        root[rootY] = rootX;
                        rank[rootX] += 1;
                    }
                }
            }
            bool isConnected(int x, int y) {
                return find(x) == find(y);
            }
        private:
            int* root;
            int* rank;
    };
};

Prim

适用场景:加权无向图,以点为中心展开选择最小边

单源最短路径

Dijkstra

针对题型:加权有向图,权值为正

算法思想:以起点为中心,逐步向外扩展并更新其他路径的最短路径,运用贪心思想,运行的每一步都是选择当前已知顶点的最小权重去寻找其他顶点

Bellman-Ford

针对题型:加权有向图,权值任意

算法思想:易知当图中有多条边时,要求两点之间最短路径的最多经过n-1条边,动态规划的思想,当前边n的最短路径由n-1条边的最短路径求得,依次遍历0~n-1,即可求大最短路径

leetcode787

class Solution {
public:
    int findCheapestPrice(int n, vector<vector<int>>& flights, int src, int dst, int k) {
        if (src == dst) {
            return 0;
        }
        int* previous = new int[n];
        int* current = new int[n];
        for (int i = 0; i < n; i++) {
            previous[i] = INT_MAX;
            current[i] = INT_MAX;
        }
        previous[src] = 0;
        for (int i = 1; i < k + 2; i++) {
            current[src] = 0;
            for (vector<int> con: flights) {
                int previous_flight = con[0];
                int current_flight = con[1];
                int cost = con[2];
                if (previous[previous_flight] < INT_MAX) {
                    current[current_flight] = min(current[current_flight], previous[previous_flight] + cost);
                }
            }
            for (int i = 0; i < n; i++) {
                previous[i] = current[i];
            }
        }
        return current[dst] == INT_MAX ? -1 : current[dst];
    }
};

拓扑排序

前提条件:有向无环图

Kahn算法

算法思想:让度为0的结点入队,出队后让其指向的结点度减1,如果其度为0入队,直到队列为空

入度:有多少个箭头指向他

leetcode210

class Solution {
public:
    vector<int> findOrder(int n, vector<vector<int>>& pre) {
        // 邻接表
        vector<vector<int>> graph(n, vector<int>());
        vector<int> ans, ind(n, 0);
        for (auto& i : pre) {
            graph[i[1]].push_back(i[0]);
            ++ind[i[0]];
        }
        queue<int> st;
        for (int i = 0; i < n; ++i) {
            if (!ind[i]) st.push(i);
        }
        // 判断是否有环
        int cnt = 0;
        while (!st.empty()) {
            int k = st.front(); st.pop();
            ans.push_back(k);
            ++cnt;
            for (auto i : graph[k]) {
                --ind[i];
                if (!ind[i]) {
                    st.push(i);
                }
            }
        }
        if (cnt != n) return {};
        return ans;
    }
};

Summary:

  • 并查级:求结点是否连通性
  • 最小生成树:所有结点重组成新树,要求边最小
  • 单源最短路径:解决有权图的最短路径问题
  • 拓扑排序:结点的先后次序

刷题之路艰难险阻,各位珍重。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值