算法总结六:图论中五花八门的算法

区别:
一。几种算法
普通BFS
Dijkstra
Bellman-Ford
Floyd-Warshall

二。配套:如何构建一个图
adjacency list节省空间,搜索时间慢
map占用空间,搜索时间快
adjacency matrix占用空间,搜索时间快

=================
1.BFS主要适用于无权重向图中搜索出步骤最少的路径,当方向图存在权重时,不再适用;
2.Dijkstra主要用于有权重的方向图中搜索出最短、cost最小的路径,但不适合于有负权重的情况。
视频讲解
对于有环图,和BFS一样,标志好已处理的节点避免进入死循环,可以支持;
3.Bellman-Ford:负权重
4.Floyd-Warshall: 本质是动态规划,解决任意两点间的最短路径。
Floyd能不能处理负权边??

=================
一。Dijkstra
1.1 定义

最短距离:
在这里插入图片描述

public class Dijkstra {
    /*
     * 参数adjMatrix:为图的权重矩阵,权值为-1的两个顶点表示不能直接相连
     * 函数功能:返回顶点0到其它所有顶点的最短距离,其中顶点0到顶点0的最短距离为0
     */
    public int[] getShortestPaths(int[][] adjMatrix) {
        int[] result = new int[adjMatrix.length];   //用于存放顶点0到其它顶点的最短距离
        boolean[] used = new boolean[adjMatrix.length];  //用于判断顶点是否被遍历
        used[0] = true;  //表示顶点0已被遍历
        for(int i = 1;i < adjMatrix.length;i++) {
            result[i] = adjMatrix[0][i];
            used[i] = false;
        }
    
        for(int i = 1;i < adjMatrix.length;i++) {
            int min = Integer.MAX_VALUE;    //用于暂时存放顶点0到i的最短距离,初始化为Integer型最大值
            int k = 0;
            for(int j = 1;j < adjMatrix.length;j++) {  //找到顶点0到其它顶点中距离最小的一个顶点
                if(!used[j] && result[j] != -1 && min > result[j]) {
                    min = result[j];
                    k = j;
                }
            }
            used[k] = true;    //将距离最小的顶点,记为已遍历
            for(int j = 1;j < adjMatrix.length;j++) {  //然后,将顶点0到其它顶点的距离与加入中间顶点k之后的距离进行比较,更新最短距离
                if(!used[j]) {  //当顶点j未被遍历时
                    //首先,顶点k到顶点j要能通行;这时,当顶点0到顶点j的距离大于顶点0到k再到j的距离或者顶点0无法直接到达顶点j时,更新顶点0到顶点j的最短距离
                    if(adjMatrix[k][j] != -1 && (result[j] > min + adjMatrix[k][j] || result[j] == -1))
                        result[j] = min + adjMatrix[k][j];
                }
            }
        }
        return result;
    }
    
    public static void main(String[] args) {
        Dijkstra test = new Dijkstra();
        int[][] adjMatrix = {{0,6,3,-1,-1,-1},
                {6,0,2,5,-1,-1},
                {3,2,0,3,4,-1},
                {-1,5,3,0,2,3},
                {-1,-1,4,2,0,5},
                {-1,-1,-1,3,5,0}};
        int[] result = test.getShortestPaths(adjMatrix);
        System.out.println("顶点0到图中所有顶点之间的最短距离为:");
        for(int i = 0;i < result.length;i++) 
            System.out.print(result[i]+" ");
    }
}
 

from:https://blog.csdn.net/a1439775520/article/details/96903328

1.2 讲解
掘金解答

1.3 leetcode练习
378. Kth Smallest Element in a Sorted Matrix

class Solution {
    public int kthSmallest(int[][] matrix, int k) {
        if(matrix==null || matrix.length==0){
            return -1;
        }
        int n=matrix.length;
        
        Set<List<Integer>> visited=new HashSet<>();
        int[][] dirs={{1,0},{-1,0},{0,1},{0,-1}};
        PriorityQueue<List<Integer>> minHeap=new PriorityQueue<List<Integer>>((e1,e2)-> e1.get(2)-e2.get(2));
             
        minHeap.offer(Arrays.asList(0,0,matrix[0][0]));   
        visited.add(Arrays.asList(0,0,matrix[0][0]));
    
        while(k>1){
            List<Integer> cur=minHeap.poll();  //exp
            for(int[] dir:dirs){
                int nextX=cur.get(0)+dir[0];
                int nextY=cur.get(1)+dir[1];
          
                if(nextX>=0 && nextX<n && nextY>=0 && nextY<n && visited.add(Arrays.asList(nextX,nextY,matrix[nextX][nextY]))){
                    minHeap.offer(Arrays.asList(nextX,nextY,matrix[nextX][nextY])); //gen                         
                }
            }
            k--;
        }
        return minHeap.poll().get(2);
    }
}

