欢迎光临
我们一直在努力

详解图的存储与遍历

目录

​编辑

图的存储

邻接矩阵

代码实现 

代码测试

邻接表

代码实现

代码测试

图的遍历

广度优先遍历

代码实现

深度优先遍历

代码实现


图的存储

因为图中既有节点,又有边(节点与节点之间的关系),因此,在图的存储中,只需要保存:节点和边关系即可。节点保存比较简单,只需要一段连续空间即可,那边关系该怎么保存呢?

邻接矩阵

因为节点与节点之间的关系就是连通与否,即为0或者1,因此邻接矩阵(二维数组)即是:先用一个数组将定点保存,然后采用矩阵来表示节点与节点之间的关系。

对于无向图来说:

对于有向图来说:

注意:

1. 无向图的邻接矩阵是对称的,第i行(列)元素之和,就是顶点i的度。有向图的邻接矩阵则不一定是对称的,第i行(列)元素之后就是顶点i 的出(入)度。 2. 如果边带有权值,并且两个节点之间是连通的,上图中的边的关系就用权值代替,如果两个顶点不通,则使用无穷大代替。

3. 用邻接矩阵存储图的优点是能够快速知道两个顶点是否连通,缺陷是如果顶点比较多,边比较少时,矩阵中存储了大量的0成为系数矩阵,比较浪费空间,并且要求两个节点之间的路径不是很好求。

代码实现 

Constant.java

public class Constant {
public static final int MAX = Integer.MAX_VALUE;
}

GraphByMatrix.java

public class GraphByMatrix {

private char[] arrayV;//顶点数组
private int[][] matrix;//矩阵
private boolean isDirect;//是否是有向图
public GraphByMatrix(int size,boolean isDirect) {
this.arrayV = new char[size];
matrix = new int[size][size];
for (int i = 0; i < size; i++) {
Arrays.fill(matrix[i],Constant.MAX);
}
this.isDirect = isDirect;
}

public void initArrayV(char[] array) {
for (int i = 0; i < array.length; i++) {
arrayV[i] = array[i];
}
}

public void addEdge(char srcV,char destV,int weight) {
int srcIndex = getIndexOfV(srcV);
int destIndex = getIndexOfV(destV);
matrix[srcIndex][destIndex] = weight;
//如果是无向图 那么相反的位置 也同样需要置为权重
if(!isDirect) {
matrix[destIndex][srcIndex] = weight;
}
}

private int getIndexOfV(char v) {
for (int i = 0; i < arrayV.length; i++) {
if(arrayV[i] == v) {
return i;
}
}
return -1;
}

public int getDevOfV(char v) {
int count = 0;

int srcIndex = getIndexOfV(v);

for (int i = 0; i < arrayV.length; i++) {
if(matrix[srcIndex][i] != Constant.MAX) {
count++;
}
}

//计算有向图的入度
if(isDirect) {
for (int i = 0; i < arrayV.length; i++) {
if(matrix[i][srcIndex] != Constant.MAX) {
count++;
}
}
}
return count;
}

public void printGraph() {
for (int i = 0; i < arrayV.length; i++) {
System.out.print(arrayV[i]+" ");
}
System.out.println();
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if(matrix[i][j] == Constant.MAX) {
System.out.print("∞ ");
}else {
System.out.print(matrix[i][j]+" ");
}
}
System.out.println();
}
}
}

代码测试

public static void main(String[] args) {

GraphByMatrix graph = new GraphByMatrix(4,false);
char[] array = {'A','B','C','D'};
graph.initArrayV(array);

graph.addEdge('A','B',1);
graph.addEdge('A','D',1);
graph.addEdge('B','A',1);
graph.addEdge('B','C',1);
graph.addEdge('C','B',1);
graph.addEdge('C','D',1);
graph.addEdge('D','A',1);
graph.addEdge('D','C',1);

graph.printGraph();
}

运行结果:

邻接表

使用数组表示顶点的集合,使用链表表示边的关系。

1. 无向图邻接表存储

注意:无向图中同一条边在邻接表中出现了两次。如果想知道顶点vi的度,只需要知道顶点vi边链表集合中结点的数目即可。

2. 有向图邻接表存储

注意:有向图中每条边在邻接表中只出现一次,与顶点vi对应的邻接表所含结点的个数,就是该顶点的出度,也称出度表,要得到vi顶点的入度,必须检测其他所有顶点对应的边链表,看有多少边顶点的dst取值是i。

代码实现

Constant.java

​public class Constant {
public static final int MAX = Integer.MAX_VALUE;
}

GraphByNode.java

import java.util.ArrayList;

public class GraphByNode {
static class Node {
public int src;//起始位置
public int dest;//目标位置
public int weight;//权重
public Node next;

public Node(int src, int dest, int weight) {
this.src = src;
this.dest = dest;
this.weight = weight;
}
}

public char[] arrayV;
public ArrayList<Node> edgList;//存储边
public boolean isDirect;

public GraphByNode(int size,boolean isDirect) {
this.arrayV = new char[size];
edgList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
edgList.add(null);
}
this.isDirect = isDirect;
}

