邻接表实现图

测试类
public class TestALGraph {
public static <E> void main(String[] args){
Scanner read=new Scanner(System.in);
ALGraph g=new ALGraph();
System.out.println("------------------------");
System.out.println("操作选项菜单:");
System.out.println("1.输出图");
System.out.println("2.添加边");
System.out.println("3.删除边");
System.out.println("4.添加顶点");
System.out.println("5.删除顶点");
System.out.println("6.图的遍历");
System.out.println("7.最短路径");
System.out.println("8.最小生成树");
System.out.println("0.退出");
System.out.println("------------------------");
int f=0,C=0,e,A,B;
char s;
do{
System.out.println("请输入操作选项:");
e=read.nextInt();
if(e>8||e<0)
throw new InputMismatchException();//防止非法输入
switch(e){
case 1:{
g.pntGraph();
break;
}
case 2:{
g.insertEdge();
break;
}
case 3:{
g.deleteEdge();
break;
}
case 4:{
g.insertVex();
break;
}
case 5:{
g.deleteVex();
break;
}
case 6:{
System.out.println("输入遍历方式:1 广度优先遍历 2 深度优先遍历");
C=read.nextInt();
if(C==1)
g.DFSTraverse();
else
g.BFSTraverse();
break;
}
case 7:{
g.dijkstra();
break;
}
case 8:{
g.prim();
break;
}

        }   
    }while(e!=0);
    read.close();//测试类中不能关闭输入,否者程序将终止
}

}

顶点类 用数组实现存储顶点
public class VNode<E>{//存储表的顶点
E data;//顶点
int indegree;//顶点的入度数
ENode firstadj;//顶点的邻接边
VNode(E vex){
this.data=vex;
}

}

边类 用链表实现存储顶点的关联边
public class ENode {//每个一个所连链表中的节点
int adjvex;//邻接顶点的序号
int weight;//边的权值
ENode next;
ENode(int adj,int data){//i,j,w
adjvex=adj;//j
weight=data;//w
}
ENode(int adj){//i,j,w
adjvex=adj;//j
}
}

