图的表示方法:
邻接矩阵法(太浪费空间,但确实把所有信息都表示了出来,计算度或者各种操作的话很方便,当然用无向图的对称性、只存放非0结点可以优化)
邻接表(算比较好的一种方法,但是对于有向图计算入度必须遍历,用逆邻接表、十字链表优化)
十字链表(和邻接表太类似)
遍历算法:
图是标准的多对多结构,因此不构成环并且不重复访问vertex需要一个标志数组visited,循环这个保证全部都访问,遍历过程中可以有DFS和BFS
DFS:递归方式,首先访问vertex,循环找邻接节点,找到并且该vertex没有被访问过,将顶点当成参数进入下一层。
BFS:需要堆栈配合,找到自己邻接的顶点如果没有被访问过则访问并将其入栈,然后用stack非空控制循环直到全部访问过。
还有一些计算连通分量个数、度的问题,就是遍历算法的应用。
代码(结构写的有点乱,回头看的时候可以优化下):
package nuaa.ds;
public class Graph {
private String[] vertexes;//邻接矩阵法所
private int[][] vr; //使用的成员变量
private CreateGraph g;
private VertexNode[] vertexNodes;//邻接表所使用的成员变量
/**
* 图的构成实在太多,写个标准无向图熟悉算法
* 顶点ABCD(其中BC不连)彼此互连,EF彼此互连
*/
public Graph(CreateGraph c){
this.g = c;
switch(c){
case AdjacencyMatrix://邻接矩阵
vertexes = new String[]{"A","B","C","D","E","F"};//ABCDEF6个顶点
vr = new int[6][6];
vr[0] = new int[]{0,1,1,1,0,0};//邻接矩阵法表示边,相连值为1,不连值为0
vr[1] = new int[]{1,0,0,1,0,0};//优点是所有信息全部显示出来
vr[2] = new int[]{1,0,0,1,0,0};//这样顶点的度等计算或者对图的所有操作都比较容易
vr[3] = new int[]{1,1,1,0,0,0};//缺点也很明显
vr[4] = new int[]{0,0,0,0,0,1};//存储空间极大的浪费
vr[5] = new int[]{0,0,0,0,1,0};//当然通过使用对称或者只存储非0元素可以优化
break;
case AdjacencyTable://邻接表表示,省了很多存储空间,
//但有时会带来不便,比如求有向图的某个节点的入度
//结点数组的初始化
vertexNodes = new VertexNode[6];
String[] temp = new String[]{"A","B","C","D","E","F"};
for(int i=0;i<vertexNodes.length;i++){
vertexNodes[i] = new VertexNode(temp[i]);
}
//结点数组连接的边结点的初始化
//写de好麻烦,还不如由输入流输进来循环构造。但每次测试重新输入图结构也很烦。
//顶点A的初始化
vertexNodes[0].arcNode = new ArcNode(1);
vertexNodes[0].arcNode.arcNode = new ArcNode(2);
vertexNodes[0].arcNode.arcNode.arcNode = new ArcNode(3);
//顶点B的初始化
vertexNodes[1].arcNode = new ArcNode(0);
vertexNodes[1].arcNode.arcNode = new ArcNode(3);
//顶点C的初始化
vertexNodes[2].arcNode = new ArcNode(0);
vertexNodes[2].arcNode.arcNode = new ArcNode(3);
//顶点D的初始化
vertexNodes[3].arcNode = new ArcNode(0);
vertexNodes[3].arcNode.arcNode = new ArcNode(1);
vertexNodes[3].arcNode.arcNode.arcNode = new ArcNode(2);
//顶点E的初始化
vertexNodes[4].arcNode = new ArcNode(5);
//顶点F的初始化
vertexNodes[5].arcNode = new ArcNode(4);
}
}
//求连通分量个数或者连通生成树也是遍历算法
public void traversalByDFS(){
this.traversalByDFS(this.g);
}
private void traversalByDFS(CreateGraph c){//深度优先算法访问图
boolean[] visited;
if(c==CreateGraph.AdjacencyTable){
visited = new boolean[vertexNodes.length];//java默认都为false
}else{
visited = new boolean[vertexes.length];//java默认都为false
}
switch(c){
case AdjacencyMatrix://图是用邻接表表示的深度优先遍历算法
for(int i=0;i<vertexes.length;i++){
if(!visited[i]){
mTraversalByDFS(visited,i);
}
}
break;
case AdjacencyTable:
for(int i=0;i<vertexNodes.length;i++){
if(!visited[i]){
tTraversalByDFS(visited,i);
}
}
}
}
private void mTraversalByDFS(boolean[] visited,int index){
visit(vertexes[index]);//和二叉树一样的问题,访问图结点可能会做些其他操作
//还是从外部当成函子传入进来比较合适
//对应的vertexes数组也要改成Object[],这样的封装更好
visited[index] = true;
for(int i=0;i<vr[index].length;i++){//循环找到下一个结点
if(vr[index][i]==1&&!visited[i]){//有连接的未访问的结点
mTraversalByDFS(visited,i);//访问该结点
}
}
}
private void tTraversalByDFS(boolean[] visited,int index){
visit(vertexNodes[index]);
visited[index] = true;
ArcNode p = vertexNodes[index].arcNode;
while(null!=p){
if(!visited[p.serialNumber]){
tTraversalByDFS(visited,p.serialNumber);
}
p = p.arcNode;
}
}
public void traversalByBFS(){
this.traversalByBFS(g);
}
private void traversalByBFS(CreateGraph c){
boolean[] visited;
if(c==CreateGraph.AdjacencyTable){
visited = new boolean[vertexNodes.length];//java默认都为false
}else{
visited = new boolean[vertexes.length];//java默认都为false
}
Stack<VertexNode> stack = new Stack<VertexNode>();
switch(c){
case AdjacencyMatrix://图是用邻接表表示的深度优先遍历算法
for(int i=0;i<vertexes.length;i++){
if(!visited[i]){
mTraversalByBFS(visited,i);
}
}
break;
case AdjacencyTable:
for(int i=0;i<vertexNodes.length;i++){
if(!visited[i]){
tTraversalByBFS(stack,visited,i);
}
}
}
}
private void tTraversalByBFS(Stack<VertexNode> stack,
boolean[] visited,int index){
stack.push(vertexNodes[index]);
visit(vertexNodes[index]);
visited[index] = true;
ArcNode p ;
while(!stack.isEmpty()){
p = stack.pop().arcNode;
while(p!=null){
if(!visited[p.serialNumber]){
stack.push(vertexNodes[p.serialNumber]);
visit(vertexNodes[p.serialNumber]);
visited[p.serialNumber] = true;
}
p = p.arcNode;
}
}
}
private void mTraversalByBFS(boolean[] visited,int index){
Stack<Integer> stack = new Stack<Integer>();
stack.push(index);
visit(vertexes[index]);
visited[index] = true;
while(!stack.isEmpty()){
for(int i=0;i<vr[index].length;i++){
if(vr[index][i]==1&&!visited[i]){
stack.push(i);
visit(vertexes[i]);
visited[i] = true;
}
}
index = stack.pop();
}
}
private void visit(String vertex){
System.out.print(vertex+" ");
}
private void visit(VertexNode node){
System.out.println(node.vertex);
}
class VertexNode{
String vertex;
ArcNode arcNode;
public VertexNode(String vertex){
this.vertex = vertex;
}
}
class ArcNode{
int serialNumber;//存放边连着的节点在vertexNode数组里面的位置
ArcNode arcNode;
public ArcNode(int serialNumber){
this.serialNumber = serialNumber;
}
}
}
一些盲点: 十字链表表示法太相似没有写,邻接矩阵法用广义表的方式对空间进行优化,然后计算连通分量、度可以用来复习下遍历算法。