图的算法

1 图的遍历
图的遍历就是从图中某个顶点出发,按某种方法对图中所有顶点访问且仅访问一次。
遍历算法是求解图的连通性问题、拓扑排序和求关键路径等算法的基础。

2 深度优先遍历
从图中某个顶点V 出发,访问此顶点,然后依次从V的各个未被访问的邻接点出发
深度优先搜索遍历图,直至图中所有和V有路径相通的顶点都被访问到。 若此时图中
善有顶点未被访问,则另选图中一个未被访问的顶点作为起始点,重复上述过程,直至
图中所有顶点都被访问到为止。
对于从某个顶点v出发的深度优先遍历过程其实是一个递归的遍历过程。
2.1 递归遍历
//对图进行深度优先遍历
public Iterator DFSTraverse(Vertex v) {
LinkedList traverseSeq = new LinkedListDLNode();//需返回的遍历结果
resetVexStatus();//重置所有顶点状态为未访问
DFSRecursion(v, traverseSeq);//从v点出发深度优先搜索
Iterator it = getVertex();//从图的所有顶点
for(it.first(); !it.isDone(); it.next()){
Vertex u = (Vertex)it.currentItem();
//如果u未被访问,从u点出发深度优先搜索
if (!u.isVisited()) DFSRecursion(u, traverseSeq);
}
//返回访问结果
return traverseSeq.elements();
}
//深度优先的递归算法
private void DFSRecursion(Vertex v, LinkedList list){
v.setToVisited();//设置顶点访问状态为 已访问
list.insertLast(v);//将顶点加入到访问结果集中
Iterator it = adjVertexs(v);//取得顶点v的所有邻接点
for(it.first(); !it.isDone(); it.next()){
Vertex u = (Vertex)it.currentItem();
//如果u未被访问,从u点出发深度优先搜索
if (!u.isVisited()) DFSRecursion(u,list);
}
}

2.2 非递归遍历
//对图进行深度优先遍历
public Iterator DFSTraverse(Vertex v) {
LinkedList traverseSeq = new LinkedListDLNode();//需返回的遍历结果
resetVexStatus();//重置所有顶点状态为未访问
DFS(v, traverseSeq);//从v点出发深度优先搜索
Iterator it = getVertex();//从图的所有顶点
for(it.first(); !it.isDone(); it.next()){
Vertex u = (Vertex)it.currentItem();
//如果u未被访问,从u点出发深度优先搜索
if (!u.isVisited()) DFS(u, traverseSeq);
}
//返回访问结果
return traverseSeq.elements();
}
//深度优先的非递归算法
private void DFS(Vertex v, LinkedList list){
Stack s = new StackSLinked();
s.push(v);
while (!s.isEmpty()){
Vertex u = (Vertex)s.pop();//从未访问顶点栈中弹出一个顶点
if (!u.isVisited()){
u.setToVisited();
list.insertLast(u);
Iterator it = adjVertexs(u);//取得顶点v的所有邻接点
for(it.first(); !it.isDone(); it.next()){
Vertex adj = (Vertex)it.currentItem();
if (!adj.isVisited()) s.push(adj);//将为访问的顶点放入栈中 等待处理
}
}//if
}//while
}

3 广度优先遍历

从图中的某个顶点V出发,并在访问此顶点之后依次访问V的所有未被访问过的邻接点,
之后按这些顶点被访问的先后次序依次访问它们的邻接点,直至图中所有和V有路径相
通的顶点都被访问到。
若此时图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上
述过程,直至图中所有顶点都被访问到为止。

