python实现图数据结构_[从头学数学] 第252节 Python实现数据结构:图(Graph)

import xml.dom.minidom

import DataStruct as ds;

class Graph():

def readXmlFile(self, file):

xmldoc = xml.dom.minidom.parse(file)

Graph = xmldoc.getElementsByTagName("Graph")[0];

attr = Graph.attributes;

width = float(attr["width"].value);

height = float(attr["height"].value);

directed = attr["directed"].value;

weighted = attr["weighted"].value;

G = [width, height, directed, weighted];

print('图属性:', G);

Vertices = xmldoc.getElementsByTagName("Vertex");

V = [];

for vertex in Vertices:

attr = vertex.attributes;

vid = int(attr["vertexId"].value);

x = float(attr["x"].value);

y = float(attr["y"].value);

lable = int(attr["label"].value);

V.append([vid, x, y, lable]);

Edges = xmldoc.getElementsByTagName("Edge");

E = [];

for edge in Edges:

attr = edge.attributes;

tail = int(attr["tail"].value);

head = int(attr["head"].value);

E.append([tail, head]);

print('顶点集:', V);

print('边集:', E);

return (G, V, E);

def writeXmlFile(self, file, GVETuple):

file = open(file, "w")

file.write('n')

G, V, E = GVETuple;

GAttr = ['width', 'height', 'directed', 'weighted'];

VAttr = ['vertexId', 'x', 'y', 'label'];

EAttr = ['tail', 'head'];

file.write('n');

file.write(lev*indent+'n');

lev+=1;

for j in range(len(V)):

file.write(lev*indent+'

用例:def tmp():

graph = Graph();

data = graph.readXmlFile("graph.xml");

#graph.writeXmlFile("graphcopy.xml", data);

#由于用例的顶点序号和它的标签数字不一样,所以有了下面这么多变换

path = graph.graphDFS(data, 10, 9);

print('path: ', path);

G,V,E = data;

vNum = len(V);

for k in range(len(path)):

print('第{0}条路径'.format(k+1));

for i in range(len(path[k])):

print(V[vNum-path[k][i]-1][3],'->', end = '');

print('');

一张不考虑权重的有向图:

#图1

20160730092553866.jpg

第二个部分:绘图部分

图属性: [595.8, 229.2, 'True', 'False']

顶点集: [[12, 343.15, 156.1, 10], [11, 246.15, 161.1, 9], [10, 288.15, 58.1, 0], [9, 374.15, 58.1, 1], [8, 135.15, 156.1, 6], [7, 49.65, 83.1, 2], [6, 167.15, 83.05, 3], [5, 121.15, 19.1, 8], [4, 419.15, 204.1, 11], [3, 426.15, 87.1, 4], [2, 546.15, 96.1, 5], [1, 546.15, 210.1, 7], [0, 485.15, 161.1, 12]]

边集: [[12, 10], [10, 6], [6, 11], [7, 6], [7, 8], [7, 5], [11, 12], [12, 4], [4, 0], [0, 2], [2, 1], [2, 3], [3, 9], [9, 10], [9, 12]]

if (1) {

var text = new DrawText();

plot.translate(0, 200);

//有向图两点间画向量连接,否则用直线连接

var drawVector = $Graph[2];

var vNum = $Verts.length;

var eNum = $Edges.length;

var x1, y1, x2, y2, dx, dy;

var P1, P2;

for (var i = 0; i < eNum; i++) {

P1 = $Verts[vNum-$Edges[i][0]-1];

P2 = $Verts[vNum-$Edges[i][1]-1];

x1 = P1[1];

y1 = P1[2];

x2 = P2[1];

y2 = P2[2];

dx = (x2-x1)/4;

dy = (y2-y1)/4;

x1 += dx;

x2 -= dx;

y1 += dy;

y2 -= dy;

if (drawVector == "True") {

shape.vectorDraw([[x1, -y1], [x2, -y2]], 'blue', 1);

}

else {

shape.multiLineDraw([[x1, -y1], [x2, -y2]], 'blue', 1);

}

}

for (var i = 0; i < vNum; i++) {

text.textWithCircle([$Verts[i][3].toFixed(0)], $Verts[i][1], $Verts[i][2], 0,

'green', 30, 'M');

}

//文本显示方便类

function DrawText() {

this.protype = function(str, xPos, yPos, traits, rotate, style, fontSize, alignment) {

//traits参数指定了显示正常/加粗/斜体等区别

//style 是颜色/渐变的区别

//fontSize是字体字号的区别

//alignment是对齐方式的区别

rotate = rotate ? rotate : 0;

style = style ? style : 'black';

fontSize = fontSize >= 10 ? fontSize : 20;

//左对齐0, 中对齐1, 右对齐2

alignment = alignment ? alignment : 'L';

var tmp = fontSize.toFixed(0)+'px';

var font = '';

if (traits == 'normal') {

font = "normal normal normal "+tmp+" Times New Roman";

}

else if (traits == 'bold') {

font = 'normal normal 800 '+tmp+' Arial';

}

else if (traits == 'italic') {

font = 'italic normal bold '+tmp+' Microsoft Sans Serif';

}

else {

font = "normal normal normal "+tmp+" Times New Roman";

}

plot.save()

.setFont(font)

//.setTextBaseline('top')

.setFillStyle(style)

.translate(xPos, yPos)

.rotate(-rotate);

var x = 0, y = 0;

var measure = 0;

var s = '';

//多行

var len = str.length;

for (var i = 0; i < len; i++) {

s = str[i];

measure = plot.measureText(s);

if (alignment == 1 || alignment.toUpperCase() == 'M' || alignment.toUpperCase() == 'C'){

//[x,y]为居中对齐的中点

plot.fillText(s, x-measure/2, y, measure);

}

else if (alignment == 2 || alignment.toUpperCase() == 'R'){

//[x,y]为右对齐的右边边界点

plot.fillText(s, x-measure, y, measure);

}

else {

//于[x,y]处左对齐

plot.fillText(s, x, y, measure);

}

y += fontSize*1.5;

}

plot.restore();

}

this.normal = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

return this.protype(str, xPos, yPos, 'normal', rotate, style, fontSize, alignment);

}

this.bold = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

return this.protype(str, xPos, yPos, 'bold', rotate, style, fontSize, alignment);

}

this.italic = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

return this.protype(str, xPos, yPos, 'italic', rotate, style, fontSize, alignment);

}

