1. 程式人生 > >Java實現無向圖鄰接表

Java實現無向圖鄰接表

這個實現只考慮實現無向圖鄰接表的功能,底層使用HashMap

提供瞭如下的API

1. addEdge,新增一條邊

2. removeEdge,刪除某條邊

3. containsEdge,是否包含某條邊

4. show,列印展示整個圖

5. edgeNum,返回邊的數目

6. getAllPoints,獲取所有點的集合

7. getAnotherSidePoints,獲取邊另一端的點

8. containsPoint,是否包含某個點

9. addPoint,新增一個點

10. removePoint,刪除某個點

11. pointNum,返回圖內點的總數

12. breadhFirstSearch,廣度優先遍歷

13. depthFirstSearch,深度優先遍歷

14. isConnected,圖是否是連通的

15. toString,重寫

16. minSpanningTree,返回圖的最小生成樹

異常處理:

1. ElementNotFoundException

2. EmptyCollectionException

3. NonComparableElementException

實體類:

1. Side

輔助類:

1. ArrayHeap(陣列實現的二叉最小堆)

package graph;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;

/**
 * 
 * @author Administrator
 * 無向圖鄰接表
 * @param <K> 頂點的Label
 * @param <V> 邊的權重
 */

public class NearTable<K,V> {
	
	Map<K,Map<K,V>> table = new HashMap<K,Map<K,V>>();
	int edgeNum = 0;
	
	//是否是空圖
	public boolean isEmpty(){
		return table.isEmpty();		
	}
	
	/*
	 * 針對無向圖
	 */
	public boolean containsEdge(K p1,K p2){
		if(!table.containsKey(p1)){
			return false;
		}
		if(!table.containsKey(p2)){
			return false;
		}
		if(!table.get(p1).containsKey(p2)){
			return false;
		}
		if(!table.get(p2).containsKey(p1)){
			return false;
		}
		return true;
	}
	
	public boolean containsEdge(K p1,K p2,V weight){
		if(this.containsEdge(p1, p2)){
			V weight1 = table.get(p1).get(p2);
			V weight2 = table.get(p2).get(p1);
			if(weight1 == weight && weight2 == weight){
				return true;
			}
			if(weight1.equals(weight) && weight2.equals(weight)){
				return true;
			}
		}
		return false;
	}
	
	public boolean addEdge(K p1,K p2,V weight){
		if(this.containsEdge(p1, p2)){
			return false;
		}
		if(!table.containsKey(p1)){
			table.put(p1, new HashMap<K,V>());
		}
		if(!table.containsKey(p2)){
			table.put(p2, new HashMap<K,V>());
		}
		table.get(p1).put(p2, weight);
		table.get(p2).put(p1, weight);
		this.edgeNum++;
		return true;
	}
	
	public boolean removeEdge(K p1,K p2) throws EmptyCollectionException, ElementNotFoundException{
		if(this.isEmpty()){
			return false;
		}
		if(!this.containsEdge(p1, p2)){
			return false;
		}
		table.get(p1).remove(p2);
		table.get(p2).remove(p1);
		this.edgeNum--;
		return true;
	}
	
	public void show(){
		Set<Entry<K, Map<K, V>>> set = table.entrySet();
		for(Entry<K, Map<K, V>> e : set){
			Set<Entry<K, V>> temp = e.getValue().entrySet();
			if(temp.size() > 0){
				System.out.print(e.getKey() + " -> ");
				for(Entry<K, V> e1 : temp){
					System.out.print(e1.getKey() + "(" + e1.getValue() + ") ");
				}
				System.out.println();
			}
		}
	}
	
	public int edgeNum(){
		return edgeNum;	
	}
	
	public Set<K> getAllPoints(){
		return table.keySet();
	}
	
