题目
第五章 图
图是一种较线性表和树更为复杂的数据结构,也是日常生活中应用广泛的结构之一。在线性表中,数据元素之间仅仅有线性关系,每个数据元素只有一个直接前驱和一个直接后继;在树形结构中,数据元素之间有着明显的层次关系,并且每一层上的数据元素可能和下一层中多个元素(即其孩子结点)相关,但只能和上一层中的一个元素(即其双亲结点相关);而在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关。由此,图的应用极为广泛,特别是近年来的迅速发展,已渗入到诸如语言学、逻辑学、物理、化学、电讯工程、计算机科学以及数学的其他分支中。
5.2 导航最短路径查询
5.2.1 项目简介
设计一个交通咨询系统,能让旅客咨询从任一个城市顶点到另一个城市顶点之间的最短路径问题。设计分三个部分,一是建立交通网络图的存储结构;二是解决单源最短路径问题;最后再实现两个城市顶点之间的最短路径问题。
最短路径问题的提出随着计算机的普及以及地理信息科学的发展,GIS因其强大的功能得到日益广泛和深入的应用。网络分析作为GIS最主要的功能之一,在电子导航、交通旅游、城市规划以及电力、通讯等各种管网、管线的布局设计中发挥了重要的作用。而网络分析中最基本和关键的问题是最短路径问题。
最短路径不仅仅指一般地理意义上的距离最短,还可以引申到其他的度量,如时间、费用、线路容量等。相应地,最短路径问题就成为最快路径问题、最低费用问题等。由于最短路径问题在实际中常用于汽车导航系统以及各种应急系统等(110报警、119火警以及医疗救护系统),这些系统一般要求计算出到出事地点的最佳路线的时间一般在1s-3s,在行车过程中还需要实时计算出车辆前方的行驶路线,这就决定了最短路径问题的实现应该是高效率的。最优路径问题不仅包括最短路径问题,还有可能涉及到最少时间问题、最少收费(存在收费公路)问题、或者是几个问题的综合,这时将必须考虑道路级别、道路流量、道路穿越代价(如红灯平均等待时间)等诸多因素。但是必须指出的是,一般来说最优路径在距离上应该是最短的,但最短路径在行驶时间和能源消耗的意义上未必是最优的。其实,无论是距离最短、时间最快还是费用最低,它们的核心算法都是最短路径算法。
5.2.2 设计思路
单源最短路径算法的主要代表之一是Dijkstra(迪杰斯特拉)算法。该算法是目前多数系统解决最短路径问题采用的理论基础,在每一步都选择局部最优解,以期望产生一个全局最优解。
Dijksira算法的基本思路是:对于图G=(V,E),V是包含n个顶点的顶点集,E是包含m条弧的弧集,(v, w)是E中从v到w的弧,c(v, w)是弧(v, w)的非负权值,设s为V中的顶点,t为V中可由s到达的顶点,则求解从s至t的具有最小弧权值和的最短路径搜索过程如下:
(1) 将v中的顶点分为3类:已标记点、未标记点、己扫描点。将s初始化为己标记点,其它顶点为未标记点。为每个顶点v都建立一个权值d和后向顶点指针p,并将d初始化如下:d(v)=0,v=s;d(v)=∞,v≠s。
(2) 重复进行扫描操作:从所有已标记点中选择一个具有最小权值的顶点v并将其设为己扫描点,然后检测每个以v为顶点的弧(v, w),若满足d(v) + c(v, w) < d(w) 则将顶点v设为已标记点,并令d(w) = d(v) + c(v, w), p(w) = v。
(3) 若终点t被设为已扫描点,则搜索结束。由t开始遍历后向顶点指针P直至起点s,即获得最短路径解。
5.2.3 数据结构
(1)定义一个数组min_dist,它的每个数组元素min_dist[i]表示当前所找到的从始点vi到每个终点vj的最短路径的长度。它的初态为:若从vi到vj有边,则min_dist[j]为边的权值;否则置min_dist[i]为∞。定义一个数组path,其元素path[k](0≤k≤n-1)用以记录vi到vk最短路径中vk的直接前驱结点序号,如果vi到vk存在边,则path[k]初值为i。定义一个数组W,存储任意两点之间边的权值。
(2)查找min(min_dist[j],j∈V-S),设min_dist[k]最小,将k加入S中。修改对于V-S 中的任一点vj,min_dist[j]=min(min_dist[k]+w[k][j], min_dist[j]) 且path[j]=k。
(3)重复上一步,直到V-S为空。
在算法设计时,用一个tag数组来记录某个顶点是否已计算过最短距离,如果tag[k]=0,则vk∈V-S,否则vk∈S。初始值除tag[i]=1以外,所有值均为0。
功能展示:
6号楼到荟园忘记画在图上了 是190m
代码
Form1.cs
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ex4_GPI2
{
public partial class Form1 : Form
{
//地点数目
static int n = 16;
//地点数组
ArrayList placeList = new ArrayList();
ArrayList placeList2 = new ArrayList();
string[] array = new string[17];
//邻接矩阵,用于存放个点间距离
static int[,] arc = new int[n+1, n + 1];
public Form1()
{
InitializeComponent();
//显示框可以多行显示
this.searchText1.Multiline = true;
//初始化地点列表
InitList();
//初始化邻接矩阵
InitArc();
}
private void pictureBox1_Click(object sender, EventArgs e)
{
}
private void label1_Click(object sender, EventArgs e)
{
}
//查询按钮点击事件
private void search1_Click(object sender, EventArgs e)
{
//标记是否已被计算过
bool[] tag = new bool[n + 1];
//记录最短距离
int[] distince = new int[n + 1];
//记录路径
string[] route = new string[17];
//起点索引
int beginKey = beginBox.SelectedIndex + 1;
//终点索引
int endKey = endBox.SelectedIndex + 1;
//初始化
for (int i = 1; i < n+1; i++)
{
tag[i] = false;
distince[i] = arc[beginKey, i];
route[i] = array[beginKey] + "->" +array[i];
//Console.WriteLine(distince[i]);
}
tag[beginKey] = true;
//找N-1条路径
for (int i = 1; i < n; i++)
{
//已找到终点最短距离
//if (tag[endKey] == true)
// break;
int u = 1;
//找到第一个没有被计算过的点
while (tag[u] == true)
u++;
//找到距离最小点
for (int w = u + 1; w <= n; w++)
{
if (tag[w] == false && distince[w] < distince[u])
u = w;
}
tag[u] = true;
//更新最短路径
for (int w = 1; w <= n; w++)
{
if (tag[w] == false && ((distince[u] + arc[u, w]) < distince[w])){
distince[w] = distince[u] + arc[u, w];
route[w] = route[u] + "->" + array[w];
}
}
}
searchText1.Text = route[endKey];
searchText2.Text = "" + distince[endKey] + "米";
for (int i = 1; i < route.Length; i++)
{
searchBox3.Text += "" + i + ":" + "路径:" + route[i] + "\r\n" + "距离:" + distince[i] + "m" + "\r\n";
}
}
//初始化地点列表
public void InitList()
{
array[1] = "6号楼"; array[2] = "荟园"; array[3] = "馨园"; array[4] = "泰园"; array[5] = "雀园";
array[6] = "图书馆"; array[7] = "商院"; array[8] = "海院"; array[9] = "机电院"; array[10] = "艺院";
array[11] = "电子楼"; array[12] = "网络楼"; array[13] = "操场"; array[14] = "浴池"; array[15] = "驿站";
array[16] = "南门";
placeList.Add(new DictionaryEntry("1", "6号楼")); placeList.Add(new DictionaryEntry("2", "荟园"));
placeList.Add(new DictionaryEntry("3", "馨园")); placeList.Add(new DictionaryEntry("4", "泰园"));
placeList.Add(new DictionaryEntry("5", "雀园")); placeList.Add(new DictionaryEntry("6", "图书馆"));
placeList.Add(new DictionaryEntry("7", "商院")); placeList.Add(new DictionaryEntry("8", "海院"));
placeList.Add(new DictionaryEntry("9", "机电院")); placeList.Add(new DictionaryEntry("10", "艺院"));
placeList.Add(new DictionaryEntry("11", "电子楼")); placeList.Add(new DictionaryEntry("12", "网络楼"));
placeList.Add(new DictionaryEntry("13", "操场")); placeList.Add(new DictionaryEntry("14", "浴池"));
placeList.Add(new DictionaryEntry("15", "驿站")); placeList.Add(new DictionaryEntry("16", "南门"));
placeList2.Add(new DictionaryEntry("1", "6号楼")); placeList2.Add(new DictionaryEntry("2", "荟园"));
placeList2.Add(new DictionaryEntry("3", "馨园")); placeList2.Add(new DictionaryEntry("4", "泰园"));
placeList2.Add(new DictionaryEntry("5", "雀园")); placeList2.Add(new DictionaryEntry("6", "图书馆"));
placeList2.Add(new DictionaryEntry("7", "商院")); placeList2.Add(new DictionaryEntry("8", "海院"));
placeList2.Add(new DictionaryEntry("9", "机电院")); placeList2.Add(new DictionaryEntry("10", "艺院"));
placeList2.Add(new DictionaryEntry("11", "电子楼")); placeList2.Add(new DictionaryEntry("12", "网络楼"));
placeList2.Add(new DictionaryEntry("13", "操场")); placeList2.Add(new DictionaryEntry("14", "浴池"));
placeList2.Add(new DictionaryEntry("15", "驿站")); placeList2.Add(new DictionaryEntry("16", "南门"));
beginBox.DataSource = placeList;
beginBox.ValueMember = "Key";
beginBox.DisplayMember = "Value";
endBox.DataSource = placeList2;
endBox.ValueMember = "Key";
endBox.DisplayMember = "Value";
}
//初始化邻接矩阵
public void InitArc()
{
for (int i = 0; i < 17; i++)
for (int j = 0; j < 17; j++)
if (i == j)
arc[i, j] = 0;
//999999表示不存在通路
else
arc[i, j] = 999999;
arc[1, 2] = arc[2, 1] = 190;
arc[1, 4] = arc[4, 1] = 100;
arc[1, 13] = arc[13, 1] = 320;
arc[1, 5] = arc[5, 1] = 140;
arc[2, 3] = arc[3, 2] = 50;
arc[2, 4] = arc[4, 2] = 150;
arc[2, 14] = arc[14, 2] = 150;
arc[3, 6] = arc[6, 4] = 368;
arc[3, 16] = arc[16, 3] = 370;
arc[5, 13] = arc[13, 5] = 220;
arc[5, 6] = arc[6, 5] = 290;
arc[6, 11] = arc[11, 6] = 100;
arc[6, 12] = arc[12, 6] = 184;
arc[6, 10] = arc[10, 6] = 280;
arc[6, 7] = arc[7, 6] = 270;
arc[7, 8] = arc[8, 7] = 110;
arc[7, 10] = arc[10, 7] = 130;
arc[8, 9] = arc[9, 8] = 140;
arc[8, 16] = arc[16, 8] = 300;
arc[11, 12] = arc[11, 12] = 100;
arc[11, 13] = arc[13, 11] = 170;
arc[14, 15] = arc[15, 14] = 120;
arc[15, 16] = arc[16, 15] = 230;
}
}
}
Form1.Designer.cs
namespace ex4_GPI2
{
partial class Form1
{
/// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
/// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows 窗体设计器生成的代码
/// <summary>
/// 设计器支持所需的方法 - 不要修改
/// 使用代码编辑器修改此方法的内容。
/// </summary>
private void InitializeComponent()
{
this.components = new System.ComponentModel.Container();
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1));
this.label1 = new System.Windows.Forms.Label();
this.pictureBox1 = new System.Windows.Forms.PictureBox();
this.label2 = new System.Windows.Forms.Label();
this.panel1 = new System.Windows.Forms.Panel();
this.label3 = new System.Windows.Forms.Label();
this.panel2 = new System.Windows.Forms.Panel();
this.panel3 = new System.Windows.Forms.Panel();
this.label4 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.beginBox = new System.Windows.Forms.ComboBox();
this.endBox = new System.Windows.Forms.ComboBox();
this.search1 = new System.Windows.Forms.Button();
this.searchText1 = new System.Windows.Forms.TextBox();
this.label6 = new System.Windows.Forms.Label();
this.searchText2 = new System.Windows.Forms.TextBox();
this.label7 = new System.Windows.Forms.Label();
this.searchBox3 = new System.Windows.Forms.RichTextBox();
this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components);
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).BeginInit();
this.panel1.SuspendLayout();
this.panel2.SuspendLayout();
this.panel3.SuspendLayout();
this.SuspendLayout();
//
// label1
//
this.label1.Font = new System.Drawing.Font("宋体", 28.2F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label1.ForeColor = System.Drawing.SystemColors.MenuHighlight;
this.label1.Location = new System.Drawing.Point(1172, 27);
this.label1.Name = "label1";
this.label1.RightToLeft = System.Windows.Forms.RightToLeft.No;
this.label1.Size = new System.Drawing.Size(412, 66);
this.label1.TabIndex = 1;
this.label1.Text = "山威最短路径查询";
this.label1.Click += new System.EventHandler(this.label1_Click);
//
// pictureBox1
//
this.pictureBox1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.pictureBox1.Image = ((System.Drawing.Image)(resources.GetObject("pictureBox1.Image")));
this.pictureBox1.Location = new System.Drawing.Point(25, 48);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(610, 588);
this.pictureBox1.TabIndex = 2;
this.pictureBox1.TabStop = false;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Font = new System.Drawing.Font("宋体", 22.2F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label2.ForeColor = System.Drawing.SystemColors.Highlight;
this.label2.Location = new System.Drawing.Point(12, 10);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(368, 38);
this.label2.TabIndex = 3;
this.label2.Text = "山威最短路路径查询";
//
// panel1
//
this.panel1.BackColor = System.Drawing.SystemColors.ActiveCaption;
this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.panel1.Controls.Add(this.label2);
this.panel1.Location = new System.Drawing.Point(641, 12);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(416, 67);
this.panel1.TabIndex = 4;
//
// label3
//
this.label3.AutoSize = true;
this.label3.BackColor = System.Drawing.Color.LightGoldenrodYellow;
this.label3.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.label3.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label3.Location = new System.Drawing.Point(25, 24);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(169, 17);
this.label3.TabIndex = 5;
this.label3.Text = "山东大学(威海)地图";
//
// panel2
//
this.panel2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.panel2.Controls.Add(this.label7);
this.panel2.Controls.Add(this.searchText2);
this.panel2.Controls.Add(this.label6);
this.panel2.Controls.Add(this.searchText1);
this.panel2.Controls.Add(this.search1);
this.panel2.Controls.Add(this.endBox);
this.panel2.Controls.Add(this.beginBox);
this.panel2.Controls.Add(this.label5);
this.panel2.Controls.Add(this.label4);
this.panel2.Location = new System.Drawing.Point(642, 106);
this.panel2.Name = "panel2";
this.panel2.Size = new System.Drawing.Size(415, 182);
this.panel2.TabIndex = 6;
//
// panel3
//
this.panel3.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.panel3.Controls.Add(this.searchBox3);
this.panel3.Location = new System.Drawing.Point(642, 318);
this.panel3.Name = "panel3";
this.panel3.Size = new System.Drawing.Size(415, 318);
this.panel3.TabIndex = 7;
//
// label4
//
this.label4.AutoSize = true;
this.label4.BackColor = System.Drawing.Color.LightCyan;
this.label4.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.label4.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label4.Location = new System.Drawing.Point(18, 24);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(57, 17);
this.label4.TabIndex = 0;
this.label4.Text = "起点:";
//
// label5
//
this.label5.AutoSize = true;
this.label5.BackColor = System.Drawing.Color.LightCyan;
this.label5.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.label5.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label5.Location = new System.Drawing.Point(219, 24);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(57, 17);
this.label5.TabIndex = 1;
this.label5.Text = "终点:";
//
// beginBox
//
this.beginBox.BackColor = System.Drawing.SystemColors.Menu;
this.beginBox.FormattingEnabled = true;
this.beginBox.Location = new System.Drawing.Point(82, 21);
this.beginBox.Name = "beginBox";
this.beginBox.Size = new System.Drawing.Size(121, 23);
this.beginBox.TabIndex = 2;
//
// endBox
//
this.endBox.BackColor = System.Drawing.SystemColors.MenuBar;
this.endBox.FormattingEnabled = true;
this.endBox.Location = new System.Drawing.Point(282, 21);
this.endBox.Name = "endBox";
this.endBox.Size = new System.Drawing.Size(121, 23);
this.endBox.TabIndex = 3;
//
// search1
//
this.search1.BackColor = System.Drawing.Color.LightSteelBlue;
this.search1.Location = new System.Drawing.Point(166, 59);
this.search1.Name = "search1";
this.search1.Size = new System.Drawing.Size(110, 29);
this.search1.TabIndex = 4;
this.search1.Text = "查询";
this.search1.UseVisualStyleBackColor = false;
this.search1.Click += new System.EventHandler(this.search1_Click);
//
// searchText1
//
this.searchText1.Location = new System.Drawing.Point(18, 104);
this.searchText1.Name = "searchText1";
this.searchText1.Size = new System.Drawing.Size(385, 25);
this.searchText1.TabIndex = 5;
this.searchText1.Text = " \r\n \r\n ";
//
// label6
//
this.label6.AutoSize = true;
this.label6.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
this.label6.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.label6.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label6.Location = new System.Drawing.Point(18, 84);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(73, 17);
this.label6.TabIndex = 6;
this.label6.Text = "最短路径";
//
// searchText2
//
this.searchText2.Location = new System.Drawing.Point(20, 150);
this.searchText2.Name = "searchText2";
this.searchText2.Size = new System.Drawing.Size(383, 25);
this.searchText2.TabIndex = 7;
//
// label7
//
this.label7.AutoSize = true;
this.label7.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(255)))), ((int)(((byte)(255)))), ((int)(((byte)(192)))));
this.label7.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.label7.Font = new System.Drawing.Font("宋体", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
this.label7.Location = new System.Drawing.Point(20, 132);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(41, 17);
this.label7.TabIndex = 8;
this.label7.Text = "距离";
//
// searchBox3
//
this.searchBox3.Location = new System.Drawing.Point(18, 17);
this.searchBox3.Name = "searchBox3";
this.searchBox3.Size = new System.Drawing.Size(373, 283);
this.searchBox3.TabIndex = 0;
this.searchBox3.Text = "";
//
// contextMenuStrip1
//
this.contextMenuStrip1.ImageScalingSize = new System.Drawing.Size(20, 20);
this.contextMenuStrip1.Name = "contextMenuStrip1";
this.contextMenuStrip1.Size = new System.Drawing.Size(61, 4);
//
// Form1
//
this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.Azure;
this.ClientSize = new System.Drawing.Size(1069, 755);
this.Controls.Add(this.panel3);
this.Controls.Add(this.panel2);
this.Controls.Add(this.label3);
this.Controls.Add(this.panel1);
this.Controls.Add(this.pictureBox1);
this.Controls.Add(this.label1);
this.ForeColor = System.Drawing.SystemColors.ControlText;
this.Name = "Form1";
this.Text = "Form1";
((System.ComponentModel.ISupportInitialize)(this.pictureBox1)).EndInit();
this.panel1.ResumeLayout(false);
this.panel1.PerformLayout();
this.panel2.ResumeLayout(false);
this.panel2.PerformLayout();
this.panel3.ResumeLayout(false);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label label1;
private System.Windows.Forms.PictureBox pictureBox1;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Panel panel1;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Panel panel2;
private System.Windows.Forms.Panel panel3;
private System.Windows.Forms.Label label5;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.ComboBox endBox;
private System.Windows.Forms.ComboBox beginBox;
private System.Windows.Forms.TextBox searchText1;
private System.Windows.Forms.Button search1;
private System.Windows.Forms.Label label7;
private System.Windows.Forms.TextBox searchText2;
private System.Windows.Forms.Label label6;
private System.Windows.Forms.RichTextBox searchBox3;
private System.Windows.Forms.ContextMenuStrip contextMenuStrip1;
}
}