1. 概念
② 對于一個帶權(quán)的連通圖,當(dāng)生成的樹不同,各邊上的權(quán)值總和也不同,如果某個生成樹的權(quán)值最小,則它就是“最小生成樹”。
2. 場景
3. prim算法
第三步: 我們尋找V1的鄰接點(V2,V3,V5),權(quán)值中發(fā)現(xiàn)(V1,V2)之間的權(quán)值最小,此時我們將V2放入U集合中并標(biāo)記V2為已訪問,
第四步: 我們找U集合中的V1和V2的鄰接邊,一陣痙攣后,發(fā)現(xiàn)(V1,V5)的權(quán)值最小,此時將V5加入到U集合并標(biāo)記為已訪問,此時
第五步:此時我們以(V1,V2,V5)為基準(zhǔn)向四周尋找最小權(quán)值的鄰接邊,發(fā)現(xiàn)(V5,V4)的權(quán)值最小,此時將V4加入到U集合并標(biāo)記
第六步: 跟第五步形式一樣,找到了(V1,V3)的權(quán)值最小,將V3加入到U集合中并標(biāo)記為已訪問,最終U的元素為(V1,V2,V5,V4,V3),
#region prim算法獲取最小生成樹
/// summary>
/// prim算法獲取最小生成樹
/// /summary>
/// param name="graph">/param>
public void Prim(MatrixGraph graph, out int sum)
{
//已訪問過的標(biāo)志
int used = 0;
//非鄰接頂點標(biāo)志
int noadj = -1;
//定義一個輸出總權(quán)值的變量
sum = 0;
//臨時數(shù)組,用于保存鄰接點的權(quán)值
int[] weight = new int[graph.vertexNum];
//臨時數(shù)組,用于保存頂點信息
int[] tempvertex = new int[graph.vertexNum];
//取出鄰接矩陣的第一行數(shù)據(jù),也就是取出第一個頂點并將權(quán)和邊信息保存于臨時數(shù)據(jù)中
for (int i = 1; i graph.vertexNum; i++)
{
//保存于鄰接點之間的權(quán)值
weight[i] = graph.edges[0, i];
//等于0則說明V1與該鄰接點沒有邊
if (weight[i] == short.MaxValue)
tempvertex[i] = noadj;
else
tempvertex[i] = int.Parse(graph.vertex[0]);
}
//從集合V中取出V1節(jié)點,只需要將此節(jié)點設(shè)置為已訪問過,weight為0集合
var index = tempvertex[0] = used;
var min = weight[0] = short.MaxValue;
//在V的鄰接點中找權(quán)值最小的節(jié)點
for (int i = 1; i graph.vertexNum; i++)
{
index = i;
min = short.MaxValue;
for (int j = 1; j graph.vertexNum; j++)
{
//用于找出當(dāng)前節(jié)點的鄰接點中權(quán)值最小的未訪問點
if (weight[j] min tempvertex[j] != 0)
{
min = weight[j];
index = j;
}
}
//累加權(quán)值
sum += min;
Console.Write("({0},{1}) ", tempvertex[index], graph.vertex[index]);
//將取得的最小節(jié)點標(biāo)識為已訪問
weight[index] = short.MaxValue;
tempvertex[index] = 0;
//從最新的節(jié)點出發(fā),將此節(jié)點的weight比較賦值
for (int j = 0; j graph.vertexNum; j++)
{
//已當(dāng)前節(jié)點為出發(fā)點,重新選擇最小邊
if (graph.edges[index, j] weight[j] tempvertex[j] != used)
{
weight[j] = graph.edges[index, j];
//這里做的目的將較短的邊覆蓋點上一個節(jié)點的鄰接點中的較長的邊
tempvertex[j] = int.Parse(graph.vertex[index]);
}
}
}
}
#endregion
1. 概念
2. Dijkstra算法
我們的學(xué)習(xí)需要站在巨人的肩膀上,那么對于現(xiàn)實中非常復(fù)雜的問題,我們肯定不能用肉眼看出來,而是根據(jù)一定的算法推導(dǎo)出來的。
發(fā)現(xiàn)(V1,V2)的權(quán)值最小,此時我們將V2放入U集合中,表示我們已經(jīng)找到了V1到V2的最短路徑。
第二步:然后將V2做中間點,繼續(xù)向前尋找權(quán)值最小的鄰接點,發(fā)現(xiàn)只有V4可以連通,此時修改V4的權(quán)值為(V1,V2)+(V2,V4)=6。
此時我們就要看一步,發(fā)現(xiàn)V1到(V3,V4,V5)中權(quán)值最小的是(V1,V5),此時將V5放入U集合中,表示我們已經(jīng)找到了
第三步:然后將V5做中間點,繼續(xù)向前尋找權(quán)值最小的鄰接點,發(fā)現(xiàn)能連通的有V3,V4,當(dāng)我們正想修該V3的權(quán)值時發(fā)現(xiàn)(V1,V3)的權(quán)值
第四步:因為V5還沒有走完,所以繼續(xù)用V5做中間點,此時只能連通(V5,V4),當(dāng)要修改權(quán)值的時候,發(fā)現(xiàn)原來的V4權(quán)值為(V1,V2)+(V2,V4),而
現(xiàn)在的權(quán)值為5,小于先前的6,此時更改原先的權(quán)值變?yōu)?,將V4放入集合中,最后我們找到了V1到V4的最短路徑。
#region dijkstra求出最短路徑
/// summary>
/// dijkstra求出最短路徑
/// /summary>
/// param name="g">/param>
public void Dijkstra(MatrixGraph g)
{
int[] weight = new int[g.vertexNum];
int[] path = new int[g.vertexNum];
int[] tempvertex = new int[g.vertexNum];
Console.WriteLine("\n請輸入源點的編號:");
//讓用戶輸入要遍歷的起始點
int vertex = int.Parse(Console.ReadLine()) - 1;
for (int i = 0; i g.vertexNum; i++)
{
//初始賦權(quán)值
weight[i] = g.edges[vertex, i];
if (weight[i] short.MaxValue weight[i] > 0)
path[i] = vertex;
tempvertex[i] = 0;
}
tempvertex[vertex] = 1;
weight[vertex] = 0;
for (int i = 0; i g.vertexNum; i++)
{
int min = short.MaxValue;
int index = vertex;
for (int j = 0; j g.vertexNum; j++)
{
//頂點的權(quán)值中找出最小的
if (tempvertex[j] == 0 weight[j] min)
{
min = weight[j];
index = j;
}
}
tempvertex[index] = 1;
//以當(dāng)前的index作為中間點,找出最小的權(quán)值
for (int j = 0; j g.vertexNum; j++)
{
if (tempvertex[j] == 0 weight[index] + g.edges[index, j] weight[j])
{
weight[j] = weight[index] + g.edges[index, j];
path[j] = index;
}
}
}
Console.WriteLine("\n頂點{0}到各頂點的最短路徑為:(終點 源點) " + g.vertex[vertex]);
//最后輸出
for (int i = 0; i g.vertexNum; i++)
{
if (tempvertex[i] == 1)
{
var index = i;
while (index != vertex)
{
var j = index;
Console.Write("{0} ", g.vertex[index]);
index = path[index];
}
Console.WriteLine("{0}\n", g.vertex[index]);
}
else
{
Console.WriteLine("{0} - {1}: 無路徑\n", g.vertex[i], g.vertex[vertex]);
}
}
}
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace MatrixGraph
{
public class Program
{
static void Main(string[] args)
{
MatrixGraphManager manager = new MatrixGraphManager();
//創(chuàng)建圖
MatrixGraph graph = manager.CreateMatrixGraph();
manager.OutMatrix(graph);
int sum = 0;
manager.Prim(graph, out sum);
Console.WriteLine("\n最小生成樹的權(quán)值為:" + sum);
manager.Dijkstra(graph);
//Console.Write("廣度遞歸:\t");
//manager.BFSTraverse(graph);
//Console.Write("\n深度遞歸:\t");
//manager.DFSTraverse(graph);
Console.ReadLine();
}
}
#region 鄰接矩陣的結(jié)構(gòu)圖
/// summary>
/// 鄰接矩陣的結(jié)構(gòu)圖
/// /summary>
public class MatrixGraph
{
//保存頂點信息
public string[] vertex;
//保存邊信息
public int[,] edges;
//深搜和廣搜的遍歷標(biāo)志
public bool[] isTrav;
//頂點數(shù)量
public int vertexNum;
//邊數(shù)量
public int edgeNum;
//圖類型
public int graphType;
/// summary>
/// 存儲容量的初始化
/// /summary>
/// param name="vertexNum">/param>
/// param name="edgeNum">/param>
/// param name="graphType">/param>
public MatrixGraph(int vertexNum, int edgeNum, int graphType)
{
this.vertexNum = vertexNum;
this.edgeNum = edgeNum;
this.graphType = graphType;
vertex = new string[vertexNum];
edges = new int[vertexNum, vertexNum];
isTrav = new bool[vertexNum];
}
}
#endregion
/// summary>
/// 圖的操作類
/// /summary>
public class MatrixGraphManager
{
#region 圖的創(chuàng)建
/// summary>
/// 圖的創(chuàng)建
/// /summary>
/// param name="g">/param>
public MatrixGraph CreateMatrixGraph()
{
Console.WriteLine("請輸入創(chuàng)建圖的頂點個數(shù),邊個數(shù),是否為無向圖(0,1來表示),已逗號隔開。");
var initData = Console.ReadLine().Split(',').Select(i => int.Parse(i)).ToList();
MatrixGraph graph = new MatrixGraph(initData[0], initData[1], initData[2]);
//我們默認(rèn)“正無窮大為沒有邊”
for (int i = 0; i graph.vertexNum; i++)
{
for (int j = 0; j graph.vertexNum; j++)
{
graph.edges[i, j] = short.MaxValue;
}
}
Console.WriteLine("請輸入各頂點信息:");
for (int i = 0; i graph.vertexNum; i++)
{
Console.Write("\n第" + (i + 1) + "個頂點為:");
var single = Console.ReadLine();
//頂點信息加入集合中
graph.vertex[i] = single;
}
Console.WriteLine("\n請輸入構(gòu)成兩個頂點的邊和權(quán)值,以逗號隔開。\n");
for (int i = 0; i graph.edgeNum; i++)
{
Console.Write("第" + (i + 1) + "條邊:\t");
initData = Console.ReadLine().Split(',').Select(j => int.Parse(j)).ToList();
int start = initData[0];
int end = initData[1];
int weight = initData[2];
//給矩陣指定坐標(biāo)位置賦值
graph.edges[start - 1, end - 1] = weight;
//如果是無向圖,則數(shù)據(jù)呈“二,四”象限對稱
if (graph.graphType == 1)
{
graph.edges[end - 1, start - 1] = weight;
}
}
return graph;
}
#endregion
#region 輸出矩陣數(shù)據(jù)
/// summary>
/// 輸出矩陣數(shù)據(jù)
/// /summary>
/// param name="graph">/param>
public void OutMatrix(MatrixGraph graph)
{
for (int i = 0; i graph.vertexNum; i++)
{
for (int j = 0; j graph.vertexNum; j++)
{
if (graph.edges[i, j] == short.MaxValue)
Console.Write("∽\t");
else
Console.Write(graph.edges[i, j] + "\t");
}
//換行
Console.WriteLine();
}
}
#endregion
#region 廣度優(yōu)先
/// summary>
/// 廣度優(yōu)先
/// /summary>
/// param name="graph">/param>
public void BFSTraverse(MatrixGraph graph)
{
//訪問標(biāo)記默認(rèn)初始化
for (int i = 0; i graph.vertexNum; i++)
{
graph.isTrav[i] = false;
}
//遍歷每個頂點
for (int i = 0; i graph.vertexNum; i++)
{
//廣度遍歷未訪問過的頂點
if (!graph.isTrav[i])
{
BFSM(ref graph, i);
}
}
}
/// summary>
/// 廣度遍歷具體算法
/// /summary>
/// param name="graph">/param>
public void BFSM(ref MatrixGraph graph, int vertex)
{
//這里就用系統(tǒng)的隊列
Queueint> queue = new Queueint>();
//先把頂點入隊
queue.Enqueue(vertex);
//標(biāo)記此頂點已經(jīng)被訪問
graph.isTrav[vertex] = true;
//輸出頂點
Console.Write(" ->" + graph.vertex[vertex]);
//廣度遍歷頂點的鄰接點
while (queue.Count != 0)
{
var temp = queue.Dequeue();
//遍歷矩陣的橫坐標(biāo)
for (int i = 0; i graph.vertexNum; i++)
{
if (!graph.isTrav[i] graph.edges[temp, i] != 0)
{
graph.isTrav[i] = true;
queue.Enqueue(i);
//輸出未被訪問的頂點
Console.Write(" ->" + graph.vertex[i]);
}
}
}
}
#endregion
#region 深度優(yōu)先
/// summary>
/// 深度優(yōu)先
/// /summary>
/// param name="graph">/param>
public void DFSTraverse(MatrixGraph graph)
{
//訪問標(biāo)記默認(rèn)初始化
for (int i = 0; i graph.vertexNum; i++)
{
graph.isTrav[i] = false;
}
//遍歷每個頂點
for (int i = 0; i graph.vertexNum; i++)
{
//廣度遍歷未訪問過的頂點
if (!graph.isTrav[i])
{
DFSM(ref graph, i);
}
}
}
#region 深度遞歸的具體算法
/// summary>
/// 深度遞歸的具體算法
/// /summary>
/// param name="graph">/param>
/// param name="vertex">/param>
public void DFSM(ref MatrixGraph graph, int vertex)
{
Console.Write("->" + graph.vertex[vertex]);
//標(biāo)記為已訪問
graph.isTrav[vertex] = true;
//要遍歷的六個點
for (int i = 0; i graph.vertexNum; i++)
{
if (graph.isTrav[i] == false graph.edges[vertex, i] != 0)
{
//深度遞歸
DFSM(ref graph, i);
}
}
}
#endregion
#endregion
#region prim算法獲取最小生成樹
/// summary>
/// prim算法獲取最小生成樹
/// /summary>
/// param name="graph">/param>
public void Prim(MatrixGraph graph, out int sum)
{
//已訪問過的標(biāo)志
int used = 0;
//非鄰接頂點標(biāo)志
int noadj = -1;
//定義一個輸出總權(quán)值的變量
sum = 0;
//臨時數(shù)組,用于保存鄰接點的權(quán)值
int[] weight = new int[graph.vertexNum];
//臨時數(shù)組,用于保存頂點信息
int[] tempvertex = new int[graph.vertexNum];
//取出鄰接矩陣的第一行數(shù)據(jù),也就是取出第一個頂點并將權(quán)和邊信息保存于臨時數(shù)據(jù)中
for (int i = 1; i graph.vertexNum; i++)
{
//保存于鄰接點之間的權(quán)值
weight[i] = graph.edges[0, i];
//等于0則說明V1與該鄰接點沒有邊
if (weight[i] == short.MaxValue)
tempvertex[i] = noadj;
else
tempvertex[i] = int.Parse(graph.vertex[0]);
}
//從集合V中取出V1節(jié)點,只需要將此節(jié)點設(shè)置為已訪問過,weight為0集合
var index = tempvertex[0] = used;
var min = weight[0] = short.MaxValue;
//在V的鄰接點中找權(quán)值最小的節(jié)點
for (int i = 1; i graph.vertexNum; i++)
{
index = i;
min = short.MaxValue;
for (int j = 1; j graph.vertexNum; j++)
{
//用于找出當(dāng)前節(jié)點的鄰接點中權(quán)值最小的未訪問點
if (weight[j] min tempvertex[j] != 0)
{
min = weight[j];
index = j;
}
}
//累加權(quán)值
sum += min;
Console.Write("({0},{1}) ", tempvertex[index], graph.vertex[index]);
//將取得的最小節(jié)點標(biāo)識為已訪問
weight[index] = short.MaxValue;
tempvertex[index] = 0;
//從最新的節(jié)點出發(fā),將此節(jié)點的weight比較賦值
for (int j = 0; j graph.vertexNum; j++)
{
//已當(dāng)前節(jié)點為出發(fā)點,重新選擇最小邊
if (graph.edges[index, j] weight[j] tempvertex[j] != used)
{
weight[j] = graph.edges[index, j];
//這里做的目的將較短的邊覆蓋點上一個節(jié)點的鄰接點中的較長的邊
tempvertex[j] = int.Parse(graph.vertex[index]);
}
}
}
}
#endregion
#region dijkstra求出最短路徑
/// summary>
/// dijkstra求出最短路徑
/// /summary>
/// param name="g">/param>
public void Dijkstra(MatrixGraph g)
{
int[] weight = new int[g.vertexNum];
int[] path = new int[g.vertexNum];
int[] tempvertex = new int[g.vertexNum];
Console.WriteLine("\n請輸入源點的編號:");
//讓用戶輸入要遍歷的起始點
int vertex = int.Parse(Console.ReadLine()) - 1;
for (int i = 0; i g.vertexNum; i++)
{
//初始賦權(quán)值
weight[i] = g.edges[vertex, i];
if (weight[i] short.MaxValue weight[i] > 0)
path[i] = vertex;
tempvertex[i] = 0;
}
tempvertex[vertex] = 1;
weight[vertex] = 0;
for (int i = 0; i g.vertexNum; i++)
{
int min = short.MaxValue;
int index = vertex;
for (int j = 0; j g.vertexNum; j++)
{
//頂點的權(quán)值中找出最小的
if (tempvertex[j] == 0 weight[j] min)
{
min = weight[j];
index = j;
}
}
tempvertex[index] = 1;
//以當(dāng)前的index作為中間點,找出最小的權(quán)值
for (int j = 0; j g.vertexNum; j++)
{
if (tempvertex[j] == 0 weight[index] + g.edges[index, j] weight[j])
{
weight[j] = weight[index] + g.edges[index, j];
path[j] = index;
}
}
}
Console.WriteLine("\n頂點{0}到各頂點的最短路徑為:(終點 源點) " + g.vertex[vertex]);
//最后輸出
for (int i = 0; i g.vertexNum; i++)
{
if (tempvertex[i] == 1)
{
var index = i;
while (index != vertex)
{
var j = index;
Console.Write("{0} ", g.vertex[index]);
index = path[index];
}
Console.WriteLine("{0}\n", g.vertex[index]);
}
else
{
Console.WriteLine("{0} - {1}: 無路徑\n", g.vertex[i], g.vertex[vertex]);
}
}
}
#endregion
}
}