数据结构与算法
图的邻接表和邻接矩阵表示法以及kruskal&prim算法求最小生成树
图的邻接表表示
概念如下:邻接表
其实我们大可以使用hashmap构建邻接表,但是为了深入理解邻接表的数据结构,以及其数组—链表表示,现给出代码:
package FifteenWeek;
/**
* 邻接表
* @author Hello
*
*/
public class AdjacencyList {
// 邻接表中表对应的链表的顶点
private class ENode{
int ivex; // 该边所指向的顶点的位置
ENode next; // 指向下一条弧的指针
}
// 邻接表中表的顶点
private class VNode {
char data; // 顶点信息
ENode firstEdge; // 指向第一条依附该顶点的弧
};
private VNode[] mVexs; // 顶点数组
/**
*
* @param vexs : [A,B,C,D,E,F,G]
* @param edges : {[A,B],[B,C],[B,E],[B,F],[C,E],[D,C],[E,B],[E,D],[F,G]}
*/
public AdjacencyList(char[] vexs, char[][] edges){
// 初始化"顶点数"和"边数"
int vlen = vexs.length;
int elen = edges.length;
// 初始化"顶点"
mVexs = new VNode[vlen];
for (int i = 0; i < mVexs.length; i++) {
mVexs[i] = new VNode();
mVexs[i].data = vexs[i];
mVexs[i].firstEdge = null;
}
// 初始化"边"
for (int i = 0; i < elen; i++) {
// 读取边的起始顶点和结束顶点
char c1 = edges[i][0];
char c2 = edges[i][1];
// 读取边的起始顶点和结束顶点
int p1 = getPosition(edges[i][0]);
int p2 = getPosition(edges[i][1]);
// 初始化node1
ENode node1 = new ENode();
node1.ivex = p2;
// 将node1链接到"p1所在链表的末尾"
if(mVexs[p1].firstEdge == null)
mVexs[p1].firstEdge = node1;
else
linkLast(mVexs[p1].firstEdge, node1);
}
}
/*
* 返回ch在mVexs中的位置
*/
private int getPosition(char ch) {
for(int i=0; i<mVexs.length; i++)
if(mVexs[i].data==ch)
return i;
return -1;
}
/*
* 将(Enode) node节点链接到(Enode) list的最后
*/
private void linkLast(ENode list, ENode node) {
ENode p = list;
while(p.next!=null)
p = p.next;
p.next = node;
}
}
图的邻接矩阵表示
这个比较简单,即所谓的map[i][j]=k表示从图的顶点i到顶点j得到的边的权重(权值)为k,对于无向图而言,它的邻接矩阵是个对称阵。
kruskl算法
概念如下:kruskal算法
其实通过这种“单步”贪心算法得到的结果并不总是最小生成树,但因其简洁性以及综合权值也足够小,故而有所推广,值得学习:
package FifteenWeek;
/**
* line 76 必须要+1
* @author Hello
*
*/
public class Kruskal {
private static final int MAX=Integer.MAX_VALUE;
public static void main(String[] args) {
// TODO Auto-generated method stub
//邻接矩阵表示的无向图
int[][] map = new int[][]{
{0,10,MAX,MAX,MAX,11,MAX,MAX,MAX},
{10,0,18,MAX,MAX,MAX,16,MAX,12},
{MAX,MAX,0,22,MAX,MAX,MAX,MAX,8},
{MAX,MAX,22,0,20,MAX,MAX,16,21},
{MAX,MAX,MAX,20,0,26,MAX,7,MAX},
{11,MAX,MAX,MAX,26,0,17,MAX,MAX},
{MAX,16,MAX,MAX,MAX,17,0,19,MAX},
{MAX,MAX,MAX,16,7,MAX,19,0,MAX},
{MAX,12,8,21,MAX,MAX,MAX,MAX,0}
};
kruskal(map);
}
private static void kruskal(int[][] map) {
// TODO Auto-generated method stub
int num=map.length, sum=0;
int[] edge=new int[3];//端点1, 端点2, 权重
DisjointSet closure=new DisjointSet(num+1);//并查集类参考上期
while(pos(closure.node) < map.length-1){//之所以要-1是因为第一次加入两个端点时closure.node数组里面只有一个正值,另一个依旧是-1(用于表示根节点)
edge=findMin(map);
while((closure.search(edge[0]) == closure.search(edge[1])) && (edge[0]!=0 && edge[1]!=0))
edge=findMin(map);
closure.union(edge[0], edge[1]);
//System.out.println(pos(closure.node));
sum+=edge[2];
print(closure.node);
}
System.out.println("sum : "+sum);
}
private static void print(int[] node) {
// TODO Auto-generated method stub
System.out.println("node : ");
for(int i=0;i<node.length;i++)
System.out.print(node[i]+", ");
System.out.println();
}
private static int pos(int[] node) {//返回node[]中有几个正数(已经压入了几个端点)
// TODO Auto-generated method stub
int count=0;
for(int i=0;i<node.length;i++)
if(node[i] >= 0) count++;
return count;
}
private static int[] findMin(int[][] map) {
// TODO Auto-generated method stub
int min=MAX, n1=0, n2=0;
for(int i=0; i<map.length; i++)
for(int j=0; j<i; j++)
if(map[i][j] < min){
min=map[i][j];
n1=i;
n2=j;
}
map[n1][n2]=MAX;
return new int[]{n1+1, n2+1, min};//必须要+1!!!!
}
}
结果如下:
node :
-1, -1, -1, -1, -1, -2, -1, -1, 5, -1,
node :
-1, -1, -1, -2, -1, -2, -1, -1, 5, 3,
node :
-1, -2, 1, -2, -1, -2, -1, -1, 5, 3,
node :
-1, -3, 1, -2, -1, -2, 1, -1, 5, 3,
node :
-1, -5, 1, 1, -1, -2, 1, -1, 5, 3,
node :
-1, -6, 1, 1, -1, -2, 1, 1, 5, 3,
node :
-1, -6, 1, 1, 5, -3, 1, 1, 5, 3,
node :
-1, -9, 1, 1, 5, 1, 1, 1, 5, 3,
sum : 99
prim算法
概念如下:prim算法
与kruskal有所不同的是, prim选择从某一既定顶点出发,每次“合并”一个顶点之后可供选择的边数也要加上新顶点可直接选择的边,然后再从这些边中不断选择最小权值的边即可。
代码如下:
package FifteenWeek;
import java.util.Arrays;
public class Prim {
private static final int max=Integer.MAX_VALUE;
private static int[][] map = new int[][]{
{0,10,max,max,max,11,max,max,max},
{10,0,18,max,max,max,16,max,12},
{max,max,0,22,max,max,max,max,8},
{max,max,22,0,20,max,max,16,21},
{max,max,max,20,0,26,max,7,max},
{11,max,max,max,26,0,17,max,max},
{max,16,max,max,max,17,0,19,max},
{max,max,max,16,7,max,19,0,max},
{max,12,8,21,max,max,max,max,0}
};
public static void main(String args[]){
prim();
}
private static void prim() {
// TODO Auto-generated method stub
int len=map.length, sum=0;
int[] lowcost;
int[] nearvex = new int[len];
int[][] map2 = new int[len][len];
DisjointSet closure=new DisjointSet();
lowcost=map[0]; Arrays.fill(nearvex, 0);
lowcost[0]=max; nearvex[0]=-1;
for(int i=0;i<len-1;i++){//因为到了第8次循环的时候就nearvex[]已经有了9个-1了,刚好形成连通图
int[] edge=findMin(lowcost);
//System.out.println(nearvex[edge[0]]+", "+edge[0]);
//edge[0] : 一条边的终点; edge[1] : value
//nearvex[edge[0]] : 一条边的起点
map2[nearvex[edge[0]]][edge[0]]=edge[1];
map2[edge[0]][nearvex[edge[0]]]=edge[1];
nearvex[edge[0]]=-1;//设置-1表示该下标edge[0](即新边的终点)已经加入连通图,无需继续在这个顶点上费时
lowcost=getMin(map[edge[0]], lowcost, nearvex, edge[0]);
}
print(map2);
}
private static void print(int[][] map2) {
// TODO Auto-generated method stub
for(int i=0;i<map2.length;i++){
for(int j=0;j<map2[0].length;j++)
System.out.print(map2[i][j]+", ");
System.out.println();
}
}
private static int[] getMin(int[] is, int[] lowcost, int[] nearvex, int start) {
// TODO Auto-generated method stub
int[] low=new int[lowcost.length];
for(int i=0;i<low.length;i++){
if(nearvex[i]==-1) low[i]=max;
else{
low[i]=Math.min(is[i], lowcost[i]);
nearvex[i]=(is[i] < lowcost[i]) ? start : nearvex[i];//当新顶点的加入使得它到顶点i的距离更短时,需要修改nearvex[],使i下标处表示的起点为这个新顶点!!!
}
}
return low;
}
private static int[] findMin(int[] lowcost) {
// TODO Auto-generated method stub
int index=0, min=max;
for(int i=0;i<lowcost.length;i++)
if(lowcost[i]<min){
index=i;
min=lowcost[i];
}
lowcost[index]=max;
return new int[]{index, min};
}
}
结果如下:
0, 10, 0, 0, 0, 11, 0, 0, 0,
10, 0, 0, 0, 0, 0, 16, 0, 12,
0, 0, 0, 0, 0, 0, 0, 0, 8,
0, 0, 0, 0, 0, 0, 0, 16, 0,
0, 0, 0, 0, 0, 0, 0, 7, 0,
11, 0, 0, 0, 0, 0, 0, 0, 0,
0, 16, 0, 0, 0, 0, 0, 19, 0,
0, 0, 0, 16, 7, 0, 19, 0, 0,
0, 12, 8, 0, 0, 0, 0, 0, 0,