最短路徑
是很經典的一個問題,最初看到該類問題時毫無思路,而一旦抓到解題思路的主脈絡後,則會驚歎於組織結構化數據的精巧!java
a、b、c、d、e、f、g是七個城鎮,它們之間的連線表示汽車行駛路線,而連線上的箭頭表示道路容許方向。(好比,a和c之間,箭頭由a指向c,表示能夠開車從a行駛到c;反之,從c直接行駛到a是不行的)問題,找出一條從A鎮到G鎮途徑鎮子最少的路線。node
解決問題的思路是這樣的:算法
從節點a開始,查找可直接到達的節點,也就是節點e和c,姑且把它們稱之爲一級節點。緩存
一級節點(e、c)中是否有終點g?很顯然沒有,那麼從e、c開始,繼續查找。數據結構
接下來怎麼作?猜到了吧——查看三級節點!等等,我聞到了遞歸的味道。工具
避免忘記,我將步驟2時代的二級節點d、a、f標註顏色了。
從節點d開始,可直達節點g……hold on!其它的不用管了,咱們已經找到了終點g,路徑爲a->e->d->g
。this
思路不難理解,接下來的工做就是將上述思路翻譯成代碼思惟,關鍵點有三。google
回顧下整個查找過程,從起點a的直達節點一級節點,到一級節點下的二級節點,再到二級節點下的三級節點……每步驟下來,有明確的前後次序。那什麼數據結構用來處理這種次序呢?FIFO(First In First Out)隊列!spa
從起點a開始,遍歷查找它的一級節(e、c)點中是否有終點g,有就開香檳慶祝,沒有則將這些節點放入到隊列中;而後從隊列中獲取這些一級節點(e、c),遍歷查找它們的可直達節點(二級節點),依然是「有就開香檳慶祝,沒有則將這些節點放入到隊列中」……翻譯
這個很少說了,前面的 步驟 2 和上面的 point 1 都流露出遞歸的痕跡,細細體會一下。
前面的分析中,有一個問題被輕易的略過了。
起點a的直達節點中有節點e,節點e的直達節點中也有節點a,它倆你中有我我中有你當然親密無間,但聽任無論的話就陷入死循環了。
因此還要有一個去重機制,這裏我選擇了用一個Set集合記錄放入隊列節點的方式進行去重。
ok,最後將前面的各類分析造成代碼。
其實就是bean,對節點數據進行封裝。
用到了lombok
,挺好用的工具,感興趣的朋友自行研究下。
import lombok.Data; /** * @description: 封裝節點數據 * @author: liuzijian * @date: 2018-04-11 23:06 */ @Data public class Node { private String node; private String path; //保存路徑 public Node(String node) { this.node = node; path = node; } private final String nextSymbol = "->"; //間隔符 public String pathAppend(String lastPath){ return lastPath + nextSymbol + node; } }
用到了guava
中的ImmutableList
,也是極好用的東西,推薦!
import com.evolution.algorithm.bean.Node; import com.google.common.collect.ImmutableList; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * @description: 最短路徑問題 * @author: liuzijian * @date: 2018-04-11 23:04 */ public class ShortestPath { Map<String,List<Node>> pic = new HashMap<>(); //存儲圖 Set<String> existsNodes = new HashSet<>(); //節點去重 LinkedList<Node> pathList = new LinkedList<>(); //FIFO隊列 /** * 圖的初始化 */ private void initPic(){ pic.put("a",ImmutableList.of(new Node("c"),new Node("e"))); pic.put("b",ImmutableList.of(new Node("a"),new Node("g"))); pic.put("c",ImmutableList.of(new Node("f"))); pic.put("d",ImmutableList.of(new Node("a"),new Node("g"))); pic.put("e",ImmutableList.of(new Node("d"),new Node("a"),new Node("f"))); pic.put("f",ImmutableList.of(new Node("b"),new Node("d"))); pic.put("g",ImmutableList.<Node>of()); } /** * 構造塊中進行圖的初始化工做 */ public ShortestPath(){ initPic(); } /** * 路徑查找方法的重載 * 爲調用方便,將參數source由<code>Node</code>類型重載爲<code>String</code>類型 */ public void findPath(String source,final String target){ findPath(new Node(source),target); } /** * 核心方法,路徑查找 */ private void findPath(Node source,final String target){ List<Node> relations = pic.get(source.getNode()); for(Node node:relations){ if(node.getNode().equals(target)){ System.out.println("Get it!Path is '"+node.pathAppend(source.getPath())+"'"); return; }else if(existsNodes.contains(node.getNode())){ //do nothing }else{ existsNodes.add(node.getNode()); pathList.add(node); node.setPath(node.pathAppend(source.getPath())); } } if(pathList.isEmpty()){ System.out.println("Sorry!Can not reach!"); }else{ Node node = pathList.removeFirst(); findPath(node,target); } } public static void main(String[] args) { ShortestPath sp = new ShortestPath(); sp.findPath("d","f"); } }
近期比較癡迷Guava
,以前老是對它只知其一;不知其二的。最近兩天有幸拜讀公司大神同事,用Guava Cache
做本地緩存的代碼,敬畏不已!遂決定潛心研究之並寫文章記錄,敬請期待。