图的基本概念表示方法以及两种搜索方式——深度优先遍历和广度优先遍历

原先的知识没好好学,导致现在很多都忘了,或者说以前就没弄懂过。现在重新看一遍,收获良多,不管怎样先写这篇基础的,当做笔记。

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

有跟多条边的图我们称为稠密图,很少条边的我们称为稀疏图。

有向图和无向图:

无向图:顶点之间的边是没有方向的,也就是两个方向互通的。比如顶点 Vi 和顶点 Vj 之间的边,用(Vi,Vj)表示。

有向图:顶点间的边是有方向的,称为有向边,也成为 弧(Arc)。比如顶点Vi指向顶点Vj 的弧,用有序的偶:

表示,Vi称为弧尾,Vj 称为弧头。


如果无向图中任意两个顶点之间都存在边,则称该图是 无向完全图。n 个顶点的无向图有 (n - 1)*n / 2条边。

与图的边或者弧有关的数叫做权(权值,Weight),带权的图通常称为网。


连通图:如果顶点Vi 到Vj 有路径,则称Vi 和Vj 是连通的。如果对于图中的任意两个顶点 Vi 和 Vj 是连通的则称该图为连通图。

无向图中的极大连通子图称为连通分量。


度:一个顶点所连接的边的数目,是对无向图而言的

入度和出度:一个顶点指向其他顶点的弧的数目称为出度,其他顶点指向该顶点的弧的数目称为入度。


图的存储结构:邻接矩阵、邻接表、十字链表临街多重表等。


邻接矩阵的存储方式:

用一个一维的数组来存储存储图中的顶点信息,用一个二维的数组来存储图中边的或者弧的信息。

邻接矩阵简单的定义图的结构:

public class graph {
	
	public char[] vexs;		//顶点
	public int[][] arc;		//邻接矩阵
	int numVexs;			//顶点数
	int numEdges;			//边数

	public graph(int nVexs,int nEdg){
		this.numVexs = nVexs;
		this.numEdges = nEdg;
	}
}
我们先用一位数组 vexs 存储 n 个顶点的信息,然后一个 n*n 的二维数组 arc 存储边或者弧。arc[ i ][ j ] 等于 1 表示顶点 Vi 连通 Vj (如果是无向图那么 arc[ j ][ i ] 也为 1),等于 0 则说明这两个顶点之间没有边或者弧 。 无向图的邻接矩阵是对称的。

邻接矩阵是一种不错的存储图的数据结构,但是当顶点相对较多而边相对较少的时候,二维的邻接矩阵显得有点浪费。
所以这里引入邻接表:用一个一维数组存储顶点,顶点的邻接点构成一个线性表。我们把这种数组和线性表相组合的存储方法称为邻接表

十字链表、多重链表就不说了。

接下来重点说图的遍历:从图中的某一顶点出发访问图中的其余顶点,且每个顶点仅访问一次,这 一过程称为图的遍历。
深度优先遍历(Depth First Search):也称为深度优先搜索,DFS,深度优先搜索其实是一个递归过程,一条路先走到底有,点像二叉树的先序遍历。我们需要一个数组 visited 来标记已经访问过的顶点 ,visited[ i ] = false 表示未访问,true 表示已经访问过。
具体实现:
package com.hunter.Graph;
/**
 * 深度优先搜索
 * @author luzi
 *
 */
public class graphDFS {
	
	public void DFSraverse(graph gh){
		
		//初始化visited数组,用  false 以表示该顶点是否已经访问过
		boolean[] visited = new boolean[gh.numVexs];
		for(int i = 0; i < gh.numVexs; i++){
			visited[i] = false;
		}
		System.out.println();
		for(int i = 0; i < gh.numVexs; i++){
			if(!visited[i])
				DFS(gh,i,visited);
		}
	}

	private void DFS(graph gh, int k,boolean[] vi) {
		// TODO Auto-generated method stub
	
		vi[k] = true;
		System.out.println("正访问的结点是 : " + gh.vexs[k]);
		
		for(int i = 0; i < gh.numVexs; i++){
			if(gh.arc[k][i] == 1 && !vi[i])
				DFS(gh,i,vi);
		}
	}	
}



广(宽)度优先遍历(Breadth First Search):又称为广度优先搜索,简称BFS 。
遍历的过程是先从顶点 V0 出发,遍历与 V0 直接连接而又未访问过的顶点V1 、V2 、V3 等,再访问 与 V1直接连接且未访问过的顶点。同样用一个数组来标记一个顶点是否已经访问过,用一个队列来存储待访问的顶点。
具体实现:
package com.hunter.Graph;

import java.util.LinkedList;
import java.util.Queue;

public class graphBFS {
	
	public void BFSraverse(graph gh){
		
		Queue que =  new LinkedList();
		boolean[] visited = new boolean[gh.numVexs];
		for(int i = 0; i < gh.numVexs; i++)
			visited[i] = false;
		
		for(int i = 0; i < gh.numVexs; i++){
			
			if(!visited[i]){
				
				visited[i] = true;
				System.out.println("正在访问的节点是 :" + gh.vexs[i]);
				que.add(i);
				
				while(!que.isEmpty()){
					que.remove();
					
					for(int j = 0; j 

深度优先和宽度优先的时间复杂度是一样的,但是访问的顺序不一样。
深度优先类似二叉树的先序遍历而宽度优先类似二叉树的层序遍历。

最后再看一个例子,有如下的无向图:
图的基本概念表示方法以及两种搜索方式——深度优先遍历和广度优先遍历_第1张图片


初始化该图,调用深度优先和宽度优先遍历:
package com.hunter.Graph;

public class Demo {
	
	public static void main(String args[]){

		int numVexs = 7;
		int numEdges = 6;
		graph gh = new graph(numVexs,numEdges);
		gh.vexs = new char[numVexs];
		gh.vexs[0] = 'A';
		gh.vexs[1] = 'B';
		gh.vexs[2] = 'C';
		gh.vexs[3] = 'D';
		gh.vexs[4] = 'E';
		gh.vexs[5] = 'F';
		gh.vexs[6] = 'G';
		
		gh.arc = new int[numVexs][numVexs];
		gh.arc[0][1] = 1;
		gh.arc[1][0] = 1;
		gh.arc[0][4] = 1;
		gh.arc[4][0] = 1;
		gh.arc[1][2] = 1;
		gh.arc[2][1] = 1;
		gh.arc[2][3] = 1;
		gh.arc[3][2] = 1;
		gh.arc[3][4] = 1;
		gh.arc[4][3] = 1;
		gh.arc[2][5] = 1;
		gh.arc[5][2] = 1;
		gh.arc[1][5] = 1;
		gh.arc[5][1] = 1;
		gh.arc[6][3] = 1;
		gh.arc[3][6] = 1;			
		
		System.out.println("********************广度优先搜索********************");
		graphDFS ghDFS = new graphDFS();
		ghDFS.DFSraverse(gh);
		System.out.println("********************广度优先搜索********************");
		graphBFS ghBFS = new graphBFS();
		ghBFS.BFSraverse(gh);
	}
}



实际运行结果:

图的基本概念表示方法以及两种搜索方式——深度优先遍历和广度优先遍历_第2张图片




你可能感兴趣的:(数据结构)