图的基本概念及存储结构

图的基本概念:

定义:图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V(vertex)是图G中顶点的集合,E(edge)是图G中边的集合。

分类:1、按有无方向分类:有向图和无向图。


无向图由顶点和边组成,有向图由顶点和弧组成, 弧有弧头(head)和弧尾(tail)两部分。

2、如果任意的两个顶点之间都存在边,叫完全图;有向的图叫有向完全图。

3、如果无重复的边或者顶点到自身的边则叫做简单图。数学表示:无向图用()表示, 有向图用<>表示。


4、带权值的图和不带权值的图。

           边带权值的图又叫做网。

图的顶点和边间关系:

顶点的度(degree): 就是顶点关联边的数目。在有向图中,分为出度:方向背向顶点的边;入度:方向指向顶点的边。有向图的度为入度和出度的和。

路径长度:路径上边或者弧的数目。

联通性:

在无向图中,从一个顶点出发,有到达其他任意顶点的路径,则称这个图是联通的。具有这种性质的有向图叫做强连通图。

如果把一个有向图的所有有向边去掉方向形成的无向图被称为这个有向图的基础图,也叫基图。

有向图不是强连通,但是他的基图是连通的,则被称为弱连通。

图的表示方法:

1、邻接矩阵:使用一个顶点数组存储顶点,使用一个边数组存储顶点间是否存在边。

a[i, j] = 1  (i , j ) 属于Edge, 否则等于0。即0表示没有边,1表示有边。

 

带权的邻接矩阵的表示方法:

a[i, j] = weight   : 若Vi 不等于Vj && (Vi, Vj) 或<Vi, Vj>属于Edges 

         = ∞ : 若Vi 不等于Vj && (Vi, Vj) 或<Vi, Vj>不属于Edges 

= 0 : Vi = Vj


2、邻接链表:

顶点表中的第i个顶点信息中保存有该顶点的数据值data和一个链表的头指针adj, 通过adj可以找到与节点Vi对应的边链表的第一个边节点。 在边界点总,节点保存表示dest:指示该边的另一个节点的顶点号; 还配有指针link,指向下一个边节点, 如果是带权值的图,则边界点中再添加一个cost,用于保存该边的权值节点。

3、邻接多重表和十字链表表示(简单介绍):

无向邻接多重表:

边节点:vertex1和vertex2是顶点域,指明该边所依附的两个节点,path1, path2表示链表指针,指向依附于vertex1和vertex2的下一条边。

mark : 标记域,是否接受过处理。0:未处理; 1 : 处理 vertex1 vertex2 path1 path2

顶点节点:

data:存放顶点相关信息 firstout:指针域,指向依附的第一条边

有向十字链表:

边节点:vertex1和vertex2是顶点域,分别表示有向边的始顶点和终顶点号,path1, path2表示链表指针,path1指向于该边有同一始顶点的下一条边,path2,指向与该边有同一终顶点的下一条边。


带权值的邻接矩阵实现:

图接口:

public interface GGraph<T> {

	//final 关键字:修饰类、变量、方法,表示不能被修改, 不能被覆盖和继承 
	public static final int MAX_WEIGHT = 999999; // 象征无穷大

	int vertex_Number(); // 节点数

	T getElement(int i); // 顶点的相关值

	int getWeight(int i, int j); // 返回<Vi, Vj>边的权值

	int insertVertex(T x);   // 插入元素值位x的结点值, 返回顶点序号

	void insertEdge(int i, int j, int weight); // 插入一条权值为weight的边<Vi, Vj>

	void removeVertex(int i);  // 删除顶点Vi及其相关的边

	void removeEdge(int i, int j); // 删除<Vi, Vj>

	int getNextNeighbor(int i, int j); //返回Vi在Vj后面的下一个邻接节点序号

}

带权值边的类:

public class Edge implements Comparable<Edge>{

	public int begin, end, weight;
	
	public Edge(int begin, int end, int weight){
		this.begin = begin;
		this.end = end;
		this.weight = weight;
	}

	public String toString(){
		return "[" + begin + "," + end + "," + weight + "]";
	}

	@Override
	public int compareTo(Edge o) {
		if(this.begin != o.begin){
		return this.begin - o.begin;       //起点不同,返回起点的差值,否则返回终点的差值
	}
	return this.end - o.end;
	}
}

实现邻接矩阵的插入、删除等:

public class AdjMatrixGraph<T>{

	// 储存图的顶点集合
	protected ArrayList<T> vertexList;
	// 图的邻接矩阵
	protected int[][] adjMatrix; 
	// 最大值设置
	private final int MAX_WEIGHT = 9999999;

	// 图的初始化
	public AdjMatrixGraph(int size){
		size = size > 10 ? 10 : size; 

		// 定义集合 和矩阵大小
		this.vertexList = new ArrayList<T>(size); 
		this.adjMatrix = new int[size][size];
		// 初始化矩阵
		for(int i = 0; i < size; i ++){
			for(int j = 0; j < size; j ++){
				// 自己和自己的权值是0
				adjMatrix[i][j] = (i == j) ? 0 : MAX_WEIGHT;
			}
		}
	}

