有向图1----数据结构以及SWT图形化表示

有向图:由一组顶点和一组有方向的边组成,每条有方向的边都连接一组有序的顶点

顶点出度:指出的边的总数  顶点入度:指向该顶点边的总数

有向路径:一系列顶点,每个顶点存在一条有向边又它指向下一个顶点

有向环:至少含有一条边,并且起点和终点相同的有向路径

单点有向环:起点和终点相同不含有其他重复顶点和边的环

有向图数据结构

有向图的表示与无向图基本类似,包含顶点数,边数以及邻接表数组用来存储边的关系。当向图中添加一条边时,由于边是有序的,只需要添加依次即可。

private void addEdge(int w, int v) {
		adj[w].add(v);
		E++;
}

具体代码实现如下:

//有向图数据结构表示
public class Digraph {
	
	private final int V;
	private int E;
	private Bag<Integer>[] adj;
	
	//创建一个只含有顶点的有向图
	public Digraph(int V){
		this.V = V;
		this.E = 0;
		adj = new Bag[V];
		for(int i=0; i<V; i++){
			adj[i] = new Bag<Integer>();
		}
	}
	
	//创建有向图  wr起点  vr终点
	public Digraph(int V, int E, int[] wr, int[] vr){
		this(V);
		for(int i=0; i<E; i++){
			addEdge(wr[i], vr[i]);
		}
	}

	//添加一条有向边  w-->v
	private void addEdge(int w, int v) {
		adj[w].add(v);
		E++;
	}
	
	public int V(){
		return this.V;
	}
	
	public int E(){
		return this.E;
	}
	
	//返回某个顶点指出的顶点连接
	public Iterable<Integer> adj(int v){
		return adj[v];
	}
	
	//有向图反转
	public Digraph reverse(){
		Digraph R = new Digraph(V);
		for(int v=0; v<E; v++){
			for(int w : adj[v]){
				R.addEdge(w, v);
			}	
		}
		return R;
	}
	
	public String toString(){
		String s = V + " vertices, " + E + " edges\n";
		for(int v =0; v<V; v++){
			s += v + ": ";
			Iterator<Integer> it = this.adj(v).iterator();
			while(it.hasNext()){
				int w = it.next();
				if(it.hasNext())
					s += w + "--->";
				else
					s += w;
			}
			s += "\n";
		}
		return s;
	}
	
	public static void main(String[] args) {
		int V = 13;
		int E = 22;
		int[] wr = {4,2,3,6,0,2,11,12,9,9,8,10,11,4,3,7,8,5,0,6,6,7};
		int[] vr = {2,3,2,0,1,0,12,9,10,11,9,12,4,3,5,8,7,4,5,4,9,6};
		Digraph d = new Digraph(V, E, wr, vr);
		System.out.println(d.toString());
	}

}

                                         

有向图的SWT图形化显示

为了便于显示有向图性质,利用SWT来绘制有向图,边是有序的,利用箭头来表示方向。

SWT箭头绘制方法参考http://www.blogjava.net/senlin-blog/archive/2007/09/21/146974.html

                    

绘制一个箭头可以看成是绘制三条线段,AB,BC,BD

假定箭头的高度为H,宽度为2L,则可以求出ABC角度,BC可以看成是BA经过顺时针矢量旋转ABC得到,BD可以看成BA经过顺时针旋转负ABC角度得到,由矢量旋转公式,可以得到旋转后的坐标。

具体旋转矢量最终坐标公式:https://www.cnblogs.com/hongsheng001/p/4127458.html

                               

x0=|R|*cosA      
y0=|R|*sinA                 
 x1 =|R|*cos(A +B)  
y1=|R|*sin(A+B)
所以将x1,y1展开,有:
x1=|R|*(cosAcosB-sinAsinB)
y1=|R|*(sinAcosB+cosAsinB)
 
把    cosA = x0/|R|   sinA = y0/|R| 代入上面的式子,得到
 x1 = |R|*(x0*cosB/|R|-y0*sinB/|R|)
 y1 = |R|*(y0*cosB/|R|+x0*sinB/|R|)
最终结果:
x1 = x0 * cosB - y0 * sinB
y1 = x0 * sinB + y0 * cosB

