1. 程式人生 > >3.1圖的兩種表示-鄰接矩陣和鄰接表

3.1圖的兩種表示-鄰接矩陣和鄰接表

圖是一種由節點和鏈(連線節點,有向或者無向)組成的資料結構。可以用鄰接矩陣和鄰接表兩種資料結構來表示。

P1:鄰接矩陣的資料結構由儲存節點的順序表和表示鄰接關係的0-1矩陣組成。要實現的功能包括頂點和邊的增加和刪除、邊的權重獲取和更新、深度優先和廣度優先搜尋。

圖的介面定義如下:

public interface GGraph<T> {

	public static final int MAX_WEIGHT=99999;
	int vertexCount();//當前圖的頂點數
	T get(int i);
	int getWeight(int i,int j);
	int insertVertex(T x);
	void insertEdge(int i,int j,int weight);
	void removeVertex(int i);
	void removeEdge(int i,int j);
	int getNextNeighbor(int i,int j);
	void DFSTraverse(int i);//深度優先搜尋
	void BFSTraverse(int i);//廣度優先搜尋
}

因為不同資料結構的同一功能介面的具體實現有時候會依賴於其具體資料結構,所以將不依賴的功能和以來的功能介面分開:

用一個抽象圖類去封裝不依賴資料結構的功能函式,而依賴具體資料結構的功能函式在資料結構內部實現:

public abstract class AbstractGraph<T> implements GGraph<T> {

	private static final int MAX_WEIGHT = 99999;

	public abstract int vertexCount();
	public abstract T get(int i);
	public abstract int getNextNeighbor(int i,int j);//返回與與頂點i相連的在頂點j後面的頂點序號
	public abstract int getWeight(int i,int j);
    
    public void DFSTraverse(int i)
    {...}

    public void BFSTraverse(int i)
    {...}

    //...最短路徑、最小生成樹演算法等
}

 

定義加權(有向或無向,無向邊在圖資料結構中儲存兩次)邊的資料結構如下:

public class Edge implements Comparable<Edge> {

	public int start,dest,weight;
	
	public Edge(int start,int dest,int weight)
	{
		this.start=start;
		this.dest=dest;
		this.weight=weight;
	}
	
	public String toString()
	{
		return "("+this.start+","+this.dest+","+this.weight+")";
	}
	
	@Override
	public int compareTo(Edge e) {
		// TODO Auto-generated method stub
		int num=this.start-e.start;
		return num==0?(this.dest-e.dest):num;
	}
}

鄰接矩陣資料結構定義和介面的程式碼如下:

public class AdjMatixGraph<T>{
	//鄰接圖的儲存結構由一個儲存節點的順序表和儲存鄰接矩陣的二維陣列組成
	SeqList<T> list;
	int[][] adjMatrix;
	
	static final int MAX_WEIGHT=99999;
	
	//建構函式初始化,構造空圖,指定頂點線性表容量
	public AdjMatixGraph(int size)
	{
		list=new SeqList<T>(size);
		for(int i=0;i<list.length();i++)
			for(int j=0;j<list.length();j++)
				if(i==j)
					adjMatrix[i][i]=0;
				else
					adjMatrix[i][j]=MAX_WEIGHT;
	}
	
	//建構函式用頂點陣列、邊陣列初始化圖的線性表和鄰接矩陣
	public AdjMatixGraph(T[] vertices,Edge[] edges)
	{
		this(vertices.length);
		for(int i=0;i<vertices.length;i++)
			this.insertVertex(vertices[i]);
		for(int j=0;j<edges.length;j++)
			this.insertEdge(edges[j]);
	}
	
	public int vertexCount()
	{
		return this.list.length();
	}
	
	public T get(int i)
	{
		return this.list.get(i);
	}
	
	public int getWeight(int i,int j)
	{
		return this.adjMatrix[i][j];
	}
	
	//返回圖的頂點集合和鄰接矩陣描述字串
	public String toString()
	{
		String str="頂點集合:"+this.list.toString()+"\n";
		int n=this.vertexCount();
		for(int i=0;i<n;i++)
			{
			for(int j=0;j<n;j++)
				str+=this.adjMatrix[i][j];
			str+="\n";
			}
		return str;
	}
	