//对图进行广度优先遍历
public Iterator BFSTraverse(Vertex v) {
LinkedList traverseSeq = new LinkedListDLNode();//需返回的遍历结果
resetVexStatus();//重置所有顶点状态为未访问
BFS(v, traverseSeq);//从v点出发广度优先搜索
Iterator it = getVertex();//获取图中所有顶点
for(it.first(); !it.isDone(); it.next()){
Vertex u = (Vertex)it.currentItem();
if (!u.isVisited()) BFS(u, traverseSeq);
}
return traverseSeq.elements();
}
//广度优先遍历算法
private void BFS(Vertex v, LinkedList list){
//按这些顶点被访问的先后次序依次访问它们的邻接点,因而采用队列
Queue q = new QueueSLinked();
v.setToVisited();
list.insertLast(v);//将顶点加入到访问结果集中
q.enqueue(v);//入队
while (!q.isEmpty()){
Vertex u = (Vertex)q.dequeue();//出队
Iterator it = adjVertexs(u);//取得顶点v的所有邻接点
for(it.first(); !it.isDone(); it.next()){
Vertex adj = (Vertex)it.currentItem();
if (!adj.isVisited()){
adj.setToVisited();
list.insertLast(adj);
q.enqueue(adj);
}//if
}//for
}//while
}

4 连通图的最小生成树

假设要在 n 个城市之间建立通讯联络网,则连通 n 个城市只需要修建 n-1条线路,
如何在最节省经费的前提下建立这个通讯网?

4.1 算法一:Prim(普里姆算法)

思路:取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w。在添加的顶点

w 和已经在生成树上的顶点v 之间必定存在一条边,并且该边的权值在所有连通顶点 v 和 w 之间的边

中取值最小。之后继续往生成树上添加顶点,直至生成树上含有 n-1 个顶点为止。

思路的另一种表示方式:从只含有一个顶点的集合开始,寻找集合外面的顶点到这个集合里的顶点最近的一条边,然后将这个顶点加入集合,修改因为这个顶点的加入而使得集合外面的顶点到集合里的顶点的最短距离产生变化的分量。因为需要对每个顶点扫描,邻接矩阵储存的图是最合适Prim算法的。

一般情况下所添加的顶点应满足下列条件:
在生成树的构造过程中,图中 n 个顶点分属两个集合:已落在生成树上的顶点集 U 和尚未

落在生成树上的顶点集V-U ,则应在所有连通U中顶点和V-U中顶点的边中选取权值最小的边。

具体的实现中:S到(V-S)中各顶点的轻边 由该点的成员变量application表示,此时
application指向Edge类的对象,最小生成树的表示--采用设置图中 边的类型 来完成。

//获取到达顶点v的最小边(轻边)的权值
protected int getCrossWeight(Vertex v){
if (getCrossEdge(v)!=null) return getCrossEdge(v).getWeight();
else return Integer.MAX_VALUE;//表示无穷大,不相邻
}
//获取轻边(权最小的边)
protected Edge getCrossEdge(Vertex v){ return (Edge)v.getAppObj();}
//设置轻边
protected void setCrossEdge(Vertex v, Edge e){ v.setAppObj(e);}

//求无向图的最小生成树,如果是有向图不支持此操作
//前提是无向图是连通图,算法不判断图的连通性
public void generateMST(){
resetVexStatus();//重置图中各顶点的状态未未访问
resetEdgeType();//重置图中各边的类型为normal
Iterator it = getVertex();//获取所有的顶点集合
Vertex v = (Vertex)it.currentItem();//选第一个顶点作为起点
v.setToVisited();//顶点v进入集合S,以visited=true表示属于S,否则不属于S
//初始化顶点集合S到V-S各顶点的边
for(it.first(); !it.isDone(); it.next()){
Vertex u = (Vertex)it.currentItem();
Edge e = edgeFromTo(v,u);//返回从v指向u的边
setCrossEdge(u,e);  //设置到达V-S中顶点u的边
}
for (int t=1;t<getVexNum();t++){  //进行|V|-1次循环找到|V|-1条边
Vertex k = selectMinVertex(it);//中间顶点k
k.setToVisited();  //顶点k加入S
Edge mst = getCrossEdge(k);  //割(S , V - S) 的轻边
if (mst!=null) mst.setToMST();  //将边加入MST
//以k为中间顶点修改S到V-S中顶点的最短横切边
Iterator adjIt = adjVertexs(k);  //取出k的所有邻接点
for(adjIt.first(); !adjIt.isDone(); adjIt.next()){
Vertex adjV = (Vertex)adjIt.currentItem();
Edge e = this.edgeFromTo(k,adjV);
if (e.getWeight()<getCrossWeight(adjV))//发现到达adjV更短的横切边
setCrossEdge(adjV,e);
}//for
}//for(int t=1...
}
//查找轻边在V-S中的顶点
protected Vertex selectMinVertex(Iterator it){
Vertex min = null;
for(it.first(); !it.isDone(); it.next()){
Vertex v = (Vertex)it.currentItem();
if(!v.isVisited()){ min = v; break;}
}
for(; !it.isDone(); it.next()){
Vertex v = (Vertex)it.currentItem();
if(!v.isVisited()&&getCrossWeight(v)<getCrossWeight(min))
min = v;
}
return min;
}