//计算文字显示需要的边界范围,这是一个矩形,并且考虑到了文字的旋转和多行问题

this.calcRectBound = function(str, xPos, yPos, rotate, fontSize, alignment) {

rotate = rotate ? rotate : 0;

fontSize = fontSize >= 10 ? fontSize : 20;

//左对齐0, 中对齐1, 右对齐2

alignment = alignment ? alignment : 'L';

var font = "normal normal normal "+fontSize.toFixed(0)+'px'+" Times New Roman";

plot.save()

.setFont(font);

var lines = str.length;

var maxChars = 0;

var lineChars = 0;

for (var i = 0; i < lines; i++) {

lineChars = plot.measureText(str[i])+5;

if (lineChars > maxChars) {

maxChars = lineChars;

}

}

var boundWidth = maxChars;

var boundHeight = fontSize*1.5*lines;

var hMargin = 0;

var array = [];

//根据行数确定偏移距离

if (lines == 1) {

hMargin = -boundHeight/2+Math.floor(fontSize/3);

}

else {

hMargin = fontSize/2 - 1.5*(lines-1)*fontSize/2;

}

if (alignment == 1 || alignment.toUpperCase() == 'M' || alignment.toUpperCase() == 'C'){

array = [[boundWidth/2, 0],

[-boundWidth/2, 0],

[-boundWidth/2, boundHeight],

[boundWidth/2, boundHeight]];

}

else if (alignment == 2 || alignment.toUpperCase() == 'R'){

array = [[0, 0],

[-boundWidth, 0],

[-boundWidth, boundHeight],

[0, boundHeight]];

}

else {

array = [[boundWidth, 0],

[0, 0],

[0, boundHeight],

[boundWidth, boundHeight]];

}

var transform = new Transform();

array = transform.translate(transform.rotate(transform.translate(array, 0, hMargin), rotate), xPos, -yPos);

plot.restore();

return array;

}

//计算文字显示需要的边界范围,这是一个椭圆形,并且考虑到了文字的旋转和多行问题

this.calcEllipBound = function(str, xPos, yPos, rotate, fontSize, alignment) {

rotate = rotate ? rotate : 0;

fontSize = fontSize >= 10 ? fontSize : 20;

//左对齐0, 中对齐1, 右对齐2

alignment = alignment ? alignment : 'L';

var font = "normal normal normal "+fontSize.toFixed(0)+'px'+" Times New Roman";

plot.save()

.setFont(font);

var lines = str.length;

var maxChars = 0;

var lineChars = 0;

for (var i = 0; i < lines; i++) {

lineChars = plot.measureText(str[i]);

if (lineChars > maxChars) {

maxChars = lineChars;

}

}

var boundWidth = maxChars;

var boundHeight = fontSize*1.5*lines;

var hMargin = 0;

var array = [];

var transform = new Transform();

//椭圆的长短半轴

var ea = boundWidth/2*1.2, eb = boundHeight/2*1.2;

//根据行数确定偏移距离

if (lines == 1) {

hMargin = fontSize/2;

}

else {

hMargin = fontSize/2 - 1.5*(lines-1)*fontSize/2;

}

if (alignment == 1 || alignment.toUpperCase() == 'M' || alignment.toUpperCase() == 'C'){

array = shape.ellipse(ea, eb);

array = transform.translate(transform.rotate(transform.translate(array, 0, hMargin), rotate), xPos, -yPos);

}

else if (alignment == 2 || alignment.toUpperCase() == 'R'){

array = shape.ellipse(ea, eb);

array = transform.translate(transform.rotate(transform.translate(array, -boundWidth/2, hMargin), rotate), xPos, -yPos);

}

else {

array = shape.ellipse(ea, eb);

array = transform.translate(transform.rotate(transform.translate(array, boundWidth/2, hMargin), rotate), xPos, -yPos);

}

plot.restore();

return array;

}

