Java图论(更新中)


单源最短路:


所有边都是正权
1.朴素Dijkstra,使用邻接矩阵,适用稠密图
2.堆优化Dijkstra,使用邻接表,适用稀疏图
存在负权边
1.bellman-ford (解决带有边数限制的题目:例如求图中从起点到终点的最短路径,且经过的中转点最多为k个【即经过的边最多为k+1条】)
2.SPFA

1.邻接矩阵Dijkstra模板

class Solution {
    /*
        Dijkstra模板
    */
    //N为图中的节点数
    int[][] map = new int[N][N];//邻接矩阵
    int[] dis = new int[N];//源点到其他点的距离数组
    boolean[] vis = new boolean[N];//点访问标记数组
    int INF = 0x3f3f3f3f;
    public int networkDelayTime() {
    
        //初始化:
        //1.map置为INF/0
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                map[i][j] = (i == j) ? 0 : INF;
            }
        }
        
        //2.建图
        for(){
            map[u][v] =  weight;
        }
        
        //执行Dijkstra:
        Dijkstra(k,n);
        
        //TODO
        
    }
    public void Dijkstra(int k,int n){
    
        //初始化:
        Arrays.fill(vis,false);
        Arrays.fill(dis,INF);
        dis[k] = 0;
        
        for(int i = 1;i<=n;i++){
            int w = 0;//w为当前找到的最优节点
            for(int j = 1;j<=n;j++){
                if(!vis[j] && (w == 0 || dis[j] < dis[w])){
                    w = j;
                }
            }
            //标记访问
            vis[w] = true;
            //以w为中转节点接着寻找最短路
            for(int j = 1;j<=n;j++){
                dis[j] = Math.min(dis[j],dis[w]+map[w][j]);
            }
        }
    }
}

2.邻接表(链式前向星)Dijkstra模板

class Solution {
    /*
        堆优化Dijkstra
    */ 
    //N为图中的节点数、M为图中的边数
    int[] dis = new int[N];//源点到其他点的距离数组
    boolean[] vis = new boolean[N];//点访问标记数组
    int[] he = new int[N];//每个节点所对应的边的集合(链表)的头结点
    int[] e = new int[M];//每条边指向的节点
    int[] ne = new int[M];//用于找到下一条边
    int[] w = new int[M];//每条边的权重
    int idx;//每条边的编号
    int INF = 0x3f3f3f3f;
    
    //邻接表加边操作
    public void add(int a, int b, int c) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx;
        w[idx] = c;
        idx ++;
    }

    public int main() {
    
        //初始化:
        Arrays.fill(he, -1);//初始化链表头
        
        //建图:
        for(){
            add(u,v,w);
        }
        
        //执行Dijkstra:
        Dijkstra(k,n);
        
        //TODO
        
    }
    public void Dijkstra(int k,int n){
        //初始化
        Arrays.fill(vis,false);
        Arrays.fill(dis,INF);
        //优先队列{点编号、点到源点的距离};点到源点的距离从小到大排序
        PriorityQueue<int[]> pq = new PriorityQueue<>((a,b) -> a[1]-b[1]);
        pq.add(new int[]{k, 0});
        dis[k] = 0;
        
        while(!pq.isEmpty()){
        	//取出堆头的 点编号p、点到源点的距离dist
            int[] cur = pq.poll();
            int p = cur[0];
            int dist = cur[1];
           
            if(vis[p]) continue;
            vis[p] = true;

			//for:遍历所有由p点发出的边
            for(int i = he[p];i != -1;i = ne[i]){
                int j = e[i];
                if(dis[p] + w[i] < dis[j]){
                    dis[j] = dis[p] + w[i];
                    pq.add(new int[]{j,dis[j]});
                }
            }
        }
    }
}

3.SPFA模板

class Solution {
    /*
        SPFA
    */
    //N为图中的节点数、M为图中的边数
    int[] dis = new int[N];//源点到其他点的距离数组
    boolean[] vis = new boolean[N];//点访问标记数组
    int[] he = new int[N];//每个节点所对应的边的集合(链表)的头结点
    int[] e = new int[M];//每条边指向的节点
    int[] ne = new int[M];//用于找到下一条边
    int[] w = new int[M];//每条边的权重
    int idx;//每条边的编号
    int INF = 0x3f3f3f3f;
    
    //邻接表加边操作
    public void add(int a, int b, int c) {
        e[idx] = b;
        ne[idx] = he[a];
        he[a] = idx;
        w[idx] = c;
        idx ++;
    }
    
    public int main() {
    
        //初始化
        Arrays.fill(he, -1);//初始化链表头
        
        //建图
        for(){
            add(u,v,w);
        }
        
        //执行SPFA:
        SPFA(k,n);
        
        //TODO
        
    }
    public void SPFA(int k,int n){
        //初始化
        Arrays.fill(vis,false);
        Arrays.fill(dis,INF);
        //队列
        Queue<Integer> q = new LinkedList<>();
        q.add(k);
        dis[k] = 0;
        vis[k] = true;//源点标记以访问!
        
        while(!q.isEmpty()){
            int p = q.poll();
            vis[p] = false;//队头标记未访问!

            for(int i = he[p];i != -1;i = ne[i]){
                int j = e[i];
                if(dis[p]+w[i] < dis[j]){
                    dis[j] = dis[p]+w[i];
                    
                    //在这里与堆优化djikstra区别,增加了vis判断和标记
                    if(vis[j]) continue;
                    vis[j] = true;
                    
                    q.add(j);
                }
            }
        }
    }
}

