数据结构(十):图
一、 圖概述
日常生活中使用的地圖導(dǎo)航,每個(gè)城市看做一個(gè)頂點(diǎn),城市與城市間連通的線路看做聯(lián)通的邊,就組成了圖。除了導(dǎo)航,迷宮,電路板等等也是圖,需要用圖這種數(shù)據(jù)結(jié)構(gòu)去解決很多連通問(wèn)題。
二、 圖的特性
圖的定義:圖是有一組頂點(diǎn)和一組能將頂點(diǎn)相連的邊組成的數(shù)據(jù)結(jié)構(gòu)
特殊的圖:
平行邊:連接同一對(duì)頂點(diǎn)的多條邊
自環(huán):一條出口和入口都來(lái)自同一個(gè)頂點(diǎn)的邊
圖的分類:
無(wú)向圖:連接頂點(diǎn)的邊是無(wú)方向無(wú)意義的圖
有向圖:連接頂點(diǎn)的邊具有方向的圖
相鄰頂點(diǎn):兩個(gè)頂點(diǎn)通過(guò)一條邊相連時(shí)為相鄰頂點(diǎn)
度:頂點(diǎn)邊的數(shù)量
子圖:圖所有邊和相連頂點(diǎn)的子集
路徑:A頂點(diǎn)到B頂點(diǎn)若干順序連接的邊
環(huán):起點(diǎn)和終點(diǎn)相同,并且至少含有一條邊的路徑
連通圖:圖中每一個(gè)頂點(diǎn)都存在一條邊到達(dá)另外一個(gè)頂點(diǎn),稱為連通圖
連通子圖:一幅非連通圖由若干連通部分組成,該連通部分稱為連通子圖
三、 圖的實(shí)現(xiàn)
3.1 鄰接矩陣
圖的數(shù)據(jù)結(jié)構(gòu)可以通過(guò)二維數(shù)組來(lái)表示,即鄰接矩陣,如下圖生成一個(gè)8*8的二維數(shù)組,通過(guò)graph[7][8]=1和graph[8][7]=1來(lái)表示頂點(diǎn)7和8的連通。
但該方式存在著內(nèi)存空間使用率低的問(wèn)題,N個(gè)頂點(diǎn)圖中,得初始化N^2規(guī)模的二維數(shù)組,空間復(fù)雜度是不可接受的。
3.2鄰接表
使用一個(gè)頂點(diǎn)數(shù)量相同大小的數(shù)組Queue[],索引作為圖的頂點(diǎn),索引對(duì)應(yīng)的隊(duì)列存放與頂點(diǎn)相連通的其他頂點(diǎn),用這種方式來(lái)表示圖稱為鄰接表。鄰接表相比鄰接矩陣可以大大的節(jié)省內(nèi)存空間。
圖的鄰接表實(shí)現(xiàn):
|
/** * 圖的實(shí)現(xiàn) * @author jiyukai */ public class Graph { //定點(diǎn)個(gè)數(shù) public int V; //邊的數(shù)量 public int E; //圖的鄰接表 public Queue<Integer>[] qTable; public Graph(int v) { this.V = v; this.E = 0; //初始化鄰接表,數(shù)組中的索引為頂點(diǎn),值為已隊(duì)列,存放相鄰的頂點(diǎn) qTable = new Queue[v]; for(int i=0;i<v;i++) { qTable[i] = new Queue<Integer>(); } } /** * 向圖中添加一條邊 * @param v * @param w */ public void addEdge(int v,int w) { //頂點(diǎn)v添加w的指向 qTable[v].enqueue(w); //頂點(diǎn)w添加v的指向 qTable[w].enqueue(v); //邊加1 E++; } /** * 返回當(dāng)前頂點(diǎn)的數(shù)量 * @return */ public int V() { return V; } /** * 返回當(dāng)前邊的數(shù)量 * @return */ public int E() { return E; } /** * 獲取與頂點(diǎn)V相鄰的頂點(diǎn) * @param V * @return */ public Queue adjoin(int V) { return qTable[V]; } } |
四、 深度優(yōu)先搜索
圖的搜索算法中,深度優(yōu)先搜索指的是搜索到的頂點(diǎn)既有子結(jié)點(diǎn)又有兄弟結(jié)點(diǎn),優(yōu)先搜索子結(jié)點(diǎn)。如下演示為圖的深度優(yōu)先搜索順序
為了不對(duì)已搜索過(guò)的頂點(diǎn)重復(fù)搜索,我們需要有個(gè)布爾類型的數(shù)組來(lái)標(biāo)記頂點(diǎn)是否被搜索過(guò),搜索過(guò)的就標(biāo)記為true,不再進(jìn)行深度搜索,提高效率的同時(shí),該數(shù)組也能判斷頂點(diǎn)A到B是否相通
因?yàn)橄嗤ǖ拇硭阉鬟^(guò),會(huì)被標(biāo)記為true。
|
/** * 深度優(yōu)先搜索 * @author jiyukai */ public class DepthSearch { //標(biāo)記頂點(diǎn)x是否有被搜索過(guò) public boolean[] flags; public int count; /** * 深度優(yōu)先搜索,找出與頂點(diǎn)V想通的所有頂點(diǎn) * @param G * @param V */ public DepthSearch(Graph G,int V) { //長(zhǎng)度置0 this.count = 0; //創(chuàng)建一個(gè)與頂點(diǎn)數(shù)量相同的標(biāo)記數(shù)組,標(biāo)記每個(gè)頂點(diǎn)是否被搜索過(guò) flags = new boolean[G.V()]; //深度優(yōu)先搜索 dfs(G, V); } /** * 深度優(yōu)先搜索實(shí)現(xiàn) * @param G * @param V */ public void dfs(Graph G,int V) { //被搜的頂點(diǎn)V標(biāo)記為搜索過(guò) flags[V] = true; for(int w : G.qTable[V]) { if(!flags[w]) { System.out.println("搜索的頂點(diǎn):"+w); dfs(G, w); } } //相通的點(diǎn)+1 count++; } /** * 返回與頂點(diǎn)V相通的所有頂點(diǎn) * @return */ public int count() { return count; } /** * 判斷頂點(diǎn)w是否與v相通 * @param w * @return */ public boolean isConnected(int w) { return flags[w]; } } |
五、 廣度優(yōu)先搜索
圖的搜索算法中,廣度優(yōu)先搜索指的是搜索到的頂點(diǎn)既有子結(jié)點(diǎn)又有兄弟結(jié)點(diǎn),優(yōu)先搜索兄弟結(jié)點(diǎn)。如下演示為圖的廣度優(yōu)先搜索順序
為了不對(duì)已搜索過(guò)的頂點(diǎn)重復(fù)搜索,我們需要有個(gè)布爾類型的數(shù)組來(lái)標(biāo)記頂點(diǎn)是否被搜索過(guò),搜索過(guò)的就標(biāo)記為true,不再進(jìn)行廣度搜索,提高效率的同時(shí),該數(shù)組也能判斷頂點(diǎn)A到B是否相通
因?yàn)橄嗤ǖ拇硭阉鬟^(guò),會(huì)被標(biāo)記為true。
|
/** * 廣度優(yōu)先搜索 * * @author jiyukai */ public class WeightSearch { // 標(biāo)記頂點(diǎn)x是否有被搜索過(guò) public boolean[] flags; // 聯(lián)通的點(diǎn)數(shù)量 public int count; // 用來(lái)存儲(chǔ)待搜索鄰接表的點(diǎn) private Queue<Integer> waitSearch; /** * 深度優(yōu)先搜索,找出與頂點(diǎn)V想通的所有頂點(diǎn) * * @param G * @param V */ public WeightSearch(Graph G, int V) { // 長(zhǎng)度置0 this.count = 0; // 創(chuàng)建一個(gè)與頂點(diǎn)數(shù)量相同的標(biāo)記數(shù)組,標(biāo)記每個(gè)頂點(diǎn)是否被搜索過(guò) flags = new boolean[G.V()]; // 初始化待搜索頂點(diǎn)隊(duì)列 waitSearch = new Queue<>(); // 深度優(yōu)先搜索 wfs(G, V); } /** * 深度優(yōu)先搜索實(shí)現(xiàn) * * @param G * @param V */ public void wfs(Graph G, int V) { // 被搜的頂點(diǎn)V標(biāo)記為搜索過(guò) flags[V] = true; // 將待搜索的元素入隊(duì)列 for (int w : G.qTable[V]) { waitSearch.enqueue(w); } while (!waitSearch.isEmpty()) { int searchKey = waitSearch.dequeue(); if (!flags[searchKey]) { wfs(G, searchKey); } } // 相通的點(diǎn)+1 count++; } /** * 返回與頂點(diǎn)V相通的所有頂點(diǎn) * * @return */ public int count() { return count; } /** * 判斷頂點(diǎn)w是否與v相通 * * @param w * @return */ public boolean isConnected(int w) { return flags[w]; } } |
六、 路徑查找
導(dǎo)航中常用的一個(gè)場(chǎng)景就是起點(diǎn)s到重點(diǎn)v之間是否存在一條可連通的路徑,若存在,需要走什么樣的路到達(dá)。
基于深度搜索來(lái)實(shí)現(xiàn)圖的搜索,新增一個(gè)數(shù)組conn[]來(lái)記錄各個(gè)連通點(diǎn)之間的關(guān)系,索引為頂點(diǎn),值為指向頂點(diǎn)的相鄰頂點(diǎn),假設(shè)0為起點(diǎn),在conn數(shù)組構(gòu)建完畢后,我們通過(guò)索引0找到頂點(diǎn)4
在通過(guò)索引4找到頂點(diǎn)5,依次找下去,并將找到的頂點(diǎn)依次入棧,則最后能找到和0相通的其他頂點(diǎn)和中間經(jīng)過(guò)的路徑。
將找到的頂點(diǎn)依次入棧
|
/** * 基于深度優(yōu)先搜索實(shí)現(xiàn)的路徑查找 * @author jiyukai */ public class DepthSearchPath { // 標(biāo)記頂點(diǎn)x是否有被搜索過(guò) public boolean[] flags; // 聯(lián)通的點(diǎn)數(shù)量 public int count; // 初始化起點(diǎn) public int s; // 連通頂點(diǎn)的關(guān)系 public int conn[]; /** * 深度優(yōu)先搜索,找出與頂點(diǎn)V想通的所有頂點(diǎn) * @param G * @param V */ public DepthSearchPath(Graph G, int s) { // 長(zhǎng)度置0 this.count = 0; // 創(chuàng)建一個(gè)與頂點(diǎn)數(shù)量相同的標(biāo)記數(shù)組,標(biāo)記每個(gè)頂點(diǎn)是否被搜索過(guò) flags = new boolean[G.V()]; // 初始化連通頂點(diǎn)的關(guān)系數(shù)組 conn = new int[G.V()]; // 初始化起點(diǎn) this.s = s; // 深度優(yōu)先搜索 wfs(G, s); } /** * 深度優(yōu)先搜索實(shí)現(xiàn) * @param G * @param V */ public void wfs(Graph G, int v) { // 被搜的頂點(diǎn)V標(biāo)記為搜索過(guò) flags[v] = true; for (int w : G.qTable[v]) { if (!flags[w]) { conn[w] = v; wfs(G, w); } } // 相通的點(diǎn)+1 count++; } /** * 返回與頂點(diǎn)V相通的所有頂點(diǎn) * @return */ public int count() { return count; } /** * 判斷頂點(diǎn)w是否與v相通 * @param w * @return */ public boolean isConnected(int w) { return flags[w]; } /** * 找出s到頂點(diǎn)v的路徑 * @param v * @return */ public Stack<Integer> pathTo(int v){ //判斷是否連接 if(!isConnected(v)) { return null; } //創(chuàng)建存放連通圖的棧 Stack<Integer> stack = new Stack<Integer>(); //找出與起點(diǎn)s連通路徑上的頂點(diǎn) for(int x=v;x!=s;x=conn[x]) { stack.push(x); } //起點(diǎn)入棧 stack.push(s); return stack; } } |
總結(jié)
- 上一篇: 经典的卷积神经网络简介
- 下一篇: 三星 SmartThings Stati