	//插入頂點,返回頂點的序號,在list尾部新增節點的同時要在鄰接關係陣列中新增一行一列
	public int insertVertex(T x)
	{
		this.list.append(x);
		int temp[][]=this.adjMatrix;
		int i=0,j=0;
		int n=this.adjMatrix.length;
		if(this.vertexCount()>n)
			{
			this.adjMatrix=new int[n+1][n+1];
			for(;i<temp.length;i++)
				{
				for(;j<temp.length;j++)
					this.adjMatrix[i][j]=temp[i][j];
				this.adjMatrix[i][j]=MAX_WEIGHT;
				}
			this.adjMatrix[i][j]=MAX_WEIGHT;
			}
		return i;
	}
	
	//插入邊
	public void insertEdge(Edge edge)
	{
		this.insertEdge(edge.start,edge.dest,edge.weight);
	}
	
	//插入邊操作過載,插入一條邊不需要對節點進行改動,只需要修改鄰接矩陣,如果該邊已經存在則不需要修改
	public void insertEdge(int start,int dest,int weight)
	{
		int n=this.vertexCount();
		if(start>=0 && start<n&& dest>=0 && dest<n && this.adjMatrix[start][dest]==MAX_WEIGHT)
			this.adjMatrix[start][dest]=weight;
	}
	
	//圖的刪除頂點操作,線上性表中刪除相應序號的頂點,再將鄰接矩陣第i列i行刪掉,並將後面的資料前移
	//序列號i從0開始,for迴圈計數器k從0到i遍歷0到i-1
	public void removeVertex(int i)
	{
		int n=this.vertexCount();
		this.list.remove(i);
		for(int k=0;k<i;k++)
			for(int j=i;j<this.vertexCount();j++)
				this.adjMatrix[k][j]=this.adjMatrix[k][j+1];
		for(int k=i;k<this.vertexCount();k++)
			for(int j=0;j<i;j++)
				this.adjMatrix[k][j]=this.adjMatrix[k+1][j];
		for(int j=i;j<n;j++)
			for(int k=i;k<n;k++)
				this.adjMatrix[j][k]=this.adjMatrix[j+1][k+1];
	}

測試程式碼如下:

public static void main(String args[])
	{
		String[] vertices={"A","B","C","D","E"};
		Edge edges[]={new Edge(0,1,5),new Edge(0,3,2),new Edge(1,0,5),
				new Edge(1,3,6),new Edge(1,2,7),new Edge(2,1,7),new Edge(2,3,8),
				new Edge(2,4,3),new Edge(4,2,3),new Edge(4,3,9),new Edge(3,0,2),new Edge(3,4,9)};
		AdjMatixGraph<String> g=new AdjMatixGraph<String>(vertices,edges);
		System.out.print(g.toString());
		
	}

P2:圖的鄰接表是一條儲存各頂點的順序表組成,而頂點的資料結構除了包含頂點資料項,還包含指向“與當前頂點的鄰接邊”的引用。

public class Vertex<T> {
	public T data;
	public SortedSinglyLinkedList<Edge> adjlink;
	public Vertex(T data)
	{
		this.data=data;
		this.adjlink=new SortedSinglyLinkedList<Edge>();
	}

	public String toString()
	{
		return "\n"+this.data.toString()+":"+this.adjlink.toString();
	}
}

圖的鄰接表資料結構如下:

public class AdjListGraph<T> {

	static final int MAX_WEIGHT=99999;
	protected SeqList<Vertex<T>> vertexlist;
	
	public AdjListGraph(int size)
	{
		size=10>size?10:size;
		this.vertexlist=new SeqList<Vertex<T>>(size);
	}
	
	public AdjListGraph(T[] vertices,Edge[] edge)
	{
		this(vertices.length*2);
		for(int i=0;i<vertices.length;i++)
			insertVertex(vertices[i]);
		for(int i=0;i<edge.length;i++)
			insertEdge(edge[i]);
	}
	
	
	public int insertVertex(T x)
	{
		Vertex<T> v=new Vertex<T>(x);
		this.vertexlist.append(v);
		return this.VertexCount()-1;
	}
	