787. Cheapest Flights Within K Stops

class Pair {
	int city, cost;

	Pair(int city, int cost) {
		this.city = city;
		this.cost = cost;
	}
}

class City {
	int city, distFromSrc, costFromSrc;

	City(int city, int distFromSrc, int cost) {
		this.city = city;
		this.distFromSrc = distFromSrc;
		this.costFromSrc = cost;
	}
}

class Solution {

    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
    	// DFS
        if(n <= 0 || flights == null || flights.length == 0 || K < 0)
        	return -1;

        List<List<Pair>> graph = new ArrayList<>();
        this.buildGraph(graph, n, flights);

        Queue<City> pQueue = new PriorityQueue<>((City c1, City c2) -> c1.costFromSrc - c2.costFromSrc);
        pQueue.offer(new City(src, 0, 0));

        int totalCost = 0;

        while (!pQueue.isEmpty()) {
        	City top = pQueue.poll();

        	if (top.city == dst) {
        		return top.costFromSrc;
        	}

        	if (top.distFromSrc > K) {
        		continue;
        	}

        	List<Pair> neighbors = graph.get(top.city);
        	for (Pair neighbor: neighbors) {
        		pQueue.offer(new City(neighbor.city, top.distFromSrc + 1, top.costFromSrc + neighbor.cost));
        	}
        }

        return -1;
    }

    private void buildGraph(List<List<Pair>> graph, int n, int[][] flights) {
    	for (int i = 0; i < n; i++) {
    		graph.add(new ArrayList<>());
    	}

    	for (int[] flight: flights) {
    		graph.get(flight[0]).add(new Pair(flight[1], flight[2]));
    	}
    }
}

//不是走recursion
//是用recursion的思路,变换City!!!!!!

for (Pair neighbor: neighbors) {
	pQueue.offer(new City(neighbor.city, top.distFromSrc + 1, top.costFromSrc + neighbor.cost));
}

二。Bellman-Ford

2.1定义:
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径的算法。
V代表顶点数,E代表边数。
对图进行最多V-1次松弛操作,得到所有可能的最短路径。其优于迪科斯彻算法的方面是边的权值可以为负数、实现简单,缺点是时间复杂度过高,高达O(VE)。
掘金解答

思路上与Dijkstra algorithm最大的不同是Bellman-Ford每次都是从源点s重新出发进行"松弛"更新操作,
而Dijkstra则是从源点出发向外扩逐个处理相邻的节点,不会去重复处理节点,这边也可以看出Dijkstra效率相对更高点。

2.2 视频讲解https://www.bilibili.com/video/BV1gb41137u4
2.3 leetcode练习:
787. Cheapest Flights Within K Stops
//v-1轮,即中转v-1次,这里是中转K次
//use tmpDist array to store each iteration round result
//each time, only iterate one city… 0-1 0-2 skip 1-2.
//otherwise: without tempdist, will iterate 0-1,1-2, and 0-2
//when stop K=0, in one iteration, we still get 200 as result, which is wrong.
//it should be 500

class Solution {
    public int findCheapestPrice(int n, int[][] flights, int src, int dst, int K) {
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE);
        dist[src] = 0;
        for (int i = 0; i <= K; i++) {
            int[] tmpDist = Arrays.copyOf(dist, dist.length);
            for (int[] edge : flights) {
                int u = edge[0];
                if (dist[u] == Integer.MAX_VALUE) continue;
                int v = edge[1];
                int w = edge[2];
                tmpDist[v] = Math.min(tmpDist[v], dist[u] + w);
            }
            dist = tmpDist;
        }
        return dist[dst] == Integer.MAX_VALUE ? -1 : dist[dst];
    }
}

三。Floyd-Warshall

3.1 Floyd-Warshall是解决任意两点间的最短路径的一种算法,

3.2 视频讲解:https://www.bilibili.com/video/BV1LE411R7CS/

a)如图:存在【0,1,2,3】 4个点,两点之间的距离就是边上的数字,如果两点之间,没有边相连,则无法到达,为无穷大。
b)要让任意两点(例如从顶点a点到顶点b)之间的路程变短,只能引入第三个点(顶点k),并通过这个顶点k中转即a->k->b,才可能缩短原来从顶点a点到顶点b的路程。那么这个中转的顶点k是0~n中的哪个点呢?

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

作者:Halburt 链接:https://juejin.cn/post/6844903833382944781