this.textWithRect = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

var boundArray = this.calcRectBound(str, xPos, yPos, rotate, fontSize, alignment);

//hint(boundArray);

shape.strokeDraw(boundArray, style, 1);

this.normal(str, xPos, yPos, rotate, style, fontSize, alignment);

}

//文字伴随一柄小旗

//当旋转时,现在还未能完全算清楚准确的坐标变换

this.textWithFlag = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

var transform = new Transform();

shape.fillDraw(shape.nEdge(xPos, -yPos-5, 10, 3, Math.PI), style, 1);

var boundArray = this.calcRectBound(str, 0, 0, 0, fontSize, 'L');

//取阵列中间两个点,是为了加一柄棋杆。

var p_1x = boundArray[1][0], p_1y = boundArray[1][1],

p_2x = boundArray[2][0], p_2y = boundArray[2][1];

var dx = 1*(p_2x-p_1x), dy = 1*(p_2y-p_1y);

p_2x = p_2x-2*dx;

p_2y = p_2y-2*dy;

var line = [[p_1x, p_1y], [p_2x, p_2y]];

xPos = xPos - dx;

yPos = yPos - dy-fontSize/4;

boundArray = transform.translate(transform.rotate(boundArray, rotate), xPos, -yPos);

line = transform.translate(transform.rotate(line, rotate), xPos, -yPos);

shape.multiLineDraw([].concat(line), style, 1);

//hint(boundArray);

shape.strokeDraw(boundArray, style, 1);

this.bold(str, xPos, yPos, rotate, style, fontSize, 'L');

}

this.textWithEllipse = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

var boundArray = this.calcEllipBound(str, xPos, yPos, rotate, fontSize, alignment);

//hint(boundArray);

shape.strokeDraw(boundArray, style, 1);

this.normal(str, xPos, yPos, rotate, style, fontSize, alignment);

}

this.textWithCircle = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

var r = 30;

//hint(boundArray);

shape.strokeCircle(xPos, yPos, r);

this.normal(str, xPos, yPos+fontSize/4, rotate, style, fontSize, alignment);

}

//两个点连直线,上面写文字

this.textWithLine = function(str, xPos1, yPos1, xPos2, yPos2, style, fontSize, alignment) {

var rotate = xPos2 == xPos1 ? 0 : Math.atan((yPos2-yPos1)/(xPos2-xPos1));

//hint(boundArray);

shape.multiLineDraw([[xPos1, yPos1], [xPos2, yPos2]], style, 1);

this.bold(str, xPos, yPos, rotate, style, fontSize, alignment);

}

//两个点连向量,上面写文字

this.textWithVector = function(str, xPos1, yPos1, xPos2, yPos2, style, fontSize, alignment) {

var rotate = xPos2 == xPos1 ? 0 : Math.atan((yPos2-yPos1)/(xPos2-xPos1));

//hint(boundArray);

shape.vectorDraw([[xPos1, yPos1], [xPos2, yPos2]], style, 1);

this.bold(str, xPos, yPos, rotate, style, fontSize, alignment);

}

this.textWithSphere = function(str, xPos, yPos, rotate, style, fontSize, alignment, sphereR) {

var R = sphereR > 5 ? sphereR : 5;

shape.sphere([xPos, yPos], R, style);

this.bold(str, xPos, yPos-R, rotate, style, fontSize, alignment);

}

//一组圆圈,以第一圈为中心,在它的外边围一圈较小的圆