public class ALGraph<E>{
VNode<E>[] vexs;//顶点表
int vexnum=0;//实际最大点数
int vex=0;//最大的点数 比实际值大5
int edgenum=0;//需要添加的边数
int isdirection=0;//是否为有向图
final int MAXVALUE=10000;//模拟无穷大值
boolean[] visited;//标记点是否被遍历
Scanner st=new Scanner(System.in);
public ALGraph(){
System.out.println("请输入储存的结点数和边的条数以及图的性质:例如 x y z(1无向图,2有向图)");
vexnum=st.nextInt();
edgenum=st.nextInt();
isdirection=st.nextInt();
vex=vexnum+5;//创建数组存储点和边时预留5的空间方便后续点的添加
vexs=(VNode<E>[])Array.newInstance(VNode.class, vex);//创建存储节点的数组
create_Graph(vexs);//构造图
}
private void create_Graph(VNode<E>[] vexs) {//创建图
int i,j,k,vi,vj;
char ch;
Scanner in=new Scanner(System.in);
System.out.println("请使用一行连续输入图的"+this.vexnum+"顶点名称字符如AB...:");
char[] vname=in.nextLine().toCharArray();
//初始化顶点表
for(i=0;i<vexnum;i++)vexs[i]=new VNode(vname[i]);
//初始化邻接矩阵
System.out.println("请输入"+edgenum+"边信息,使用顶点的序号输入,如(i,j)为第i顶点到第j顶点的边的权值,输入i j weight,第一个顶点序号为0:");
int w=0;
for(i=0;i<edgenum;i++){
vi=in.nextInt();
vj=in.nextInt();
w=in.nextInt();
ENode vex1=new ENode(vj,w);//创建新插入的边
ENode vex2=new ENode(vi,w);
if(vexs[vi].firstadj==null){//节点没有相邻点
vexs[vi].firstadj=vex1;
if(isdirection==1){//如果是无向图在对应vj-vi也添加数据
vexs[vj].firstadj=vex2;
}
}else{//节点存在相邻点 在距离顶点最近的地方添加
vex1.next=vexs[vi].firstadj;
vexs[vi].firstadj=vex1;
if(isdirection==1){
vex2.next=vexs[vj].firstadj;
vexs[vj].firstadj=vex2;
}
}
}
}
public void pntGraph(){//输出图的信息
System.out.println("图的顶点表为:");
ENode p;
if(vexnum==0){
System.out.println("图未创建!");
return;
}
for(int i=0;i<vexnum;i++){
System.out.print(i+":");//输出顶点名 valueOffCex函数用来获取数组某个标号对应的节点名称 例如A节点在数组的标号为0
p=vexs[i].firstadj;
while(p!=null){
System.out.print(vexs[i].data+"-->["+valueOffCex(p.adjvex)+","+p.weight+"]\t");
p=p.next;
}
System.out.println();
}
}

public boolean insertVex() {//插入点
    System.out.println("请输入需要添加的点:");
    char v=st.next().charAt(0);
    if(vexnum>vex)  
         return false;
    vexs[vexnum++]=new VNode(v);
    return true;
}
public boolean deleteVex() {//删除点
    System.out.println("请输入需要删除的点:");
    char v=st.next().charAt(0);
    for(int i=0;i<vexnum;i++){
        char ss=(char) vexs[i].data;
        if(ss==v){
            for(int j=i;j<vexnum-1;j++){
                vexs[j]=vexs[j+1];
            }
            vexs[vexnum-1]=null;
            vexnum--;
            ENode ren;
            ENode pre;
            //删除与点相关的边
            for(int col=0;col<vexnum;col++){//删除与与顶点v相关的所有边
                if(vexs[col].firstadj==null)
                        continue;
                if(vexs[col].firstadj.adjvex==i){//删除 与顶点直接相连的v的关联边
                    if(vexs[col].firstadj.next==null){
                         vexs[col].firstadj=null;//如果节点直接连接被删除的节点不能直接指向空  否则节点后面链表其他的点都无法访问

                    } 
                    else
                        vexs[col].firstadj=vexs[col].firstadj.next;
                        continue;
                }
                ren=vexs[col].firstadj;//删除顶点链表中与v相关联的边
                while(ren!=null){//删除顶点链表内部与删除顶点的关联边
                    pre=ren;
                    ren=ren.next;
                    if(ren!=null&&ren.adjvex==i){
                        if(ren.next!=null)
                            pre.next=ren.next;
                        else 
                            pre.next=null;
                        break;
                    }
                }
            }
            for(int col=0;col<vexnum;col++){//被删除后面序号的点的序号-1 例如删除4  5就变成4 6变成5.
                     ren=vexs[col].firstadj;        
                     while(ren!=null){
                         if(ren.adjvex>i)
                              ren.adjvex--;
                         ren=ren.next;

                     }  
            }   
            return true;
        }   
    }
    return false;
}
public boolean insertEdge() {//插入边
    System.out.println("请输入需要插入的边:");
    int v1=st.nextInt();
    int v2=st.nextInt();
    if(v1<0||v2<0||v1>=vexnum||v2>=vexnum)
        throw new ArrayIndexOutOfBoundsException();
    ENode vex1=new ENode(v2);
    ENode vex2=new ENode(v1);
    if(vexs[v1].firstadj==null){//节点没有相邻点
         vexs[v1].firstadj=vex1;
         if(isdirection==1){
             vexs[v2].firstadj=vex2;
         }
    }else{//节点存在相邻点 在距离顶点最近的地方添加
        vex1.next=vexs[v1].firstadj;
        vexs[v1].firstadj=vex1;
        if(isdirection==1){
             vex2.next=vexs[v2].firstadj;
             vexs[v2].firstadj=vex2;
         }
    }
    return true;
}
public boolean deleteEdge() {//删除边
    System.out.println("请输入需要删除的边:");
    int v1=st.nextInt();
    int v2=st.nextInt();
    if(v1<0||v2<0||v1>=vexnum||v2>=vexnum)
        throw new ArrayIndexOutOfBoundsException();
    ENode ren=vexs[v1].firstadj;
    ENode pre=null;
    while(ren!=null&&ren.adjvex!=v2){
        pre=ren;
        ren=ren.next;
    }
    if(ren!=null){
        pre.next=ren.next;
    }
    if(isdirection==1){//无向图
        ren=vexs[v2].firstadj;
        while(ren!=null&&ren.adjvex!=v1){
            pre=ren;
            ren=ren.next;
        }
        if(ren!=null){
            pre.next=ren.next;
        }
   }
   return true;
}

public void DFSTraverse() {//深度优先遍历
    System.out.println("请输入遍历的源点名称:");
    char v=st.next().charAt(0);
    int i=indexOfVex(v);
    if(i<0||i>vexnum)
        throw new ArrayIndexOutOfBoundsException();
    visited = new boolean[vexnum];//初始化用来标记的数组
    StringBuilder sb=new StringBuilder();//创建字符串用来储存遍历的点 特点是创建后大小还可以变动  String则不行
    Stack<Integer> stack =new Stack<Integer>();//创建栈用来存储图的点
    stack.push(i);//将源点压栈
    visited[i]=true;//标记该点已经被访问
    ENode ren;
    while(!stack.isEmpty()){//取出栈顶元素并把该点关联的未被访问的点继续压入栈中直到全部访问完毕
        i=stack.pop();
        sb.append(vexs[i].data+"-->");//将遍历的点存在字符串中
        ren=vexs[i].firstadj;
        while(ren!=null){
            if(visited[ren.adjvex]!=true){//点未被访问并且不为无穷大有具体的数值就是可达
                stack.push(ren.adjvex);
               visited[ren.adjvex]=true;
            }   
            ren=ren.next;
        }
    }
    sb.append("^");
    System.out.println("深度优先搜索结果:");
    String s=sb.length()>0?sb.substring(0,sb.length()):null;
    System.out.println(s);
}

public void BFSTraverse() {// 广度优先搜索遍历
 System.out.println("请输入遍历的源点名称:");
 char v=st.next().charAt(0);
 int i=indexOfVex(v);
 if(i<0||i>vexnum)
        throw new ArrayIndexOutOfBoundsException();
 visited = new boolean[vexnum];
 StringBuilder sb1=new StringBuilder();
 Queue<Integer> queue =new LinkedList<Integer>();//创建队列 
 queue.offer(i);//源点入队
 visited[i]=true;//标记已被访问
 ENode ren;
 while(!queue.isEmpty()){
        i=queue.poll();//出队 并把该点相关的点入队
        sb1.append(vexs[i].data+"-->");
        ren=vexs[i].firstadj;
        while(ren!=null){
            if(visited[ren.adjvex]!=true){
                 queue.offer(ren.adjvex);
                 visited[ren.adjvex]=true;
            }
            ren=ren.next;
        }
    }
 sb1.append("^");
 System.out.println("广度优先搜索结果:");
 String s=sb1.length()>0?sb1.substring(0,sb1.length()):null;
 System.out.println(s);
}

public void dijkstra() {//dijkstra算法实现最短路径 
    System.out.println("请输入遍历的源点名称:");
     char v=st.next().charAt(0);
     int t=indexOfVex(v);
     if(t<0||t>vexnum)
            throw new ArrayIndexOutOfBoundsException();
     int[] flag=new int[vexnum];//标记点是否被访问
     int[] D=new int[vexnum];//存储该点到 源点 的最短距离
     int[] P=new int[vexnum];//记录到达该点最短路径上的上一个节点
     //选择vi为源点,进行初始化 数组P 、D和flag组 
     for(int hi=0;hi<vexnum;hi++)//初始化D的数组 防止空指
         D[hi]=MAXVALUE;
     D[t]=0;flag[t]=1;P[t]=-1;//初始化源点的相关数组数值
     ENode ren;
     ren=vexs[t].firstadj;
     while(ren!=null){//将源点数据存进D数组 并且将所有的点的上一个节点标记为源点
                D[ren.adjvex]=ren.weight;
                P[ren.adjvex]=t;
                ren=ren.next;

     }
     for(int n=1;n<vexnum;n++){//除源点还有n-1个节点未访问,所以需要在循环上一步对源点操作的步骤n-1次
       int min=MAXVALUE;
       int j=0;
       for(int k=0;k<vexnum;k++){//假设选择顶点k
           if(flag[k]==0&&D[k]<min){//未被访问的节点,并且是数值D数组里最小的一个
                  min=D[k];
                  j=k;
                }                            
        } 
        flag[j]=1; //标记顶点j已经被访问  
        ren=vexs[j].firstadj;
        while(ren!=null){ //修改通过Vj到达的顶点距离,近修改,反之不改
            //其余未被确定为最小距离的点到达改j点的距离加上j到源点的距离小于该点直接到达源点的距离就修改D的数据 同时把P改为j
           if(flag[ren.adjvex]==0&& ren.weight+min<D[ren.adjvex]){
                             D[ren.adjvex]=ren.weight+min;
                             P[ren.adjvex]=j;
                       }    
            ren=ren.next;       
       }    
       if(j==0){//当所有的点遍历完了还是找不到不为无穷大最近的点时视该点孤立  与其他点路径不可达
          System.out.println("路径不可达!");
          return;
       }
     }  
     System.out.println("请输入遍历的终点:");
     char ss=st.next().charAt(0);
     int r=indexOfVex(ss);//获取该节点在数组的标号  例如A点的标号为0
     pntShortPath(t,r,D,P);//调用最短路径输出函数

}
public void pntShortPath(int v1,int v2,int[] d,int[] p){//输出最短路径
    Stack<Integer> st=new Stack<Integer>();//创建栈 因为访问的时候是从重点的标记反过来查找的  利用栈先进后出的特点把错误顺序反过来正序输出
    int k=v2;
    while(p[k]!=-1){//将数组数据压站
        st.push(p[k]);
        k=p[k];
    }
    System.out.println("顶点"+valueOffCex(v1)+"到顶点"+valueOffCex(v2)+"最短路径长度为:"+d[v2]);
    System.out.print("最短路径为:");
    int w;
    while(!st.isEmpty()){
        w=st.pop();
        System.out.print(valueOffCex(w)+"-->");
    }
    System.out.println(valueOffCex(v2));
}
public void prim(){//prim算法实现最小生成树
    System.out.println("请输入遍历的源点名称:");
     char v=st.next().charAt(0);
     int p=indexOfVex(v);
     if(p<0||p>vexnum)
            throw new ArrayIndexOutOfBoundsException();
    int[] cost=new int[vexnum];//存储该点到达最近节点的距离
    for(int hi=0;hi<vexnum;hi++)//初始化数组 防止空指
          cost[hi]=MAXVALUE;
    int[] tree=new int[vexnum];//记录最近的哪个点的标号
    int vnum=vexnum;
    boolean[] flag=new boolean[vnum];//标记该点是否被访问  被访问的我们视为已经找到了距离该点最近的点
    int i,j,k=0,mincost;
    ENode ren;
    ren=vexs[p].firstadj;
    while(ren!=null){//将源点到达各个点的距离赋给数组
                cost[ren.adjvex]=ren.weight;
                tree[ren.adjvex]=p;//顶点0到达的顶点为i
                ren=ren.next;

    }
    tree[p]=-1;//防止进入死循环造成越界异常 将源点的最近的节点的标号设为1
    flag[p]=true;//以p为第一个树根节点 
    for(i=1;i<vnum;i++){
        mincost=MAXVALUE;
        k=0;
        for(j=0;j<vnum;j++){
            if(flag[j]==false && cost[j]<mincost){
                mincost=cost[j];
                k=j;//k表示到邻接顶点k到边权小
            }
        }
        flag[k]=true;//表示第k顶点已经加入U集合
        ren=vexs[k].firstadj;   
        while(ren!=null){
            if(flag[ren.adjvex]==false &&ren.weight<cost[ren.adjvex]){
                             cost[ren.adjvex]=ren.weight;
                             tree[ren.adjvex]=k;
                       }    
            ren=ren.next;       
         }  

    }
    pntPrimTree(tree,cost,p);
    System.out.println("^");
}
public void pntPrimTree(int[] tree,int[] cost,int begin){//输出最小生成树
    int N=tree.length;
    for(int i=0;i<N;i++){
        if(tree[i]==begin){
            System.out.print("("+valueOffCex(begin)+","+valueOffCex(i)+")["+cost[i]+"] --> ");
            pntPrimTree(tree,cost,i);
        }
    }
}

public int getNumOfVertex() {//返回节点数
    return vexnum;
}
public int indexOfVex(char v) {//定位点的位置
    for(int i=0;i<vexnum;i++){
        if(vexs[i].data.equals(v))
            return i;
    }
    return -1;
}
public char valueOffCex(int v){//定位指定位置的顶点
    if(v<0||v>vexnum)
        return (Character) null;
    return (char) vexs[v].data;
}
public int getEdge(int v1, int v2) {//获取边的权值
    if(v1<0 || v2<0 || v1>=vexnum || v2>vexnum)
        throw new ArrayIndexOutOfBoundsException();
    ENode ren=vexs[v1].firstadj;
    while(ren!=null){
        if(ren.adjvex==v2){
            return ren.weight;
        }
        ren=ren.next;
    }
    return 0;
}

}
小编整理了一些java进阶学习资料和面试题,需要资料的请加JAVA高阶学习Q群:701136382 这是小编创建的java高阶学习交流群,加群一起交流学习深造。群里也有小编整理的2019年最新最全的java高阶学习资料!

猜你喜欢

转载自blog.51cto.com/14180251/2344520