自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

圖算法系列之計(jì)算圖中最短路徑

網(wǎng)絡(luò) 通信技術(shù) 算法
在前面兩篇中我們通過深度優(yōu)先搜索可以從圖中找出一條通過頂點(diǎn)v到頂點(diǎn)w的路徑,但是深度優(yōu)先搜索與頂點(diǎn)的輸入有很大的關(guān)系,找出來的路徑也不一定是最短的,通常情況下我們很多時(shí)候需要找出圖中的最短路徑,比如:地圖功能。

[[398324]]

本文轉(zhuǎn)載自微信公眾號(hào)「貝塔學(xué)JAVA」,作者Silently9527。轉(zhuǎn)載本文請(qǐng)聯(lián)系貝塔學(xué)JAVA公眾號(hào)。

前言

在前面兩篇中我們通過深度優(yōu)先搜索可以從圖中找出一條通過頂點(diǎn)v到頂點(diǎn)w的路徑,但是深度優(yōu)先搜索與頂點(diǎn)的輸入有很大的關(guān)系,找出來的路徑也不一定是最短的,通常情況下我們很多時(shí)候需要找出圖中的最短路徑,比如:地圖功能。這里我們就需要使用到廣度優(yōu)先搜索算法

廣度優(yōu)先搜索

依然使用之前定義的尋找路徑的API

  1. public class Paths { 
  2.     Paths(Graph graph, int s); 
  3.      
  4.     boolean hasPathTo(int v); //判斷出從s->v是否存在路徑 
  5.      
  6.     Iterable<Integer> pathTo(int v); //如果存在路徑,返回路徑 

在廣度優(yōu)先搜索中,為了找出最短路徑,我們需要按照起點(diǎn)的順序來遍歷所有的頂點(diǎn),而不在是使用遞歸來實(shí)現(xiàn);算法的思路:

  • 使用隊(duì)列來保存已經(jīng)被標(biāo)記過但是鄰接表還未被遍歷過的頂點(diǎn)
  • 取出隊(duì)列中的下一個(gè)頂點(diǎn)v并標(biāo)記它
  • 將v相鄰的所有未被標(biāo)記的頂點(diǎn)加入到隊(duì)列

在該算法中,為了保存路徑,我們依然需要使用一個(gè)邊的數(shù)組edgeTo[],用一顆父鏈樹來表示根節(jié)點(diǎn)到所有連通頂點(diǎn)的最短路徑。

  1. public class BreadthFirstPaths { 
  2.     private boolean marked[]; 
  3.     private int[] edgeTo; 
  4.     private int s; 
  5.     private Queue<Integer> queue = new LinkedListQueue<>(); 
  6.  
  7.     public BreadthFirstPaths(Graph graph, int s) { 
  8.         this.s = s; 
  9.         this.marked = new boolean[graph.V()]; 
  10.         this.edgeTo = new int[graph.V()]; 
  11.  
  12.         bfs(graph, s); 
  13.     } 
  14.  
  15.     private void bfs(Graph graph, int s) { 
  16.         this.marked[s] = true
  17.         this.queue.enqueue(s); 
  18.         while (!this.queue.isEmpty()) { 
  19.             Integer v = this.queue.dequeue(); 
  20.             for (int w : graph.adj(v)) { 
  21.                 if (!this.marked[w]) { 
  22.                     this.marked[w] = true
  23.                     this.edgeTo[w] = v; 
  24.                     this.queue.enqueue(w); 
  25.                 } 
  26.             } 
  27.         } 
  28.  
  29.  
  30.     } 
  31.  
  32.     public boolean hasPathTo(int v) { 
  33.         return this.marked[v]; 
  34.     } 
  35.  
  36.     public Iterable<Integer> pathTo(int v) { 
  37.         if (!hasPathTo(v)) { 
  38.             throw new IllegalStateException("s不能到達(dá)v"); 
  39.         } 
  40.         Stack<Integer> stack = new LinkedListStack<>(); 
  41.         stack.push(v); 
  42.         while (edgeTo[v] != s) { 
  43.             stack.push(edgeTo[v]); 
  44.             v = edgeTo[v]; 
  45.         } 
  46.         stack.push(s); 
  47.         return stack; 
  48.     } 

以下圖為列,來看看廣度優(yōu)先搜索的運(yùn)行軌跡

 

單元測試的代碼:

  1. @Test 
  2. public void test() { 
  3.     Graph graph = new Graph(8); 
  4.     graph.addEdge(0, 1); 
  5.     graph.addEdge(0, 2); 
  6.     graph.addEdge(0, 5); 
  7.     graph.addEdge(1, 3); 
  8.     graph.addEdge(2, 4); 
  9.     graph.addEdge(4, 3); 
  10.     graph.addEdge(5, 3); 
  11.     graph.addEdge(6, 7); 
  12.  
  13.     BreadthFirstPaths paths = new BreadthFirstPaths(graph, 0); 
  14.     System.out.println(paths.hasPathTo(5)); 
  15.     System.out.println(paths.hasPathTo(2)); 
  16.     System.out.println(paths.hasPathTo(6)); 
  17.  
  18.     paths.pathTo(5).forEach(System.out::print); 
  19.     System.out.println(); 
  20.     paths.pathTo(4).forEach(System.out::print); 
  21.     System.out.println(); 
  22.     paths.pathTo(2).forEach(System.out::print); 
  23.     System.out.println(); 
  24.     paths.pathTo(3).forEach(System.out::print); 
  25.  

執(zhí)行的結(jié)果與我們分析的運(yùn)行軌跡一致

符號(hào)圖

最近幾篇文章一起學(xué)習(xí)到的圖算法都是以數(shù)字作為了頂點(diǎn),是因?yàn)閿?shù)字來實(shí)現(xiàn)這些算法會(huì)非常的簡潔方便,但是在實(shí)際的場景中,通常都是使用的字符作為頂點(diǎn)而非數(shù)字,比如:地圖上的位置、電影與演員的關(guān)系。

為了滿足實(shí)際的場景,我們只需要在數(shù)字與字符串的關(guān)系做一個(gè)映射,此時(shí)我們會(huì)想到之前文章實(shí)現(xiàn)的map(通過二叉樹實(shí)現(xiàn)map、紅黑樹實(shí)現(xiàn)map、哈希表實(shí)現(xiàn)map等等,有興趣的同學(xué)可以去翻翻),使用Map來維護(hù)字符串和數(shù)字的映射關(guān)系。

  1. public interface SymbolGraph { 
  2.     boolean contains(String key); //判斷是否存在頂點(diǎn) 
  3.  
  4.     int index(String key); //通過名稱返回對(duì)應(yīng)的數(shù)字頂點(diǎn) 
  5.  
  6.     String name(int v); //通過數(shù)字頂點(diǎn)返回對(duì)應(yīng)的字符名稱 
  7.  
  8.     Graph graph(); 

實(shí)現(xiàn)的思路:

  • 使用Map來保存字符串-數(shù)字的映射,key為字符串,value為數(shù)字
  • 使用一個(gè)數(shù)組來反向映射數(shù)字-字符串,數(shù)組的下標(biāo)對(duì)應(yīng)數(shù)字頂點(diǎn),值對(duì)應(yīng)字符串名稱
  • 假設(shè)構(gòu)造圖的頂點(diǎn)與邊是通過字符串來表示的,如:a,b,c,d\nb,a,h,l,p\ng,s,z 使用\n分隔的每段第一個(gè)字符串表示頂點(diǎn)v,后面的表示與頂點(diǎn)v相連的相鄰頂點(diǎn);

實(shí)際的過程可以根據(jù)具體情況來確定,不一定非得這種字符串,可以來源于數(shù)據(jù)庫,也可以來源于網(wǎng)路的請(qǐng)求。

代碼實(shí)現(xiàn)如下:

  1. public class SymbolGraph { 
  2.     private Map<String, Integer> map = new RedBlack23RedBlackTreeMap<>(); 
  3.     private String[] keys; 
  4.     private Graph graph; 
  5.  
  6.     public SymbolGraph(String text) { 
  7.         Arrays.stream(text.split("\n")).forEach(line -> { 
  8.             String[] split = line.split(","); 
  9.             for (int i = 0; i < split.length; i++) { 
  10.                 map.put(split[i], i); 
  11.             } 
  12.         }); 
  13.  
  14.         this.keys = new String[map.size()]; 
  15.         map.keys().forEach(key -> { 
  16.             this.keys[this.map.get(key)] = key
  17.         }); 
  18.  
  19.         this.graph = new Graph(this.keys.length); 
  20.         Arrays.stream(text.split("\n")).forEach(line -> { 
  21.             String[] split = line.split(","); 
  22.             int v = this.map.get(split[0]); 
  23.             for (int i = 1; i < split.length; i++) { 
  24.                 this.graph.addEdge(v, this.map.get(split[i])); 
  25.             } 
  26.         }); 
  27.          
  28.     } 
  29.  
  30.     public boolean contains(String key) { 
  31.         return map.contains(key); 
  32.     } 
  33.  
  34.     public int index(String key) { 
  35.         return map.get(key); 
  36.     } 
  37.  
  38.     public String name(int index) { 
  39.         return this.keys[index]; 
  40.     } 
  41.  
  42.     public Graph graph() { 
  43.         return this.graph; 
  44.     } 
  45.  
  46.     public static void main(String[] args) { 
  47.         System.out.println(Arrays.toString("323\n2323".split("\n"))); 
  48.     } 

 

文中所有源碼已放入到了github倉庫:https://github.com/silently9527/JavaCore

 

責(zé)任編輯:武曉燕 來源: 貝塔學(xué)JAVA
相關(guān)推薦

2011-05-17 13:58:37

最短路徑

2021-04-28 07:59:21

深度優(yōu)先搜索

2021-08-26 17:36:42

Floyd算法數(shù)據(jù)結(jié)構(gòu)

2013-04-23 09:31:52

SQL Server

2021-04-19 09:08:19

無向圖數(shù)據(jù)結(jié)構(gòu)

2011-12-19 12:39:37

Java

2021-03-10 09:50:15

算法Dijkstra短路問題

2011-04-11 16:32:28

路徑C++

2024-05-24 08:00:00

2015-07-16 14:25:56

SDN網(wǎng)絡(luò)感知服務(wù)

2011-06-01 09:27:00

OSPF路由路由器

2011-05-17 14:29:29

Dijkstra

2011-05-17 14:11:06

Dijkstra

2025-02-26 05:00:00

DFS算法遞歸

2024-04-02 11:37:59

AGI網(wǎng)絡(luò)模型GAN

2015-12-07 17:07:36

SDN網(wǎng)絡(luò)流量

2021-09-08 10:32:29

微服務(wù)容器化Serverless

2020-09-16 12:23:37

TypeScript

2019-04-01 06:54:10

2014-03-26 09:04:42

算法Floyd最短算法
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)