this.textWithCluster = function(str, xPos, yPos, rotate, style, fontSize, alignment) {

var centerD = fontSize*4;

var peripheralD = fontSize*3;

//中央大圆

var circle_C = shape.nEdge(xPos, yPos, centerD/2, 36, 0);

//传入的str应该是一个字符串数组,['s1', 's2', ...]这种格式

//把s1填入中央圆圈,其它的填入它周围的一系列圆圈中

//字符串数组中除第一个串以外的字符串的个数

var count = str.length - 1;

//小弟不能太少,到少留三个位置

if (count < 3) count = 3;

//内外圆的圆心距离 periOff > (centerD+peripheralD)/2

//同时要满足 (periOff* 6)/count > peripheralD

var periOff = Math.max((centerD+peripheralD)/2, peripheralD*count/6);

var periArray = shape.nEdge(0, 0, periOff, count);

var transform = new Transform;

periArray = transform.translate(transform.rotate(periArray, rotate), xPos, yPos);

//画圆圈集群

shape.strokeDraw([].concat(circle_C), style, 1);

var circle_P = [];

for (var i = 0; i < count; i++) {

circle_P = shape.nEdge(periArray[i][0], periArray[i][1], peripheralD/2, 36, 0);

shape.strokeDraw(circle_P, style, 1);

}

this.bold([str[0]], xPos, Math.abs(yPos)+0.5*fontSize, 0, style, fontSize, alignment);

for (var i = 0; i < count; i++) {

this.normal([str[1+i]], periArray[i][0], Math.abs(periArray[i][1])+0.4*fontSize, 0, style, fontSize*0.7, alignment);

}

}

}

第三个部分:Dijk寻路算法

###

# @usage 求两个给定地点,所有路径中的最短值

# @author mw

# @date 2016年01月13日 星期三 09:50:23

# @param

# @return

#

###

class DijkstraPath():

#初始化

def __init__(self, node_map):

self.node_map = node_map;

self.node_length = len(node_map);

self.used_node_list = [];

self.collected_node_dict = {};

#调用函数

def __call__(self, from_node, to_node):

self.from_node = from_node;

self.to_node = to_node;

self._init_dijkstra();

return self._format_path();

def _init_dijkstra(self):

self.used_node_list.append(self.from_node);

self.collected_node_dict[self.from_node] = [0, -1];

for index1, node1 in enumerate(self.node_map[self.from_node]):

if node1:

self.collected_node_dict[index1] = [node1, self.from_node];

self._foreach_dijkstra();

def _foreach_dijkstra(self):

#保证每个点最多只到一次

if len(self.used_node_list) == self.node_length - 1:

return;

#由于items()方法会改变原字典内容,所以此处拷贝副本

collected_node_dict = dict(self.collected_node_dict);

# 遍历已有权值节点

for key, val in collected_node_dict.items():

if key not in self.used_node_list and key != to_node:

self.used_node_list.append(key);

else:

continue;

# 对节点进行遍历

for index1, node1 in enumerate(self.node_map[key]):

# 如果节点在权值节点中并且权值大于新权值

if node1 and index1 in self.collected_node_dict

and self.collected_node_dict[index1][0] > node1 + val[0]:

# 更新权值

self.collected_node_dict[index1][0] = node1 + val[0]

self.collected_node_dict[index1][1] = key;

elif node1 and index1 not in self.collected_node_dict:

self.collected_node_dict[index1] = [node1 + val[0], key];

#递归

self._foreach_dijkstra();

def _format_path(self):

node_list = [];

temp_node = self.to_node;

node_list.append((temp_node, self.collected_node_dict[temp_node][0]));

while self.collected_node_dict[temp_node][1] != -1:

temp_node = self.collected_node_dict[temp_node][1];

node_list.append((temp_node, self.collected_node_dict[temp_node][0]));

node_list.reverse();

return node_list;

def pathString(self, node):

node_list = self._format_path();

size = len(node_list);

s = '选择的路线是:n';

for i in range(size):

if i < size-1:

s += str(node[node_list[i][0]])+'-->';

else:

s += str(node[node_list[i][0]]);

s+= ' ,这条路线总长为{0}米'.format(node_list[size-1][1]);

return s;

def set_node_map(node_map, node, node_list):

for x, y, val in node_list:

node_map[node.index(x)][node.index(y)] = node_map[node.index(y)][node.index(x)] = val

用例:

if __name__ == "__main__":

#节点表

node = [1, 2, 3, 4, 5, 6];

addressString = ['学校', '医院', '体育场', '公园', '小年宫', '小伟家'];

#带权重的边表

node_list = [(1,6,250), (1,2,300), (5,6,300), (4,5,470),

(3,4,150), (2,3,450),(2,5,250)];

#节点数量的平方

node_map = [[0 for val in range(len(node))] for val in range(len(node))];

#预设节点表

set_node_map(node_map, node, node_list);

#Demo 求A --> D 的最短距离

from_node = node.index(6);

to_node = node.index(3);

#求取路径

dijkstrapath = DijkstraPath(node_map);

#路径字符串

path = dijkstrapath(from_node, to_node);

print(dijkstrapath.pathString(addressString));

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值