	public Set<K> getAnotherSidePoints(K point){
		//要做非空判斷
		//continue...
		if(!table.containsKey(point)){
			try {
				throw new ElementNotFoundException(this.getClass().getName() + ", Point:" + point.toString());
			} catch (ElementNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return table.get(point).keySet();
	}
	
	public boolean containsPoint(K point){
		return table.containsKey(point);		
	}
	
	public boolean addPoint(K point){
		if(this.containsPoint(point)){
			return false;	
		}
		table.put(point, new HashMap<K,V>());
		return true;
	}
	
	@SuppressWarnings("unchecked")
	public boolean removePoint(K point){
		if(!this.containsPoint(point)){
			return false;
		}
		Set<K> pointsSet = this.getAnotherSidePoints(point);
		Object [] pointsArray = pointsSet.toArray();
		for(int i=pointsArray.length-1;i>=0;i--){
			try {
				this.removeEdge(point, (K)pointsArray[i]);
			} catch (EmptyCollectionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ElementNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		table.remove(point);
		return true;	
	}
	
	public int pointNum(){
		return table.size();
	}
	
	public List<K> breadhFirstSearch(K startPoint){
		if(!this.containsPoint(startPoint)){
			try {
				throw new ElementNotFoundException(this.getClass().getName() + ", Point:" + startPoint.toString());
			} catch (ElementNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		List<K> resultList = new ArrayList<K>(this.pointNum());
		Queue<K> queue = new ArrayDeque<K>();
		queue.add(startPoint);
		while(!queue.isEmpty()){
			K current = queue.remove();
			if(!resultList.contains(current)){
				resultList.add(current);
				queue.addAll(this.getAnotherSidePoints(current));
			}
		}
		return resultList;		
	}
	
	public List<K> depthFirstSearch(K parent,List<K> result){
		if(result == null){
			result = new ArrayList<K>();
		}	
		result.add(parent);
		for(K key : this.getAnotherSidePoints(parent)){
			if(!result.contains(key)){
				this.depthFirstSearch(key, result);
			}
		}
		return result;	
	}
	
	/*
	 * 是否是連通的
	 */
	public boolean isConnected(){
		for(K point : this.getAllPoints()){
			List<K> result = this.breadhFirstSearch(point);
			if(result.size() != this.pointNum()){
				return false;
			}
		}
		return true;		
	}
	
	@Override
	public String toString() {
		return table.toString();
	}
	
	//for minimal spanning tree
	Map<K,V> getConnectedSides(K point){
		return table.get(point);
	}
	
	//for minimal spanning tree
	void visit(List<K> hasVisited,ArrayHeap<Side<K,V>> heap,K point){
		//mark as visited
		hasVisited.add(point);
		
		//all sides contain point
		Map<K,V> connectedSides = this.getConnectedSides(point);
		//all points on the other side
		Set<K> connectedPoints = connectedSides.keySet();
		//add all sides to minimal heap
		for(K p:connectedPoints){
			if(!hasVisited.contains(p)){
				try {
					heap.addElement(new Side<K,V>(point,p,connectedSides.get(p)));
				} catch (NonComparableElementException e) {
					e.printStackTrace();
				}
			}
		}
	}
		
	/*
	 * Prim algorithm
	 */
	@SuppressWarnings("unchecked")
	public NearTable<K,V> minSpanningTree() throws EmptyCollectionException, NonComparableElementException{
		
		//if the graph is empty,throw exception
		if(this.isEmpty()){
			throw new EmptyCollectionException(this.getClass().getName());
		}
		
		//if it is not connected, return empty graph
		if(!this.isConnected()){
			return new NearTable<K,V>();
		}
		
		//minimal heap
		ArrayHeap<Side<K,V>> heap = new ArrayHeap<Side<K,V>>();
		//minimal spanning tree
		NearTable<K,V> result = new NearTable<K,V>();
		//has visited points
		List<K> hasVisited = new ArrayList<K>();
		
		//get all points
		Set<K> points = table.keySet();
		//get first point as start point
		K point = (K) points.toArray()[0];
		
		//visit start point
		this.visit(hasVisited, heap, point);
		
		//stop if get all point or heap is empty
		while((result.pointNum() < this.pointNum()) || !heap.isEmpty()){
				
			//minimal side
			Side<K,V> side = heap.removeMin();
			
			K pointA = side.getPointA();
			K pointB = side.getPointB();
			
			//skip invalid side
			if(hasVisited.contains(pointA) && hasVisited.contains(pointB)){
				continue;
			}
			
			//add to minimal spanning tree
			result.addEdge(side.getPointA(), side.getPointB(), side.getWeight());
			
			if(!hasVisited.contains(pointA)){
				this.visit(hasVisited, heap, pointA);
			}
			
			if(!hasVisited.contains(pointB)){
				this.visit(hasVisited, heap, pointB);
			}
		}
		return result;
	}
	
	public static void main(String[] args) throws EmptyCollectionException, NonComparableElementException {
		NearTable<String,Integer> table1 = new NearTable<String,Integer>();
		table1.addEdge("1", "2", 12);
		table1.addEdge("1", "4", 6);
		table1.addEdge("2", "4", 8);
		table1.addEdge("2", "5", 3);
		table1.addEdge("3", "4", 11);
		table1.addEdge("3", "5", 1);
		table1.show();
		System.out.println("--------------------------");
		table1.minSpanningTree().show();	
	}

}