あなたは、私は私がしたいと思います考えてはいけない、私はそれはものではありません図と述べました。 - 明はそれを明らかにしました。
なぜチャート
多くの関係を表すために使用されます。
直接的な関係に直線状の即時の前任者と後継者に限定
木は、唯一の親が直接の前駆体であることができます
基本コンセプト
サイド:2つの接続ノード
頂点(頂点):データ要素は、頂点は、ゼロまたはそれ以上の隣接する要素を有していてもよいです。
パスは:例えば、のD-> Cからの経路が存在します
1)D-> B-> C
2)D-> A-> B-> C
分類
無向グラフ:上記のように、頂点間の無指向性接続。例えばAB、すなわちA-> Bであってもよいが> A.をB-できます
有向グラフ:接続された頂点間の方向、例えばAB、
のみA-> BがB-> Aありません
重み付きグラフ:重みを持つこのグラフ、また、ネットワークエッジとして知られています
表現
あるいはまた、ストレージ構造
2つの方法の図表現を2次元配列表現(隣接行列)は、リスト(隣接リスト)を表します。
隣接行列
私たちは、マップを表現するために2つの配列を使用します。
描画を格納するために使用される一次元アレイ頂点情報を
二次元配列は、寄託格納するサイド情報
全て求めて隣接頂点VIは、行i及び再び走査における行列要素であるarr[i][j]
隣接点1と同様。
隣接行列は、n図の頂点の隣接頂点のパターンとの関係を示す行列である。行列は、行とCOLある。1 .... N点を表します。
以下が観察され、無向グラフの例です。
隣接行列は対称行列であります
主対角線は0であり、エッジ自体への頂点は存在しません。
隣接リスト
エッジの存在を懸念のみ。
隣接行列は、n個の頂点の各側面のためのスペースを割り当てる必要があるため、多くの側面は、存在しない空間の一部の損失を引き起こすであろうがあります。
唯一の懸念は、側面に存在する隣接リストを達成するために、我々は存在しない側を気にしないでください。したがってそこには無駄なスペースがなく、隣接する表の配列組成を示しています。
チャートを作成します。
図のコードは、以下の構造を実装します
A B C D E
A 0 1 1 0 0
B 1 0 1 1 1
C 1 1 0 0 0
D 0 1 0 0 0
E 0 1 0 0 0
//说明
//(1) 1 表示能够直接连接
//(2) 0 表示不能直接连接
アイデアの分析:
我々は2つの配列を必要とします
文字列型は、頂点のArrayListを格納するために使用されます
情報側を格納するための二次元アレイ
一般的な方法:
- 頂点を挿入します
- エッジを挿入
- ノードの数を返します。
- 累積的に、各挿入端で得られたエッジの数
- 図2に対応するマトリクスディスプレイ。
- 先頭ノードI(添字)データに対応した0 - > "" 1 - > "B" 2 - > "C"
- V1とV2の重みを返す、アレイのどの重み存在。
コードの実装
import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedList; /** * @ClassName: Demo20_Graph * @author: benjamin * @version: 1.0 * @description: TODO * @createTime: 2019/08/26/11:20 */ public class Demo20_Graph { // 用于存储顶点的集合 private ArrayList<String> vertexList; // 存储边的信息的二维数组 private int[][] edges; // 记录边的数目 private int numOfEdges; // 定义数组boolean[],记录某个结点是否被访问 public static void main(String[] args) { Demo20_Graph graph = new Demo20_Graph(5); // 插入顶点 String vertexs[] = {"A","B","C","D","E","F"}; for(String vertex:vertexs){ graph.insertVertex(vertex); } // 添加边 // A-B A-C B-C B-D B-E graph.insertEdge(0,1,1); graph.insertEdge(0,2,1); graph.insertEdge(1,2,1); graph.insertEdge(1,3,1); graph.insertEdge(1,4,1); // 显示一把邻结矩阵 graph.showGraph(); } // 构造器,初始化矩阵和顶点集合 Demo20_Graph(int n) { // 集合长度为n vertexList = new ArrayList<String>(n); // 邻接矩阵为n*n edges = new int[n][n]; numOfEdges = 0; } //常用的方法 //返回结点的个数 public int getNumOfVertex() { return vertexList.size(); } //显示图对应的矩阵 public void showGraph() { for(int[] vertex:edges){ System.out.println(Arrays.toString(vertex)); } } //得到边的数目 public int getNumOfEdges() { return numOfEdges; } //返回结点i(下标)对应的数据 0->"A" 1->"B" 2->"C" public String getValueByIndex(int i) { return vertexList.get(i); } //返回v1和v2的权值 public int getWeight(int v1, int v2) { return edges[v1][v2]; } //插入结点 public void insertVertex(String vertex) { vertexList.add(vertex); } /** * 添加边 * * @param v1 表示点的下标即使第几个顶点 "A"-"B" "A"->0 "B"->1 * @param v2 第二个顶点对应的下标 * @param weight 表示权,1或者0 */ public void insertEdge(int v1, int v2, int weight) { edges[v1][v2] = weight; edges[v2][v1] = weight; numOfEdges++; } }
出力:
[0、1、1、0、0]
[1、0、1、1、1]
[1、1、0、0、0]
[0、1、0、0、0]
[0、1、0 、0、0]
トラバースグラフ
すなわち、ノードのアクセスポイント。ので、複数のノードがあるグラフは、これらのノードをトラバースする方法を、具体的な戦略を必要とする2つのアクセスポリシーがあります。
- 深さ優先探索
- 幅優先トラバーサル
フィールドで行われる研究に戻っ芸術に精通元、およびを選択した後、
後者は、起業家精神のように、既知から始め、すでに関心の緩やかな再発掘部分から何かを知っています。
深さ優先探索(DFS)
基本的な考え方
深さ優先探索マップ(深さ優先探索)
初期アクセスノードから、初期のアクセス・ノードは、複数の隣接ノードを有していてもよく、深さ優先トラバーサル戦略は、まず、第1の隣接ノードを訪問することであり、次いで、隣接ノードへの最初のノードへのアクセスとしてアクセスされますその最初の隣接ノードは、理解することができる:最初に、毎回、現在のアクセスノードの完了後、現在のノードの最初の隣接ノードにアクセスします。
私たちは、深さ優先探索は再帰的プロセスであり、この戦略は垂直掘削深さ優先のアクセスであることがわかります
DFSは、上の図を達成するためにどのようにステップ?
初期としてノードは、Aから出発し、訪れた最初の標識
最初の隣接ノードBは、Bが存在すると、初期ノードBとして今、訪問されていません
Bから出発して、標識BがCの存在により、第1隣接ノードBへのアクセスを有し、Cは、アクセスされないが、今、初期ノードCとして
D Cの次のノードのノードは、バックBの次の隣接ノードE Bの端部に存在しません
アルゴリズムのステップ
- 訪問したように、初期アクセスノードv、およびノードvがマーク。
- 第1隣接ノードWにおけるノードvを探します。
- 存在するW場合、存在しないW場合、プロセスは継続する次のノードから1、Vに戻り、4に進みます。
- アクセスは、再帰深さ優先トラバーサルのW、Wない場合(すなわち、W Vとして他方は、次いでステップ123)。
- ステップ3に進み、次の隣接ノードノードwの隣接ノードvの検索。
コードの実装
/**
* 得到第一个邻接结点的下标 w
*
* @return 如果存在就返回对应的下标,否则返回-1
*/
public int getFirstNeighbor(int index) {
for (int j = 0; j < vertexList.size(); j++) {
if (edges[index][j] > 0) {
return j;
}
}
return -1;
}
/**
* @Description: 根据前一个邻接结点的下标来获取下一个邻接结点
* @Param: [v1, v2]
* @return: int
* @Author: benjamin
* @Date: 2019/8/26
*/
public int getNextNeighbor(int v1, int v2) {
for (int j = v2 + 1; j < vertexList.size(); j++) {
if (edges[v1][j] > 0) {
return j;
}
}
return -1;
}
//深度优先遍历算法
//i 第一次就是 0
private void dfs(boolean[] isVisited, int i) {
// 首先输出访问的结点
System.out.print(getValueByIndex(i) + "->");
// 将结点设置为已经访问
isVisited[i] = true;
// 查找结点i的第一个邻接结点w
int w = getFirstNeighbor(i);
// 只要w不为1,说明有
while (w != -1){
if(!isVisited[w]){
dfs(isVisited,w);
}
// 如果该结点已经被访问过,则访问i的下下一个邻接结点
w = getNextNeighbor(i,w);
}
}
//对dfs 进行一个重载, 遍历我们所有的结点,并进行 dfs
public void dfs() {
isVisited = new boolean[vertexList.size()];
//遍历所有的结点,长度为集合的长度,进行dfs[回溯]
for(int i = 0; i < getNumOfVertex(); i++) {
if(!isVisited[i]) {
dfs(isVisited, i);
}
}
}
幅優先トラバーサル(BFS)
基本的な考え方
幅優先探索グラフ(広いまず検索)。
幅優先トラバーサルを使用して階層的検索と同様の処理が、順序キューを維持する必要があるこれらのノードにアクセスするので、この順に隣接するノードに、ノードを訪問
アルゴリズムのステップ
- 訪問したように、初期のアクセス・ノードは、ノードvとVをラベル。
- ノード・キューV
- キューが空でない場合は、そうでない場合、アルゴリズムは終了し、継続します。
- キューは、HOLノードuを得ることができます。
- 第1隣接ノードWのノードUを見つけます。
- 隣接ノードwをノードuが存在しない場合、ステップ3に進み、そうでなければ、サイクルは3つのステップを以下
- まだワットノードがアクセスされた場合は訪問したとして、アクセスノードは、Wとマーク。
- ノード・キューのw
- 隣接ノードを、以下のワットノードUへワット次の隣接ノードを検索し、手順6に進みます。
コードの実装
/**
* 得到第一个邻接结点的下标 w
*
* @return 如果存在就返回对应的下标,否则返回-1
*/
public int getFirstNeighbor(int index) {
for (int j = 0; j < vertexList.size(); j++) {
if (edges[index][j] > 0) {
return j;
}
}
return -1;
}
/**
* @Description: 根据前一个邻接结点的下标来获取下一个邻接结点
* @Param: [v1, v2]
* @return: int
* @Author: benjamin
* @Date: 2019/8/26
*/
public int getNextNeighbor(int v1, int v2) {
for (int j = v2 + 1; j < vertexList.size(); j++) {
if (edges[v1][j] > 0) {
return j;
}
}
return -1;
}
//对一个结点进行广度优先遍历的方法
private void bfs(boolean[] isVisited, int i) {
int u ; // 表示队列的头结点对应下标
int w ; // 邻接结点w
//队列,记录结点访问的顺序
LinkedList queue = new LinkedList();
//访问结点,输出结点信息
System.out.print(getValueByIndex(i) + "=>");
//标记为已访问
isVisited[i] = true;
//将结点加入队列
queue.addLast(i);
while( !queue.isEmpty()) {
//取出队列的头结点下标
u = (Integer)queue.removeFirst();
//得到第一个邻接结点的下标 w
w = getFirstNeighbor(u);
while(w != -1) {//找到
//是否访问过
if(!isVisited[w]) {
System.out.print(getValueByIndex(w) + "=>");
//标记已经访问
isVisited[w] = true;
//入队
queue.addLast(w);
}
//以u为前驱点,找w后面的下一个邻结点
w = getNextNeighbor(u, w); //体现出我们的广度优先
}
}
}
//遍历所有的结点,都进行广度优先搜索
public void bfs() {
isVisited = new boolean[vertexList.size()];// 清空标志位
for (int j = 0; j < vertexList.size(); j++) {
if (!isVisited[j]) {
bfs(isVisited, j);
}
}
}