Public class Solution{
public static int MAX = Integer.MAX_VALUE;
    //    距离矩阵
    public int[][] dist;
    //    路径Path矩阵
    public int[][] path;

    public FloydDemo(int size) {
        this.path = new int[size][size];
        this.dist = new int[size][size];
    }

    public static void print(int[][] arrs) {
        System.out.println("------------------------");
        for (int[] arr : arrs) {
            for (int a : arr) {
                if (a == FloydDemo.MAX) {
                    System.out.print("∞ ");
                } else {
                    System.out.print(a + " ");
                }
            }
            System.out.println();
        }
        System.out.println("------------------------");
    }

    public void floyd(int[][] matrix) {
//        matrix和path length不一致可处理异常
        int size = matrix.length;
        //初始化 dist 和 path
        for(int i = 0;i< size;i++){
            for(int j = 0;j < size; j++){
                path[i][j]=-1;
                dist[i][j]=matrix[i][j];
            }
        }
//        核心算法
        for(int k = 0 ; k < size ; k++){
            for(int i = 0;i < size;i++){
                for(int j = 0 ;j < size;j++){
                    if(i == j){
                        continue;
                    }
//                判断如果 ik距离可达且 kj距离可达 且 i和j的距离是否大于 i-> k 与 k->j的距离和
                    if( dist[i][k] != MAX &&  dist[k][j] != MAX  &&  dist[i][j] > (dist[i][k] + dist[k][j]) ){
                        //System.out.println("路径{" + i +"->" + j +"}在点"+k +"处可中转(原先中转点为"+path[i][j]+"),距离由"+ dist[i][j]+"缩短为"+(dist[i][k] + dist[k][j]));
                        path[i][j]= k;
                        dist[i][j]= dist[i][k] + dist[k][j];
                    }else{
                        //System.out.println("路径{" + i +"->" +j +"}在点"+k +"中转不合适");
                    }
                }
            }
        }
    }
}

743. Network Delay Time
huahua

public class Solution {
    public int networkDelayTime(int[][] times, int N, int K) {
        int INF = 0x3f3f3f3f;
        int[][] g = new int[N + 1][N + 1];
        // 初始化图,注意,一开始距离是初始化为INF的,而不是像 spfa初始化成-1
        // spfa初始化成-1只是为了判断是否为邻居,这里初始化为INF是因为要取min的
        for (int i = 1; i <= N; i++) {
            for (int j = 1; j <= N; j++) {
                g[i][j] = i == j ? 0 : INF;
            }
        }
        for (int[] t : times) {
            g[t[0]][t[1]] = t[2];
        }
        // 使用K节点来松弛i->j的最短路径(大白话就是利用k作为中间节点)
        for (int k = 1; k <= N; k++) {
            for (int i = 1; i <= N; i++) {
                for (int j = 1; j <= N; j++) {
                    // 判断语句可以不用写,具体得看题目
                    // if (k != i && k != j && g[i][k] != INF && g[k][j] != INF) {
                        g[i][j] = Math.min(g[i][j], g[i][k] + g[k][j]);
                    // }
                }
            }
        }
        // g[a][b]表示a到b的最短距离
        // 拿结果
        int res = 0;
        for (int distance : g[K]) {
            res = Math.max(res, distance);
        }
        return res == INF ? -1 : res;
    }
}

3.3 leetcode练习:
1334. Find the City With the Smallest Number of Neighbors at a Threshold Distance

class Solution {
    public int findTheCity(int n, int[][] edges, int distanceThreshold) {
        int[][] distance = new int[n][n];
        for(int[] ele: distance){
            Arrays.fill(ele, Integer.MAX_VALUE);
        }
        
        for(int i=0;i<n;i++){
            distance[i][i] = 0;
        }
        
        for(int[] ele: edges){
            distance[ele[0]][ele[1]] = ele[2];
            distance[ele[1]][ele[0]] = ele[2];
        }
        
        for(int k=0;k<n;k++){
            for(int i=0;i<n;i++){
                for(int j=0;j<n;j++){
                    if(distance[i][k]!=Integer.MAX_VALUE && distance[j][k]!=Integer.MAX_VALUE){
                          if(distance[i][j] > distance[i][k] + distance[j][k]){
                              distance[i][j] = distance[i][k] + distance[j][k];
                              
                          }
                    }
                }
            }
        }

        
        
        int min = Integer.MAX_VALUE;
        int res = -1;
        for(int i=0;i<n;i++){
            int count = 0;
            for(int j=0;j<n;j++){
                if(distance[i][j] <= distanceThreshold){
                    count++;
                }
            }
            
            if(count <= min){
                res = i;
                min = count;
            }
        }
        
        return res;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值