4.bellman-ford模板 787. K 站中转内最便宜的航班

class Solution {
    int[][] map = new int[N][N];
    int[] dis = new int[N];
    int INF = 0x3f3f3f3f;
    public int main() {
    
    	//初始化
        for(int i = 0;i<n;i++){
            for(int j = 0;j<n;j++){
                map[i][j] = (i == j) ? 0 : INF;
            }
        }
        
        //建图
        for(){
            map[u][v] = w;
        }
        
		//bellman-ford   k:经过的中转点最多为k个
        bf(start,end,k,n);
    }
    public void bf(int start,int end,int k,int n){
    	//初始化
        Arrays.fill(dis,INF);
        dis[start] = 0;
        
        //三层for
        //最外层for为边数限制条件    经过的中转点最多为k个 转换为   经过的边数最多为k+1条
        for(int limit = 0;limit < k+1;limit++){
            int[] clone = dis.clone();//克隆一份!!!
            for(int i = 0;i<n;i++){
                for(int j = 0;j<n;j++){
                    dis[j] = Math.min(dis[j],clone[i]+map[i][j]);
                }
            }
        }
    }
}

多源最短路:

1.Folyd

Folyd模板 1334. 阈值距离内邻居最少的城市

class Solution {
    /*
        Floyd
    */
    //N为图中的节点数
    int[][] map = new int[N][N];
    int INF = 0x3f3f3f3f;
    public int main() {
    
        //初始化:
        for(int i = 1;i<=n;i++){
            for(int j = 1;j<=n;j++){
                map[i][j] = (i == j) ? 0 : INF;
            }
        }
        
        //建图:
        for(){
            map[u][v] = w
        }
        
		//执行Floyd:
        Floyd(n);
        
        //TODO:
        
    }
    public void Floyd(int n){
    	//枚举中转点-枚举起点-枚举终点
        for(int k = 1;k<=n;k++){
            for(int i = 1;i<=n;i++){
                for(int j = 1;j<=n;j++){
                    map[i][j] = Math.min(map[i][j],map[i][k]+map[k][j]);
                }
            }
        }
    }
}

拓扑排序

对一个有向无环图(DAG)进行拓扑排序,是将图中所有节点排成一个线性序列,使得图中任意一对顶点u和v,若边<u,v>∈E(G),则u在线性序列中出现在v之前。

算法步骤:

1.建图、记录节点的入度数
2.将入度为0的节点存入队列中
3.while循环直到队列为空,依次弹出入度为0的节点,加入答案中,并依次将入度为0节点的所有出边删除(即在a->b中,【删除->】等价于【将节点b的入度减1】),再寻找并入队入度为0的节点。

1.邻接矩阵拓扑排序模板
class Solution {
    int[][] map = new int[N][N];//邻接矩阵
    int[] in = new int[N];//记录节点入度
    public int[] main() {
    	//建图及记录节点的入度数
        for(int i = 0;i<n;i++){
            map[a][b] = 1;
            in[b]++;
        }
        
		//Deque
        Deque<Integer> q = new ArrayDeque<>();
        //答案数组
        int[] ans = new int[n];
        
        //存所有入度为0的节点
        for(int i = 0;i<n;i++){
            if(in[i] == 0) q.addLast(i);
        }
        
        int c = 0;
        while(!q.isEmpty()){
            int u = q.pollFirst();//取出节点
            ans[c] = u;//记录答案序列
            c++; 
            for(int i = 0;i<n;i++){
                if(map[u][i] == 1){//找出u的所有出边
                    in[i]--;//删除出边
                    if(in[i] == 0) q.addLast(i);//若删除后出边指向的节点的入度为0则加入队列                    
                }
            }
        }
        return ans;
    }
}
2.邻接表拓扑排序模板(推荐) 207. 课程表 210. 课程表 II 851. 喧闹和富有
class Solution {
    public int[] main() {
        int[] in = new int[N];//记录节点入度
        List<List<Integer>> map = new ArrayList<>();//链表存图
        
        //初始化
        for(int i = 0; i < n; i++)
            map.add(new ArrayList<>());
         
        //存图和存节点的入度
        for() {
            map.get(u).add(v);
            in[v]++;
        }
        
       	//将入度为0的节点放入队列中
		Deque<Integer> q = new ArrayDeque<>();
        for(int i = 0; i < n; i++)
            if(in[i] == 0) q.addLast(i);
        
        //答案数组
        int[] ans = new int[n];
        int c = 0;
        while(!q.isEmpty()) {
            int v = q.pollFirst();
            ans[c] = v;//记录答案序列
            c++; 
            for(int u : map.get(v))//get出来的就是v直接连接的节点
                if(--in[u] == 0) q.add(u);
        }
    }
}

未完待续…………

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Uranus^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值