	//插入一條邊,首先找到該頂點的排序單鏈表,再在排序單鏈表中逐個比較找到插入位置,如果找到相同邊(起點、中點、權重都相同)則不執行插入操作
	public void insertEdge(int i,int j,int weight)
	{
		Edge e=new Edge(i,j,weight);
		SortedSinglyLinkedList<Edge> ssll=this.vertexlist.get(i).adjlink; 
		Node<Edge> front=ssll.head,p=front.next;
		while(p!=null&&p.data.compareTo(e)<0)
		{
			front=p;
			p=p.next;
		}
		if(p!=null && p.data.compareTo(e)==0)
			return;
		Node<Edge> node=new Node<Edge>(e,p);
		front.next=node;
		
		
	}
	
	public void insertEdge(Edge e)
	{
		insertEdge(e.start,e.dest,e.weight);
	}
	
	public void removeEdge(int i,int j)
	{
		int n=this.VertexCount();
		if(i>=0 && i<n && j>=0 && j<n && i!=j)
		{
			Node<Edge> front=this.vertexlist.get(i).adjlink.head;
			Node<Edge> p=front.next;
			while(p!=null && p.data.compareTo(new Edge(i,j,1))!=0)
			{
				front=p;
				p=p.next;
			}
			front.next=p.next;
			front=this.vertexlist.get(j).adjlink.head;
			p=front.next;
			while(p!=null && p.data.compareTo(new Edge(j,i,1))!=0)
			{
				front=p;
				p=p.next;
			}
			front.next=p.next;
		}
	}
	
	public void removeEdge(Edge e)
	{
		removeEdge(e.start,e.dest);
	}
	
	public void removeVertex(int i)
	{
		int n=this.VertexCount();
		if(i>=0 && i<n)
		{
			for(int j=0;j<n;j++)
			{
				Node<Edge> front=this.vertexlist.get(j).adjlink.head,p=front.next;
				while(p!=null)
				{
					Edge e=p.data;
					if(e.start==j ||e.dest==j)
						{
						front.next=p.next;
						p=front.next;
						}
					else
					{
						if(e.start>j)
							e.start--;
						if(e.dest>j)
							e.dest--;
						front=p;
						p=p.next;
					}
				}
			}
			this.vertexlist.remove(i);
		}
		else
			return;
	}
	
	public int VertexCount(){
		return this.vertexlist.length();
	}
	
	public T get(int i){
		return this.vertexlist.get(i).data;
	}
	
	//得到頂點序號為i,j的兩點權重,先在順序表中搜索到頂點i,再在對應的邊連結串列中找到edge.dest=j那個節點
	public int getWeight(int i,int j)
	{
		int n=this.VertexCount();
		if(i>=0 && i<n && j>=0 &&j<n)
		{
			if(i==j)
				return 0;
			Vertex<T> vertexTemp=this.vertexlist.get(i);
			Node<Edge> p=vertexTemp.adjlink.head.next;//p指向第i條單鏈表的第一個節點
			while(p!=null)
			{
				if(p.data.dest==j)
					return p.data.weight;
				p=p.next;
			}
			return MAX_WEIGHT;
		}
		throw new IndexOutOfBoundsException("i="+i+",j="+j);
	}
	
	public String toString()
	{
		String str="頂點有:"+this.vertexlist.toString()+"\n";
		for(int i=0;i<this.vertexlist.length();i++)
		{
			str+=this.vertexlist.get(i).adjlink.toString();
			str+="\n";
		}
		return str;
	}

測試程式碼如下:

public static void main(String[] args)
	{
		String[] vertices={"A","B","C","D","E"};
		Edge edges[]={new Edge(0,1,5),new Edge(0,3,2),new Edge(1,0,5),
				new Edge(1,3,6),new Edge(3,1,6),new Edge(1,2,7),new Edge(2,1,7),new Edge(2,3,8),
				new Edge(2,4,3),new Edge(4,2,3),new Edge(4,3,9),new Edge(3,0,2),new Edge(3,4,9)};
		
		AdjListGraph<String> g=new AdjListGraph<String>(vertices,edges);
		System.out.println(g.toString());
		g.removeEdge(1, 2);
		System.out.println(g.toString());
		g.insertEdge(1, 4, 10);
		System.out.println(g.toString());
	}