【典型习题整理】数据结构与算法作业

本文介绍了数据结构中的图表示方法,包括邻接表和邻接矩阵,并详细讲解了kruskal算法和prim算法求解最小生成树的过程。通过示例展示了两种算法的应用及结果。
摘要由CSDN通过智能技术生成

数据结构与算法


图的邻接表和邻接矩阵表示法以及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,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值