4.2 Kruskal算法

  最小生成树直白的讲就是,挑选N-1条不产生回路最短的边。Kruskal算法算是最直接的表达了这个思想:在剩余边中挑选一条最短的边,看是否产生回路,是放弃,不是选定然后重复这个步骤。

5 最短距离
在许多应用领域,带权图都被用来描述某个网络,比如通信网络、交通网络。这种情况下,各边的权重就对应于两点之间通信的成本或交通费用。此时,一类典型的问题就是:在任意指定的两点之间如果存在通路,那么最小的消费是多少。这类问题实际上就是带权图中两点之间最短路径的问题。
对于求解最短路径问题:A)有时应当是有向图:如同一信道两个方向的信息流量不同,会造成信息从终端A到B和从终端B到A所需延时不同。B)有时应当是无向图:如从城市A到B和从城市B到A的公路长度都一样。

定理:最短路径的子路径也是最短路径。

5.1 求从某个源点到其余各点的最短路径
迪克斯特拉Dijkstra算法执行规程:S 、V - S 是图的2个顶点集合;S是已求出的最短路径的终点集合;V - S 是尚未求出最短路径的终点集合。

1) 初始化:

S = {s}; distance(s) = 0; distancer(ui) = w(s, ui)或∞ ,(ui ∈ V-S);

2) 选择distance(uk) = min{distance(ui)|ui ∈ V-S }, uk为下一条最短路径的终点;

3) S = S ∪ { uk }

4) 以uk 为”中转”,修正V-S中各个顶点distance;

distancer(ui) = min{ distance(ui), distance(ui) + w(uk , ui)} (ui ∈ V-S)

5) 重复2) -4)步|V| -1 次

Path对象:

public class Path {
private int distance;  //起点与终点的距离
private Vertex start;  //起点信息
private Vertex end;  //终点信息
private LinkedList pathInfo;//起点到终点的完整路径
public Path() {
this(Integer.MAX_VALUE,null,null);
}
public Path(int distance, Vertex start, Vertex end) {
this.distance = distance;
this.start = start;
this.end = end;
pathInfo = new LinkedListDLNode();
}
//判断起点与终点之间是否存在路径
public boolean hasPath() {
return distance!=Integer.MAX_VALUE&&start!=null&&end!=null;
}
//求路径长度
public int pathLength(){
if (!hasPath()) return -1;
else if (start==end) return 0;
else return pathInfo.getSize()+1;
}

//get&set methods
public void setDistance(int dis){ distance = dis;}
public void setStart(Vertex v){ start = v;}
public void setEnd(Vertex v){ end = v;}
public int getDistance(){ return distance;}
public Vertex getStart(){ return start;}
public Vertex getEnd(){ return end;}
public Iterator getPathInfo(){
return pathInfo.elements();
}

//清空路经信息
public void clearPathInfo(){
pathInfo = new LinkedListDLNode();
}

//添加路径信息
public void addPathInfo(Object info){
pathInfo.insertLast(info);
}
}

关键算法 代码片段:

//求顶点v到其他顶点的最短路径
public Iterator shortestPath(Vertex v) {
LinkedList sPath = new LinkedListDLNode();
resetVexStatus();//重置图中各顶点的状态信息
Iterator it = getVertex();//初始化,将v到各顶点的最短距离初始化为由v直接可达的距离
for(it.first(); !it.isDone(); it.next()){
Vertex u = (Vertex)it.currentItem();
int weight = Integer.MAX_VALUE;
Edge e = edgeFromTo(v,u);
if (e!=null)
weight = e.getWeight();
if(u==v) weight = 0;
Path p = new Path(weight,v,u);
setPath(u, p);
}
v.setToVisited();//顶点v进入集合S,以visited=true表示属于S,否则不属于S
sPath.insertLast(getPath(v));//求得的最短路径进入链接表
for (int t=1;t<getVexNum();t++){//进行n-1次循环找到n-1条最短路径
Vertex k = selectMin(it);//中间顶点k。可能选出无穷大距离的点,但不会为空
k.setToVisited();  //顶点k加入S
sPath.insertLast(getPath(k));  //求得的最短路径进入链接表
int distK = getDistance(k);  //以k为中间顶点修改v到V-S中顶点的当前最短路径
Iterator adjIt = adjVertexs(k);  //取出k的所有邻接点
for(adjIt.first(); !adjIt.isDone(); adjIt.next()){
Vertex adjV = (Vertex)adjIt.currentItem();
Edge e = edgeFromTo(k,adjV);
if ((long)distK+(long)e.getWeight()<(long)getDistance(adjV)){//发现更短的路径
setDistance(adjV, distK+e.getWeight());
amendPathInfo(k,adjV);  //以k的路径信息修改adjV的路径信息
}
}//for
}//for(int t=1...
return sPath.elements();
}

//在顶点集合中选择路径距离最小的
protected Vertex selectMin(Iterator it){
Vertex min = null;
for(it.first(); !it.isDone(); it.next()){
Vertex v = (Vertex)it.currentItem();
if(!v.isVisited()){ min = v; break;}
}
for(; !it.isDone(); it.next()){
Vertex v = (Vertex)it.currentItem();
if(!v.isVisited()&&getDistance(v)<getDistance(min))
min = v;
}
return min;
}

//修改到终点的路径信息
protected void amendPathInfo(Vertex mid, Vertex end){
Iterator it = getPath(mid).getPathInfo();
getPath(end).clearPathInfo();
for(it.first(); !it.isDone(); it.next()){
getPath(end).addPathInfo(it.currentItem());
}
getPath(end).addPathInfo(mid.getInfo());
}

5.2 求每一对顶点之间的最短路径

Floyd弗洛伊德算法, 基本思想是:

1) 从 vi 到 vj 的所有可能存在的路径中,选出一条长度最短的路径。

2) 若< vi, vj >存在,则存在路径{ vi, vj }// 路径中不含其它顶点

3) 若< vi,v1>,< v1, vj >存在,则存在路径{ vi, v1, vj }// 路径中所含顶点序号不大于1

4) 若{ vi,…,v2}, { v2,…, vj }存在,则存在一条路径{ vi, …, v2, …vj }// 路径中所含顶点序号不大于2

5) 依次类推,则 vi 至 vj 的最短路径应是上述这些路径中,路径长度最小者。