	//根据顶点集合和边集合构造图
	
	public AdjMatrixGraph(T[] vertices, Edge [] edges){
		this(vertices.length);

		if(vertices == null){
			return;
		}
		for(int i = 0; i < vertices.length; i ++){
			insertVertex(vertices[i]);
		}
		if(edges != null){
			for(int j = 0; j < edges.length; j ++){
				insertEdge(edges[j]);
			}
		}
	}

	//返回顶点个数
	public int vertex_Number(){
		return this.vertexList.size();
	}

	//返回顶点Vi的数据元素值, 若无效返回null
	public T getElement(int i){
		return this.vertexList.get(i);
	}

	// 返回<Vi, Vj>边的权值
	public int getWeight(int i, int j){
		return adjMatrix[i][j];
	}

	// 插入边及其权值
	public void insertEdge(Edge edge){
		this.insertEdge(edge.begin, edge.end, edge.weight);
	}

	public void insertEdge(int i, int j, int weight) {

		int len = this.vertex_Number();
		if(i >= 0 && i < len && j >= 0 && j < len || i != j && this.adjMatrix[i][j] == MAX_WEIGHT){
			this.adjMatrix[i][j] = weight;
		}
	}

	// 插入新的节点, 并返回它的位置
	public int insertVertex(T vertex) {
		this.vertexList.add(vertex);
		//扩容
		if(this.vertex_Number() > this.adjMatrix.length){
			// 复制元素
			int temp[][] = adjMatrix, i, j;

			this.adjMatrix = new int[temp.length * 2][temp.length * 2];

			for(i = 0; i < temp.length; i ++){
				for(j = 0; j < temp.length; j ++){
					this.adjMatrix[i][j] = temp[i][j];
				}
				for(j = temp.length; j < temp.length * 2; j ++){
					this.adjMatrix[i][j] = MAX_WEIGHT;
				}
			}		
			for(i = temp.length; i < temp.length * 2; i ++){
				for(j = 0; j < temp.length * 2; j ++){
					this.adjMatrix[i][j] = (i == j) ? 0 : MAX_WEIGHT;
				}
			}
		}
		return this.vertexList.size() - 1;	
	}

	//移除节点 它的对应的矩阵也在变化
	public void removeVertex(int x){
		int len = this.vertex_Number();
		if(x < 0 || x > len){
			System.out.println("error!");
			return;
		}
		this.vertexList.remove(x);
		//删除的是关于它的一行一列
		for(int i = 0; i < x; i ++){
			for(int j = x + 1; j < len; j ++){
				// 左移一位
				this.adjMatrix[i][j - 1] = this.adjMatrix[i][j];
			}
		}
		for(int i = x + 1; i < len; i ++){
			for(int j = 0; j < x; j ++){
				// 上移一位
				this.adjMatrix[i - 1][j] = this.adjMatrix[i][j];
			}
		}
		for(int i = x + 1; i < len; i ++){
			for(int j = i + 1; j < len; j ++){
				// 上移一位
				this.adjMatrix[i - 1][j - 1] = this.adjMatrix[i][j];
			}
		}
	}

	//移除边 直接让它无效即可
	public void removeEdge(int i, int j){
		if(i >= 0 && i < vertex_Number() && j >= 0 && j < vertex_Number() && i != j){
			this.adjMatrix[i][j] = MAX_WEIGHT;
		}
	}

	public String toString(){
		String str = "顶点集合:" + this.vertexList.toString() + "\n 邻接矩阵是:\n";
		int len = this.vertex_Number();

		for(int i = 0; i < len; i ++){
			for(int j = 0; j < len; j ++){
				str += this.adjMatrix[i][j] == MAX_WEIGHT ? " ∞" : " " + this.adjMatrix[i][j];
			}
			str += "\n";
		}		
		return str;
	}
}

测试代码:

public class Test {

	public static void main(String[] args) {
		String [] vertices = {"A", "B", "C", "D", "E"};
		Edge [] edges = {new Edge(0, 1, 5), new Edge(0, 3, 2), new Edge(1, 0, 5),
				new Edge(1, 2, 7), new Edge(1, 3, 6), new Edge(2, 1, 7),
				new Edge(2, 3, 8), new Edge(2, 4, 3), new Edge(3, 0, 2),
				new Edge(3, 1, 6), new Edge(3, 2, 8), new Edge(3, 4, 9),
				new Edge(4, 2, 3), new Edge(4, 3, 9)};
		AdjMatrixGraph<String> graph = new AdjMatrixGraph<>(vertices, edges);
		System.out.println(graph.toString());

		int i = graph.insertVertex("F");
		graph.insertEdge(0, i, 20);
		graph.insertEdge(i, 0, 20);

		graph.removeVertex(2);
		graph.removeEdge(2, 3);
		graph.removeEdge(3, 2);
		System.out.println(graph.toString());
	}
}

测试截图:



文章内容有限,这篇就写到这里。欢迎提出建议,谢谢!

猜你喜欢

转载自blog.csdn.net/kobe_jr/article/details/80399192