public void initArrayV(char[] array) {
for (int i = 0; i < array.length; i++) {
arrayV[i] = array[i];
}
}

public void addEdge(char srcV,char destV,int weight) {
int srcIndex = getIndexOfV(srcV);
int destIndex = getIndexOfV(destV);
addEdgeChild(srcIndex,destIndex,weight);
//无向图 需要添加两条边
if(!isDirect) {
addEdgeChild(destIndex,srcIndex,weight);
}
}

private void addEdgeChild (int srcIndex , int destIndex,int weight) {
//这里拿到是头节点
Node cur = edgList.get(srcIndex);
while (cur != null) {
if(cur.dest == destIndex) {
return;
}
cur = cur.next;
}
//之前没有存储过这条边
Node node = new Node(srcIndex,destIndex,weight);
node.next = edgList.get(srcIndex);
edgList.set(srcIndex,node);
}

private int getIndexOfV(char v) {
for (int i = 0; i < arrayV.length; i++) {
if(arrayV[i] == v) {
return i;
}
}
return -1;
}

public void printGraph() {
for (int i = 0; i < arrayV.length; i++) {
System.out.print(arrayV[i]+"->");
Node cur = edgList.get(i);
while (cur != null) {
System.out.print(arrayV[cur.dest]+" ->");
cur = cur.next;
}
System.out.println();
}
}

public int getDevOfV(char v) {
int count = 0;
int srcIndex = getIndexOfV(v);
Node cur = edgList.get(srcIndex);

while (cur != null) {
count++;
cur = cur.next;
}

//只是计算了出度
if(isDirect) {
int destIndex = srcIndex;
for (int i = 0; i < arrayV.length; i++) {
if(i == destIndex) {
continue;
}else {
Node pCur = edgList.get(i);
while (pCur != null) {
if(pCur.dest == destIndex) {
count++;
}
pCur = pCur.next;
}
}
}
}
return count;
}
}

代码测试

public static void main(String[] args) {

GraphByNode graph = new GraphByNode(4,true);
char[] array = {'A','B','C','D'};
graph.initArrayV(array);

graph.addEdge('A','B',1);
graph.addEdge('A','D',1);
graph.addEdge('B','A',1);
graph.addEdge('B','C',1);
graph.addEdge('C','B',1);
graph.addEdge('C','D',1);
graph.addEdge('D','A',1);
graph.addEdge('D','C',1);

System.out.println("getDevOfV:: "+graph.getDevOfV('A'));
graph.printGraph();
}

运行结果:

图的遍历
广度优先遍历

步骤:

1.选择起始节点:从图中选择一个节点作为起始点。 2.访问起始节点:访问该节点,并将其标记为已访问。 3.加入队列:将起始节点加入队列中。 4.循环处理队列:当队列不为空时,进行以下操作:         从队列中取出一个节点(队首元素)。         访问该节点的所有未访问的邻接节点。         将这些未访问的邻接节点加入队列中。         将当前节点标记为已访问。 5.重复步骤4:直到队列为空,即所有可达的节点都被访问过。

代码实现

public void bfs(char v) {
//1、定义一个visited数组 标记当前这个顶点是不是已经被 访问的
boolean[] visited = new boolean[arrayV.length];
//2、定义一个队列,来辅助完成广度优先遍历
Queue<Integer> queue = new LinkedList<>();
int srcIndex = getIndexOfV(v);
queue.offer(srcIndex);
while (!queue.isEmpty()) {
int top = queue.poll();
System.out.print(arrayV[top]+"->");
visited[top] = true;//每次弹出一个元素 就置为true
for (int i = 0; i < arrayV.length; i++) {
if(matrix[top][i] != Constant.MAX && !visited[i]) {
queue.offer(i);
visited[i] = true;
}
}
}
}

深度优先遍历

步骤:

1.选择起始节点:从图中选择一个节点作为起始点。 2.访问起始节点:访问该节点,并将其标记为已访问。 3.递归探索:对当前节点的每个未访问的邻接节点,递归地进行深度优先遍历。         如果当前节点没有未访问的邻接节点,则回溯到前一个节点。 4.重复步骤3:直到所有可达的节点都被访问过。

代码实现

public void dfs(char v) {
boolean[] visited = new boolean[arrayV.length];
int srcIndex = getIndexOfV(v);
dfsChild(srcIndex,visited);

}

private void dfsChild(int srcIndex,boolean[] visited) {
System.out.print(arrayV[srcIndex]+"->");
visited[srcIndex] = true;

for (int i = 0; i < arrayV.length; i++) {
if(matrix[srcIndex][i] != Constant.MAX && !visited[i]) {
dfsChild(i,visited);
}
}
}

赞(0)
未经允许不得转载:171主机测评 » 详解图的存储与遍历
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址