java数据结构_笔记(5)_图的算法的更多相关文章

  1. 【Java数据结构学习笔记之二】Java数据结构与算法之栈(Stack)实现

      本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型   栈是 ...

  2. java虚拟机学习笔记(六)---垃圾收集算法

    主要讨论集中垃圾收集算法的思想及发展过程. 1.标记-清除法 最基础的收集算法是标记-清除法,算法分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,其标记过程 ...

  3. java数据结构_笔记(4)_图

    图一.概念.图: 是一种复杂的非线性数据结构.图的二元组定义: 图 G 由两个集合 V 和 E 组成,记为:G=(V, E)  其中: V 是顶点的有穷非空集合,E 是 V 中顶点偶对(称为边)的有穷 ...

  4. 【Java数据结构学习笔记之二】Java数据结构与算法之队列(Queue)实现

      本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型 ...

  5. 【Java数据结构学习笔记之一】线性表的存储结构及其代码实现

    应用程序后在那个的数据大致有四种基本的逻辑结构: 集合:数据元素之间只有"同属于一个集合"的关系 线性结构:数据元素之间存在一个对一个的关系 树形结构:数据元素之间存在一个对多个关 ...

  6. 【Java数据结构学习笔记之三】Java数据结构与算法之队列(Queue)实现

      本篇是数据结构与算法的第三篇,本篇我们将来了解一下知识点: 队列的抽象数据类型 顺序队列的设计与实现 链式队列的设计与实现 队列应用的简单举例 优先队列的设置与实现双链表实现 队列的抽象数据类型 ...

  7. java数据结构读书笔记--引论

    1 递归简论 需求:求出f(x)=2f(x-1)+x²的值.满足f(0)=0 public class Recursion { // 需求: 求出f(x)=2f(x-1)+x²的值.满足f(0)=0 ...

  8. Java之旅_高级教程_数据结构

    摘自:http://www.runoob.com/java/java-data-structures.html 了解即可 Java 数据结构 Java 工具包提供了强大的数据结构.在Java中的数据结 ...

  9. 20165325 2017-2018-2 《Java程序设计》结对编程_第二周:四则运算

    20165325 2017-2018-2 <Java程序设计>结对编程_第二周:四则运算 一.码云链接 FAO项目的码云链接; 1.Git提交日志已经实现一个功能/一个bug修复的注释说明 ...

随机推荐

  1. mybatis动态切换数据源

    (#)背景:由于业务的需求,导致需要随时切换15个数据源,此时不能low逼的去写十几个mapper,所以想到了实现一个数据源的动态切换 首先要想重写多数据源,那么你应该理解数据源的一个概念是什么,Da ...

  2. appium 常用API

    ''.appium api第二弹 锋利的python,这是初稿,2015/1/5 如有错误的地方,请同学们进行留言,我会及时予以修改,尽量整合一份ok的api 作者:Mads Spiral QQ:79 ...

  3. jmx相关资料

    http://www.ibm.com/developerworks/cn/java/j-lo-jse63/ http://blog.csdn.net/javafreely/article/detail ...

  4. [讨论] 这几天来封装Win7用户配置文件丢失的解决方法个人心得

    [讨论] 这几天来封装Win7用户配置文件丢失的解决方法个人心得 prerouting 发表于 2010-5-9 16:50:46 https://www.itsk.com/thread-36634- ...

  5. iOS CommonCrypto 对称加密 AES ecb,cbc

    CommonCrypto 为苹果提供的系统加密接口,支持iOS 和 mac 开发: 不仅限于AES加密,提供的接口还支持其他DES,3DES,RC4,BLOWFISH等算法, 本文章主要讨论AES在i ...

  6. Python之路 day2 集合的基本操作

    #!/usr/bin/env python # -*- coding:utf-8 -*- #Author:ersa ''' #集合是无序的 集合的关系测试, 增加,删除,查找等操作 ''' #列表去重 ...

  7. 使用scanner工具类来获取用户输入的信息

    使用scanner工具类来获取用户输入的成绩信息. 步骤:1.导入java.util.Scanner包 2.创建Scanner对象 3.接受并保存用户输入的值 例子:通过用户输入来获取学生成绩 pac ...

  8. Elasticsearch5.0.1 + Kibana5.0.1 + IK 5.0.1安装记录

    最近工作需要,开始研究ES,当前ES的最新版本为5.0.1,从之前的2.x的版本号一下升级到5.x,主要是之前Elastic的产品版本号因为收购等原因很乱,ES 2.X版本的和Kibana 4.x版本 ...

  9. css权值计算

    外部样式表<内部样式表<内联样式: HTML 标签选择器的权值为 1: Class 类选择器的权值为 10: ID 选择器的权值为 100: 内联样式表的权值最高 1000: !impor ...

  10. 我的毕业设计——基于安卓和.NET的笔记本电脑远程控制系统

    手机端: 电脑端:    答辩完成后会开放代码.