測試類
public class TestGraphAdjMatrix { //測試類
public static <E> void main(String[] args){
Scanner read=new Scanner(System.in);
GraphAdjMatrix g=new GraphAdjMatrix();
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();
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 GraphAdjMatrix<E>{
Vex[] vexs;//頂點表
int [][] edges;//鄰接矩陣 存儲邊的關係
int vexnum=0;//實際最大點數
int vex=0;//最大的點數 比實際值大5
int edgenum=0;//需要添加的邊數
int edge=0;//最大邊數 定點數的平方
int isdirection=0;//是否爲有向圖
final int MAXVALUE=10000;//模擬無窮大值
boolean[] visited;//標記點是否被遍歷
Scanner st=new Scanner(System.in);
public GraphAdjMatrix(){
System.out.println("請輸入儲存的結點數和邊的條數以及圖的性質:例如 x y z(1無向圖,2有向圖)");
vexnum=st.nextInt();
edgenum=st.nextInt();
isdirection=st.nextInt();
vex=vexnum+5;//創建數組存儲點和邊時預留5的空間方便後續點的添加
vexs=new Vex[vex];
edge=(vexnum+5)*(vexnum+5);
edges=new int[edge][edge];//其中每個元素爲0;
for(int i=0;i<vexnum;i++)//初始化邊
for(int j=0;j<vexnum;j++){
if(i==j)edges[i][j]=0;
else edges[i][j]=this.MAXVALUE;
}
create_Graph(vexs,edges);//構造圖
}
private void create_Graph(Vex[] vexs,int[][] edges) {//創建圖
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 Vex(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();
edges[vi][vj]=w;
if(isdirection==1) edges[vj][vi]=w;//如果是無向圖在對應vj-vi也添加數據
}
}
public void pntGraph(){//輸出圖的信息
System.out.println("圖的頂點表爲:");
for(int i=0;i<vexnum;i++)System.out.print(vexs[i].vname+" ");
System.out.println();
System.out.println("圖的鄰接矩陣爲:");
for(int i=0;i<vexnum;i++){
for(int j=0;j<vexnum;j++){//因爲不同位數輸出寬度不一樣 現在值考慮最多兩位是矩陣對齊
System.out.print(edges[i][j]==this.MAXVALUE?"∞ ":edges[i][j]/10==0?edges[i][j]+" ":edges[i][j]+" ");
}
System.out.println();
}
}
public boolean insertVex() {//插入點
System.out.println("請輸入需要添加的點:");
char v=st.next().charAt(0);
if(vexnum>vex) //邊界判斷
return false;
vexs[vexnum++]=new Vex(v);
for(int i=0;i<vexnum;i++)//初始化與新插入點的邊
edges[i][vexnum-1]=this.MAXVALUE;
for(int j=0;j<vexnum;j++)
edges[vexnum-1][j]=this.MAXVALUE;
edges[vexnum][vexnum]=0;
return true;
}
public boolean deleteVex() {//刪除點
System.out.println("請輸入需要刪除的點:");
char v=st.next().charAt(0);
for(int i=0;i<vexnum;i++){
char ss=vexs[i].vname;
if(ss==v){
for(int j=i;j<vexnum-1;j++){
vexs[j]=vexs[j+1];//刪除點
}
vexs[vexnum-1]=null;//刪除與點相關的邊
for(int col=i;col<vexnum-1;col++){//刪除行
for(int row=0;row<vexnum;row++)
edges[col][row]=edges[col+1][row];
}
for(int row=i;row<vexnum-1;row++){//刪除列
for(int col=0;col<vexnum;col++)
edges[col][row]=edges[col][row+1];
}
vexnum--;
return true;
}
}
return false;
}
public boolean insertEdge() {//插入邊
System.out.println("請輸入需要插入的邊:");
int v1=st.nextInt();
int v2=st.nextInt();
int weight=st.nextInt();
if(v1<0||v2<0||v1>=vexnum||v2>=vexnum)//邊界判斷
throw new ArrayIndexOutOfBoundsException();
edges[v1][v2]=weight;
if(isdirection==1)
edges[v2][v1]=weight;
return false;
}
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();
edges[v1][v2]=MAXVALUE;//把權值恢復初始值
if(isdirection==1)//如果是無向圖v2-v1也需要恢復
edges[v2][v1]=MAXVALUE;
return false;
}
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;//標記該點已經被訪問
while(!stack.isEmpty()){//取出棧頂元素並把該點關聯的未被訪問的點繼續壓入棧中直到全部訪問完畢
i=stack.pop();
sb.append(vexs[i].vname+",");//將遍歷的點存在字符串中
for(int j=vexnum-1;j>=0;j--){
if((edges[i][j]!=0&&edges[i][j]!=MAXVALUE)&&!visited[j]){//點未被訪問並且不爲無窮大有具體的數值就是可達
stack.push(j);
visited[j]=true;//標記該點
}
}
}
System.out.println("深度優先搜索結果:");
String s=sb.length()>0?sb.substring(0,sb.length()-1):null;//將串的值賦給String對象輸出
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;//標記已被訪問
while(!queue.isEmpty()){
i=queue.poll();//出隊 並把該點相關的點入隊
sb1.append(vexs[i].vname+",");
for(int p=vexnum-1;p>=0;p--){
if((edges[i][p]!=0&&edges[i][p]!=MAXVALUE)&&!visited[p]){
queue.offer(p);
visited[p]=true;
}
}
}
System.out.println("廣度優先搜索結果:");
String s=sb1.length()>0?sb1.substring(0,sb1.length()-1):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數組
D[t]=0;flag[t]=1;P[t]=-1;//初始化源點的相關數組數值
for(int k=0;k<vexnum;k++){//將源點這一行的矩陣數據存進D數組 並且將所有的點的上一個節點標記爲源點
if(k!=t){
D[k]=edges[t][k];
P[k]=t;
}
}
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已經被訪問
//其餘未被確定爲最小距離的點到達改j點的距離加上j到源點的距離小於該點直接到達源點的距離就修改D的數據 同時把P改爲j
for(int k=0;k<vexnum;k++){
if(flag[k]==0&&(min+edges[j][k])<D[k]){
D[k]=min+edges[j][k];
P[k]=j;
}
}
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[][] gm=edges;
int[] cost=new int[vexnum];//存儲該點到達最近節點的距離
int[] tree=new int[vexnum];//記錄最近的哪個點的標號
int vnum=vexnum;
boolean[] flag=new boolean[vnum];//標記該點是否被訪問 被訪問的我們視爲已經找到了距離該點最近的點
int i,j,k=0,mincost;
for(i=0;i<vnum;i++){//將源點到達各個點的距離賦給數組
cost[i]= gm[p][i];
tree[i]=p;//頂點p到達的頂點爲i
}
tree[p]=-1;//防止進入死循環造成越界異常 將源點的最近的節點的標號設爲1
flag[p]=true;//以p爲第一個樹根節點
for(i=0;i<vnum-1;i++){
mincost=MAXVALUE;
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集合
for(j=0;j<vnum;j++){
if(flag[j]==false&&gm[k][j]<cost[j]){
cost[j]=gm[k][j];
tree[j]=k;
}
}
}
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++){
char sss=vexs[i].vname;
if(sss==v){
return i;
}
}
return -1;
}
public char valueOffCex(int v){//定位指定位置的頂點
if(v<0||v>vexnum)
throw new ArrayIndexOutOfBoundsException();
return vexs[v].vname;
}
public int getEdge(int v1, int v2) {//獲取邊
if(v1<0 || v2<0 || v1>=vexnum || v2>vexnum)
throw new ArrayIndexOutOfBoundsException();
return edges[v1][v2];
}
}
節點數據類
public class Vex {//節點的數據類型
char vname; //節點名稱
public Vex(char ch){
this.vname = ch;
}
}