最终得到的绘制箭头SWT代码如下:

         /**
	 * 画带箭头的线
	 */
	public void paintk(GC g, int x1, int y1, int x2, int y2) {

		double H = 10; // 箭头高度
		double L = 7; // 底边的一半
		int x3 = 0;
		int y3 = 0;
		int x4 = 0;
		int y4 = 0;
		double awrad = Math.atan(L / H); // 箭头角度
		double arraow_len = Math.sqrt(L * L + H * H); // 箭头的长度
		double[] arrXY_1 = rotateVec(x2 - x1, y2 - y1, awrad, true, arraow_len);
		double[] arrXY_2 = rotateVec(x2 - x1, y2 - y1, -awrad, true, arraow_len);
		double x_3 = x2 - arrXY_1[0]; // (x3,y3)是第一端点
		double y_3 = y2 - arrXY_1[1];
		double x_4 = x2 - arrXY_2[0]; // (x4,y4)是第二端点
		double y_4 = y2 - arrXY_2[1];

		Double X3 = new Double(x_3);
		x3 = X3.intValue();
		Double Y3 = new Double(y_3);
		y3 = Y3.intValue();
		Double X4 = new Double(x_4);
		x4 = X4.intValue();
		Double Y4 = new Double(y_4);
		y4 = Y4.intValue();
		// g.setColor(SWT.COLOR_WHITE);
		// 画线
		g.drawLine(x1, y1, x2, y2);
		// 画箭头的一半
		g.drawLine(x2, y2, x3, y3);
		// 画箭头的另一半
		g.drawLine(x2, y2, x4, y4);

	}

	/**
	 * 取得箭头的绘画范围
	 */
	public double[] rotateVec(int px, int py, double ang, boolean isChLen, double newLen) {
		double mathstr[] = new double[2];
		// 矢量旋转函数,参数含义分别是x分量、y分量、旋转角、是否改变长度、新长度
		double vx = px * Math.cos(ang) - py * Math.sin(ang);
		double vy = px * Math.sin(ang) + py * Math.cos(ang);
		if (isChLen) {
			double d = Math.sqrt(vx * vx + vy * vy);
			vx = vx / d * newLen;
			vy = vy / d * newLen;
			mathstr[0] = vx;
			mathstr[1] = vy;
		}
		return mathstr;
	}

为了便于显示,需要考虑起始点,终点相对位置,来设置终点的坐标,以免button的本身形状影响箭头显示。

        //绘制有向图
	private void drawDiGraph(List<PointG> lp, DiGraphPoint gp){
		for(int i=0; i<lp.size(); i++){
			Button lname = new Button(group, SWT.NONE|SWT.CENTER);
			lname.setBounds(startX+lp.get(i).x*dilableOffsetX, startY+lp.get(i).y*dilableOffsetY, labelW, labelH);
			lname.setText(String.valueOf(i));
			lname.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
			lname.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
		}
		gc = new GC(group);
		gc.setLineWidth(2);
		gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_BLACK));
		int[] vr = gp.getVr();
		int[] wr = gp.getWr();
		for(int i=0; i<gp.getG().E(); i++){
			PointG p1 = lp.get(vr[i]);
			PointG p2 = lp.get(wr[i]);
			int x1 = startX+p1.x*dilableOffsetX+labelW/2;
			int y1 = startY+p1.y*dilableOffsetY+labelH/2;
			int x2 = 0;
			int y2 = 0;
			//由于箭头绘制特殊性,坐标需要考虑起始点以及终点位置
			//终点在起始点左边   y<--x
			if(p2.x<=p1.x && p2.y==p1.y){
				x2 = startX+p2.x*dilableOffsetX+labelW;
				y2 = startY+p2.y*dilableOffsetY+labelH/2;
			}			
			//终点在起始点右边 x-->y
			else if(p2.x>=p1.x && p2.y==p1.y){
				x2 = startX+p2.x*dilableOffsetX;
				y2 = startY+p2.y*dilableOffsetY+labelH/2;			
			}
			//终点在起始点上边方 x-->y
			else if(p2.x==p1.x && p2.y<=p1.y){
				x2 = startX+p2.x*dilableOffsetX+labelW/2;
				y2 = startY+p2.y*dilableOffsetY+labelH;
			}
			//终点在起始点下边方 x-->y
			else if(p2.x==p1.x && p2.y>=p1.y){
				x2 = startX+p2.x*dilableOffsetX+labelW/2;
				y2 = startY+p2.y*dilableOffsetY;
			}
			//终点在起始点左上方 x-->y
			else if(p2.x<=p1.x && p2.y<=p1.y){
				x2 = startX+p2.x*dilableOffsetX+labelW;
				y2 = startY+p2.y*dilableOffsetY+labelH;			
			}
			//终点在起始点左下方 x-->y
			else if(p2.x<=p1.x && p2.y>=p1.y){
				x2 = startX+p2.x*dilableOffsetX+labelW;
				y2 = startY+p2.y*dilableOffsetY;				
			}
			//终点在起始点右上方 x-->y
			else if(p2.x>=p1.x && p2.y<=p1.y){
				x2 = startX+p2.x*dilableOffsetX;
				y2 = startY+p2.y*dilableOffsetY+labelH;
			}
			//终点在起始点右下方 x-->y
			else if(p2.x>=p1.x && p2.y>=p1.y){
				x2 = startX+p2.x*dilableOffsetX;
				y2 = startY+p2.y*dilableOffsetY;	
			}
			paintk(gc, x1, y1, x2, y2);
		}
		gc.dispose();
	}

最终效果如下所示;

                                                 

代码位置:https://github.com/ChenWenKaiVN/TreeSWT/blob/master/src/com/swt/GraphSWT.java

http://www.blogjava.net/senlin-blog/archive/2007/09/21/146974.html

https://www.cnblogs.com/hongsheng001/p/4127458.html

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值