目录
图的概念

图(Graph)是一种复杂的非线性表结构。 图中的元素叫做顶点(vertex),图中的一个顶点可以与任意其他顶点建立连接关系。把这种建立的关系叫做边(edge),跟顶点相连接的边的条数叫做度(degree)。
边有方向的图叫做有向图,比如A点到B点的直线距离,微信的添加好友是双向的。
边无方向的图叫无向图,比如网络拓扑图。
带权图(weighted graph)则是在图中,每条边都有一个权重(weight),我们可以通过这个权重来表示 一些可度量的值。
图具有很广泛的应用,比如社交网络,电子地图,多对多的关系就可以用图来表示。
图的存储
邻接矩阵
图最直观的存储方法就是,邻接矩阵,邻接矩阵的底层是一个二维数组,如图一所示,采用临界矩阵的方式如下
当图为无向图时,顶点 A 与顶点 B 有边相连,则在邻接矩阵中 [A][B] 与 [B][A] 都用 1 来表示
当图为有向图时,若顶点 A 指向顶点 B,则在邻接矩阵中 [A][B] 用 1 来表示
当图为带权图时,则在邻接矩阵中用 对应边的权重 来表示
import java.util.ArrayList;
import java.util.List;
/**
* 邻接矩阵实现
*/
public class Graph1 {
/** 存储点的链表 */
private List vertexList;
/**邻接矩阵,用来存储边 */
private int[][] edges;
/** 边的数目 */
private int numOfEdges;
public Graph1(int n) {
//初始化邻接矩阵,一维数组,和边的数目
edges = new int[n][n];
vertexList = new ArrayList(n);
numOfEdges = 0;
}
/** 得到结点的个数 */
public int getNumOfVertex(){
return vertexList.size();
}
/** 得到边的数目 */
public int getNumOfEdges(){
return numOfEdges;
}
/** 返回结点 i 的数据 */
public Object getValueByIndex(int n){
return vertexList.get(n);
}
/** 返回v1, v2的权值 */
public int getWeight(int v1, int v2){
return edges[v1][v2];
}
/** 插入结点 */
public void insertVertex(Object vertex){
vertexList.add(vertex);
}
/** 插入边 */
public void insertEdge(int v1, int v2, int weight){
edges[v1][v2] = weight;
numOfEdges++;
}
}
class testGraph{
public static void main(String[] args) {
//结点个数与边的个数
int n = 4, e = 4;
//结点标识
String labels[] = {"v1", "v2", "v3", "v4"};
Graph1 graph1 = new Graph1(n);
//插入节点
for (String label : labels) {
graph1.insertVertex(label);
}
graph1.insertEdge(0, 1, 2);
graph1.insertEdge(0, 2, 5);
graph1.insertEdge(2, 3, 2);
graph1.insertEdge(3, 1, 9);
System.out.println("结点个数:" + graph1.getNumOfVertex());
System.out.println("边个数:" + graph1.getNumOfEdges());
}
}
邻接表
用邻接矩阵来表示一个图,简单直观但非常浪费空间,对于无向图来说,如果A[i][j] = 1 则 A[j][i] = 1。因此我们只需要存储一个就可以了。也就是说,无向图的二维数组中,如果我们将其用对角线划分上下两部分,只需要利用上面或下面一半的空间就足够了,另一半空间完全浪费掉了,如果我们存储的是稀疏图,顶点多,但是边较少,采用邻接矩阵的方法更加的浪费空间。
因此针对邻接矩阵浪费空间的问题,我们通过邻接表的方法来解决。
每一个顶点对应一个链表,链表中存储与这个顶点相连接的其他顶点。
如图我们如果采用邻接表的方法来存储的话就是
根据邻接表的结构和图,不难发现,图其实就是由顶点和边来组成的,因此我们可以抽象出来两个类,一个是Vertext类,一个是Edge边类
/**
* 顶点
*/
class Vertex {
String name;
Edge next;
public Vertex(String name, Edge next) {
this.name = name;
this.next = next;
}
}
/**
* 边
*/
class Edge {
String name;
int weight;
Edge next;
public Edge(String name, int weight, Edge next) {
this.name = name;
this.weight = weight;
this.next = next;
}
}
public class Graph2 {
Map<String, Vertex> vertexMap;
Graph2(){
this.vertexMap = new HashMap<>();
}
public void insertVertex(String vertexName){
Vertex vertex = new Vertex(vertexName, null);
vertexMap.put(vertexName, vertex);
}
public void insertEdge(String begin, String end, int weight){
Vertex beginVertex = vertexMap.get(begin);
if (beginVertex == null){
insertVertex(begin);
}
Edge edge = new Edge(end, weight, null);
if(beginVertex.next == null){
beginVertex.next = edge;
} else {
Edge lastEdge = beginVertex.next;
while(lastEdge.next != null){
lastEdge = lastEdge.next;
}
lastEdge.next = edge;
}
}
public void print(){
Set<Map.Entry<String, Vertex>> set = vertexMap.entrySet();
Iterator<Map.Entry<String, Vertex>> iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry<String, Vertex> entry = iterator.next();
Vertex vertex = entry.getValue();
Edge edge = vertex.next;
while (edge != null){
System.out.println(vertex.name + "指向" + edge.name + "权值为:" + edge.weight);
edge = edge.next;
}
}
}
}
class testGraph2{
public static void main(String[] args) {
Graph2 graph = new Graph2();
graph.insertVertex("A");
graph.insertVertex("B");
graph.insertVertex("C");
graph.insertVertex("D");
graph.insertVertex("E");
graph.insertVertex("F");
graph.insertEdge("C", "A", 1);
graph.insertEdge("F", "C", 2);
graph.insertEdge("A", "B", 4);
graph.insertEdge("E", "B", 2);
graph.insertEdge("A", "D", 5);
graph.insertEdge("D", "F", 4);
graph.insertEdge("D", "E", 3);
graph.print();
}
}
图的遍历
图的遍历是指,从给定图中任意指定的顶点出发,按照某种搜索方法沿着图的边访问图中的所有顶点,使每一个顶点仅被访问一次,这个过程是 图的遍历。 遍历过程中得到的顶点序列称为图遍历序列