數據結構系列是我學習做的筆記,會持續更新,源碼分享在github:數據結構,當然你也可以從下面的代碼片中獲取
注:github代碼更新會有延遲,關注不迷路😄
本篇博文簡單介紹鄰接矩陣與其存儲圖的特點,並用java實現
1. 鄰接矩陣(Adjacency Matrix)
教科書上定義:圖的鄰接矩陣是用來表示頂點之間相鄰關係的矩陣。
鄰接矩陣是一個二維數組arcs[ ][ ],數組的行數和列數都與圖的頂點數相同
在沒有權值的時候,若圖中的第a個頂點和第b個頂點之間有連接,則arcs[ a ][ b ] = 1,無連接則爲定義爲0。由此定義可以簡單得出,無向圖的鄰接矩陣沿arc[ i ][ i ]軸對稱,一般採用壓縮存儲。
當圖的邊有權值爲x,若圖中的第a個頂點和第b個頂點之間有連接,則arcs[ a ][ b ] = x,無連接則爲定義爲正無窮,即Integer.MAX_VALUE。
有以下特點:
-
1.用鄰接矩陣存儲圖,所需要的存儲空間只和定點數有關
-
2.容易判斷任意倆頂點是否有邊,容易求出各個頂點上的度
-
3.對於無向圖,鄰接矩陣第i行或i列非0元素個數正好是第i個頂點vi的度
-
4.對於有向圖,第i行非0元素個數爲i個頂點的出度,i列爲入度
2. 圖的實現
2.1 圖的類型: GraphKind.java
圖的類型主要有四種,無向圖,有向圖,無向網(帶權圖),有向網,我們使用枚舉表示:
package code.Graph;
/*
* 羅列圖的四種類型
* */
public enum GraphKind {
UDG, //無向圖----(UnDirected Graph)
DG, //有向圖----(Directed Graph)
UDN, //無向網----(UnDirected Network)
DN; //有向網----(Directed Network)
}
2.2 圖的定義(接口): IGraph.java
package code.Graph;
/*
* 圖的存儲結構除了存儲圖中各個頂點的信息外,還要存儲與頂點相關聯的邊的信息,
* 圖的常見存儲結構有鄰接矩陣,鄰接表,鄰接多重表,十字鏈表
*
* 圖的接口
*
* */
public interface IGraph {
/**創建一個圖*/
public void createGraph();
/**返回圖中的頂點數*/
public int getVexNum();
/**返回圖中的邊數*/
public int getArcNum();
/**
* 給定點的位置v,返回其對應的頂點值,其中, 0 <= v < vexNum
* @param v
* */
public Object getVex(int v) throws Exception;
/**
* 給定頂點的值vex,返回其在圖中的位置,如果圖中不包含此定點,返回-1
* @param vex
*/
public int locateVex(Object vex);
/**
* 返回v的第一個鄰接點,若V沒有鄰接點,則返回-1,其中, 0 <= v < vexNum
* @param v
*/
public int firstAdjVex(int v) throws Exception;
/**
* 返回v相對於w的下一個鄰接點,若w是v的最後一個鄰接點,則返回-1,其中, 0 <= v, w < vexNum
* @param v
* @param w
*/
public int nextAdjVex(int v,int w) throws Exception;
}
2.3 鄰接矩陣存儲圖實現類: MGraph.java
package code.Graph;
import java.util.Scanner;
/**
* @author 大青兒學習筆記
*
* 使用鄰接矩陣表示圖
*
* 注意點:
* 1.無向圖的鄰接矩陣是對稱陣,一般採用壓縮存儲
*
* 2.用鄰接矩陣存儲圖,所需要的存儲空間只和定點數有關
*
* 3.優點:容易判斷任意倆頂點是否有邊,容易求出各個頂點上的度
*
* 4.對於無向圖,鄰接矩陣第i行或i列非0元素個數正好是第i個頂點vi的度
* 對於有向圖,第i行非0元素個數爲i個頂點的出度,i列爲入度
*
* 0.圖的鄰接矩陣----(Adjacency Matrix)是用來表示頂點之間相鄰關係的矩陣
* 圖的鄰接矩陣是一個n階矩陣,n爲頂點個數
* */
public class MGraph implements IGraph{
public final static int INFINITY = Integer.MAX_VALUE;//這裏表示網絡不連通,他們之間的權值也是無窮大
private GraphKind kind; //圖的種類標誌
private int vexNum,arcNum; //圖的當前頂點數和邊數
private Object [] vexs; //頂點數組
private int [][]arcs; //鄰接矩陣
public MGraph(){
this(null,0,0,null,null);
}
public MGraph(GraphKind kind, int vexNum, int arcNum, Object[] vexs, int[][] arcs) {
this.kind = kind;
this.vexNum = vexNum;
this.arcNum = arcNum;
this.vexs = vexs;
this.arcs = arcs;
}
/**
* 創建一個圖
*/
@Override
public void createGraph() {
Scanner sc = new Scanner(System.in);
System.out.println("請輸入圖的類型:");
GraphKind kind = GraphKind.valueOf(sc.next());
switch (kind){
case UDG:
createUDG();
return;
case DG:
createDG();
return;
case UDN:
createUDN();
return;
case DN:
createDN();
return;
}
}
//創建 圖,網 的工具方法
private void createNetworkOrGraphUnit(){
Scanner sc = new Scanner(System.in);
System.out.println("請分別輸入圖的頂點數,圖的邊數");
vexNum = sc.nextInt();//圖的頂點數
arcNum = sc.nextInt();//圖的邊數
vexs = new Object[vexNum];//存儲頂點的數組
System.out.println("請分別輸入圖的各個頂點:");
for (int v = 0;v < vexNum;v++){//構造頂點向量
vexs[v] = sc.next();
}
arcs = new int[vexNum][vexNum];//創建鄰接矩陣
//初始化鄰接矩陣
for (int v = 0;v < vexNum;v++){
for (int u = 0;u < vexNum;u++){
arcs[v][u] = INFINITY;//設初始的全部的權值爲最大
}
}
}
/*
* 創建無向圖
* 書上的方法
*
* 我本人更傾向於傳參式的方法
* */
private void createUDG(){
Scanner sc = new Scanner(System.in);
createNetworkOrGraphUnit();
System.out.println("請輸入各個邊的兩個頂點及其權值:");
for(int k = 0; k <arcNum;k++){//循環 圖的邊數 次
int v = locateVex(sc.next());
int u = locateVex(sc.next());
arcs[v][u] = arcs[u][v] = 1;
}
}
/*
* 創建有向圖
* 書上的方法
*
* 我本人更傾向於傳參式的方法
* */
private void createDG(){
Scanner sc = new Scanner(System.in);
createNetworkOrGraphUnit();
System.out.println("請輸入各個邊的兩個頂點及其權值:");
for(int k = 0; k <arcNum;k++){//循環 圖的邊數 次
int v = locateVex(sc.next());
int u = locateVex(sc.next());
arcs[v][u]= 1;
}
}
/*
* 創建無向網
* 書上的方法
*
* 我本人更傾向於傳參式的方法
* */
private void createUDN(){
Scanner sc = new Scanner(System.in);
createNetworkOrGraphUnit();
System.out.println("請輸入各個邊的兩個頂點及其權值:");
for(int k = 0; k <arcNum;k++){//循環 圖的邊數 次
int v = locateVex(sc.next());
int u = locateVex(sc.next());
arcs[v][u] = arcs[u][v] = sc.nextInt();
}
}
/*
* 創建有向網
* 書上的方法
*
* 我本人更傾向於傳參式的方法
* */
private void createDN(){
Scanner sc = new Scanner(System.in);
createNetworkOrGraphUnit();
System.out.println("請輸入各個邊的兩個頂點及其權值:");
for(int k = 0; k <arcNum;k++){//循環 圖的邊數 次
int v = locateVex(sc.next());
int u = locateVex(sc.next());
arcs[v][u]= sc.nextInt();
}
}
/**
* 返回圖中的頂點數
*/
@Override
public int getVexNum() {
return vexNum;
}
/**
* 返回圖中的邊數
*/
@Override
public int getArcNum() {
return arcNum;
}
/**
* 給定點的位置v,返回其對應的頂點值,其中, 0 <= v < vexNum
*
* @param v
*/
@Override
public Object getVex(int v) throws Exception {
if (v < 0 && v >= vexNum){
throw new Exception("第"+v+"個頂點不存在");
}
return vexs[v];
}
/**
* 給定頂點的值vex,返回其在圖中的位置,如果圖中不包含此定點,返回-1
*
* @param vex
*/
@Override
public int locateVex(Object vex) {
for (int v = 0;v < vexNum;v++){
if (vexs[v].equals(vex)){
return v;
}
}
return -1;
}
/**
* 返回v的第一個鄰接點,若V沒有鄰接點,則返回-1,其中, 0 <= v < vexNum
*
* @param v
*/
@Override
public int firstAdjVex(int v) throws Exception {
if (v < 0 && v >= vexNum){
throw new Exception("第" + v + "個頂點不存在!");
}
for (int j = 0;j<vexNum;j++){
if (arcs[v][j] != 0 && arcs[v][j] < INFINITY){
return j;
}
}
return -1;
}
/**
* 返回v相對於w的下一個鄰接點,若w是v的最後一個鄰接點,則返回-1,其中, 0 <= v, w < vexNum
*
* @param v
* @param w
*/
@Override
public int nextAdjVex(int v, int w) throws Exception {
if (v < 0 && v >= vexNum){
throw new Exception("第" + v + "個頂點不存在!");
}
if (w >= vexNum){
throw new Exception("相對頂點不存在!");
}
for (int j = w + 1;j < vexNum;j++){
if (arcs[v][j] != 0 && arcs[v][j] <INFINITY){
return j;
}
}
return -1;
}
public GraphKind getKind() {
return kind;
}
public Object[] getVexs() {
return vexs;
}
public int[][] getArcs() {
return arcs;
}
}