区别:
一。几种算法
普通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;
}
}