这个实现只考虑实现无向图邻接表的功能,底层使用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();
}
}