图论 邻接链表存储 BFS DFS 拓扑排序 最小生成树 KRUSKAL PRIM
来源:互联网 发布:apache spark 安装 编辑:程序博客网 时间:2024/05/10 20:56
package Algorithms;import java.util.ArrayList;import java.util.Arrays;import java.util.Comparator;import java.util.LinkedList;import java.util.PriorityQueue;import java.util.Stack;import java.util.TreeSet;import javax.swing.text.html.HTMLDocument.Iterator;class MinProQueue<T extends Comparable<? super T>>{ int heapSize; T[] heap; int capacity; public MinProQueue(int capaticty) { this.capacity=capaticty; heapSize=0; //因为泛型擦除,泛型不能实例化,只能创建Object,然后再强制类型转换为数组 //这里不能使用new Object 因为没有comparable,要使用直接父类comparable heap=(T[])new Comparable[capaticty]; } public boolean isEmpty() { if(heapSize==0) return true; return false; } /** * 最小优先队列的维护 */ public void heapfy(int i) { if(i>=heapSize&&i<0) { System.out.println("要维护的节点错误"); return ; } int left=2*i+1; int right=2*i+2; int min=i; //寻找i与其两个孩子的最小值 if(left<heapSize&&heap[left].compareTo(heap[min])==-1) min=left; if(right<heapSize&&heap[right].compareTo(heap[min])==-1) min=right; if(min!=i) { T temp=heap[min]; heap[min]=heap[i]; heap[i]=temp; heapfy(min); } } /** * 建立最小优先队列 */ public void insert(T ele) { if(heapSize>=capacity) { System.out.println("最小优先队列已满!"); return ; } heap[heapSize]=ele; heapSize++; int child=heapSize-1; int parent=(heapSize/2)-1; while(parent>=0&&heap[parent].compareTo(heap[child])>0) { T temp=heap[parent]; heap[parent]=heap[child]; heap[child]=temp; child=parent; parent=(child+1)/2-1; } } public T extractMin() { if(heapSize<=0) { System.out.println("没有元素"); return null; } T min=heap[0]; heapSize--; heap[0]=heap[heapSize]; heap[heapSize]=min; heapfy(0); return min; } public void decreaseKey(T a) { int child; for( child=0;child<capacity;child++) if(a==heap[child]) break; if(child==capacity) { System.out.println("没有此元素"); return ; } int parent=((child+1)/2)-1; while(parent>=0&&heap[parent].compareTo(heap[child])>0) { T temp=heap[parent]; heap[parent]=heap[child]; heap[child]=temp; child=parent; parent=((child+1)/2)-1; } }}public class Graphic { public static class Vertex implements Comparable<Vertex>{ public int num;//节点编号 public int weight;//边的权重 或者最小生成树中节点到此顶点的权重最小值 //在最短路径中存放距离源节点的最短距离 public Vertex next;//指向顶点的下一个弧尾,即下一条边 public Vertex(int num,int weight,Vertex next) { this.num=num; this.weight=weight; this.next=next; } @Override public int compareTo(Vertex o) { if(this.weight>o.weight) return 1; else if(this.weight==o.weight) return 0; else return -1; } } //边节点应用于 KRUSKAL的最小生成树 的边按权值排序 public static class Edge{ public int head;//弧头 public int tail;//弧尾 public int w;//边的权重 public Edge(int head,int tail,int weight){ this.head=head; this.tail=tail; this.w=weight; } } //数组比较器 class edgeComparator implements Comparator<Edge>{ @Override public int compare(Edge o1, Edge o2) { // TODO Auto-generated method stub if(o1.w==o2.w) return 0; else if(o1.w>o2.w) return 1; else return -1; } } //创建存放边的数组 创建从大到小排序的比较器 private Edge arc[]; private int size;//图的顶点数 private int Esize;//边的数目 private int countE;//边计数器 用于arc数组的添加元素 private boolean isDirection;//是否为有向图true, private boolean visit[];//标志遍历时节点是否被访问 private boolean[] inQueue;//prim算法每个顶点是否在队列中的标志位,初始状态顶点都在队列 private Vertex p[];//存放每个顶点的前驱 private int distance[];//广度优先遍历时存放与根节点的距离 private Vertex adj[]; public int startT[];//深度优先遍历的开始时间 public int endT[];//深度优先遍历的结束时间 用于拓扑排序 int time;//深度优先遍历时记录每个节点的开始和结束时间的计时器 LinkedList<Vertex>topologicalSort=new LinkedList<Vertex>();//存放拓扑排序的数组 public Graphic(int verNum,int Esize ,boolean isDirection){ adj=new Vertex[verNum]; p=new Vertex[verNum]; size=verNum;//顶点个数 this.Esize=Esize;//边的数目 countE=0; this.isDirection=isDirection; arc=new Edge[Esize]; inQueue=new boolean[size]; visit=new boolean[verNum]; distance=new int [verNum]; endT=new int [size]; startT=new int [size]; //构造顶点,防止下面的有向图中顶点不是弧头,从而顶点数组会为空指针 for(int i=0;i<size;i++) { adj[i]=new Vertex(i, 1000, null); } } /** * 创建图 只插入所有节点即可创建 图的连接表表示 * x y分别表示弧头,和 弧尾 */ public void insertE(int x,int y,int w) { Vertex temp=adj[x]; while(temp.next!=null) temp=temp.next; temp.next=new Vertex(y, w, null); if(isDirection==false) { Vertex temp1=adj[y]; while(temp1.next!=null) temp1=temp1.next; temp1.next=new Vertex(x, w, null); } //这个只是为了使用kruskal算法,prim算法用不到 arc[countE++]=new Edge(x, y, w); } /** 广度优先遍历 * @param x:图的根节点 开始遍历 * 存放在队列中的节点都是将要访问的所以设置为true ,以免重复添加 * 可用于求无权图上的最短路径 */ public void BFS(int x) { //初始化 Arrays.fill(visit, false); Arrays.fill(distance, -1); Arrays.fill(p, null); java.util.Queue<Integer> q=new LinkedList<Integer>(); distance[x]=0;//初始化距离根节点的深度 visit[x]=true; q.add(x); while(!q.isEmpty()) { //这里遍历顶点的边,要找到定点数组中的引用,才能找到相应的邻接表,否则 //只在一个顶点的边上乱转,所以 使用顶点的编号 在队列中表示 不要用引用 //使用引用,还要找到相应的定点数组上的引用 Vertex parent=adj[q.poll()]; System.out.print(parent.num+" "); Vertex temp=parent.next; while(temp!=null)//在向队列中添加节点时就把此节点的前驱和深度进行赋值 { if(visit[temp.num]==false) { p[temp.num]=parent; visit[temp.num]=true;//添加访问标志进入队列之后就要添加标志防止 //重复进入 重复访问 distance[temp.num]=distance[p[temp.num].num]+1; q.add(temp.num); } temp=temp.next; } } } /** * 深度优先遍历 与拓扑排序 */ public void DFS() { //初始化 Arrays.fill(visit, false); Arrays.fill(distance, -1); Arrays.fill(p, null); Arrays.fill(endT, 0); Arrays.fill(startT, 0); time=0; for(int i=0;i<size;i++)//避免有的图为不强连通的 { if(visit[i]==false)//注意不能把这个条件写到for循环中,因为遇到2 //已经访问退出循环不再迭代,应该放在循环体中 DFSvisit3(i); } } //递归实现 public void DFSvisit(int i) { time++;//开始访问的时间 startT[i]=time; visit[i]=true; System.out.print(i+" "); Vertex temp=adj[i].next; while(temp!=null) { if(visit[temp.num]==false) DFSvisit(temp.num); temp=temp.next; } time++;//访问完成+1 ,记录 结束的时间 如果不加就和开始的时间一样了 endT[i]=time; topologicalSort.addFirst(adj[i]); } //使用栈实现 public void DFSvisit2(int i) { Stack<Integer> s=new Stack<Integer>(); s.push(i); while(!s.isEmpty()) { time++; int num=s.pop(); visit[num]=true; System.out.print(num+" "); Vertex temp=adj[num].next; while(temp!=null) { if(visit[temp.num]==false) s.push(temp.num); temp=temp.next; } } } //使用栈实现 public void DFSvisit3(int i) { Stack<Integer> s=new Stack<Integer>(); s.push(i); System.out.print("深度优先遍历:"); while(!s.isEmpty()) { time++; int num=s.peek(); if(visit[num]==false) { visit[num]=true; startT[num]=time; System.out.print(num+" "); Vertex temp=adj[num].next; while(temp!=null) { if(visit[temp.num]==false) s.push(temp.num); temp=temp.next; } } else { endT[num]=time; s.pop(); topologicalSort.addFirst(adj[num]); } } System.out.println(); } /** * 拓扑排序(对于无环图) * 深度优先遍历的按 结束时间从大到小排序 * 结束时间大的表示 为最前面的节点 */ public void topologicalSort() { DFS(); for(int i=0;i<topologicalSort.size();i++) { int num=topologicalSort.get(i).num; System.out.println("第"+i+"执行的"+"节点"+num+": "+startT[num]+"/"+endT[num]); } } /** * MST :最小生成树的生成 * kruskal算法,初始状态为 含有顶点个数的森林, * 每个顶点存放在8个不相交数据结构中(对于发现的最短边,用于判断有无回路), * 判断是否有无回路 如果一条边所包含的 两个顶点 在一个集合中,说明两个顶点已经 * 连接,此边若在添加就会产生回路 */ public String MSTKRUSKAL() { String mst=""; //对每一个顶点 创建不相交集合 ////参见 http://blog.csdn.net/jiaomingliang/article/details/41322285 DisjointSet<Integer>a=new DisjointSet<Integer>(size); for(int i=0;i<size;i++) //第一个参数为这个集合所在的数组中的位置,第二个参数为此元素的标识 a.makeSet(i, i); Arrays.sort(arc, new edgeComparator()); for(int i=0;i<Esize;i++) { Edge temp=arc[i]; if(a.findSet(temp.head)!=a.findSet(temp.tail))//不在一个集合说明为安全边 { mst+="E("+temp.head+","+temp.tail+") "; a.union(temp.head, temp.tail); } } return mst ; } /** * 最小生成树的prim算法(借助 最小优先队列实现)还可以使用两个数组实现, * 道理一样,这里就不实现了 * 先将所有节点放入最小优先队列,然后距离生成树距离最近的出队,更新还 * 没在树中节点(在队列中的)到现有生成树的距离, * 不管是用 邻接矩阵还是 邻接链表 都必须记录每个树外的结点距离树中最近 * 的那个节点这里称为他的父亲,输出在最小生成树中的边 */ public String MSTPRIM(int x) { String mst=""; Arrays.fill(p, null); Arrays.fill(inQueue, true); //这个队列作用找出最小的权重的节点 MinProQueue<Vertex>q=new MinProQueue<Graphic.Vertex>(size); adj[x].weight=0; p[x]=adj[x]; for(int i=0;i<size;i++) q.insert(adj[i]); while(!q.isEmpty()) { int num=q.extractMin().num; mst+="E("+p[num].num+","+num+") "; inQueue[num]=false; Vertex temp=adj[num].next; while(temp!=null)//遍历所有的边 {//顶点不在树中 并且顶点距离树的最短距离比旧树的小 则更新距离 if(inQueue[temp.num]==true&&temp.weight<adj[temp.num].weight) { p[temp.num]=adj[num];//用于输出最小生成树中的边 adj[temp.num].weight=temp.weight; q.decreaseKey(adj[temp.num]); } temp=temp.next; } } return mst; } /** *对数组节点中的顶点,进行单源路径 初始化 *前驱节点为null 距离源节点的距离(weight)为正无穷 *并且令源节点 的weight=0 */ public void initialzieSource(int s) { for(int i=0;i<size;i++) { adj[i].weight=10000; p[i]=null; } adj[s].weight=0; } /**松弛操作 * 对此边进行松弛操作: * 也是更新节点weight 不同于prim算法,更新的是此节点距离树中结点的最短距离) * 这里是更新此节点 距离源节点的最短路径 * v.d>u.d+w 则更新为 v.d=u.d+w * @param u v:表示一条边 对v到s的最短距离进行松弛 * @param w:此边的权重 * */ public void relax(int u,int v,int w) { if(adj[v].weight>adj[u].weight+w) { adj[v].weight=adj[u].weight+w; p[v]=adj[u]; } } /**单元最短路径的 Bellman-Ford算法 时间复杂度:O(VE) * 思路:对条边进行size-1次松弛操作,然后在判断是否存在环, * 若存在则返回false,否则为true * s:为根节点 */ public boolean bellmanFord(int s) { initialzieSource(s); for(int i=0;i<size-1;i++) { for(int j=0;j<size;j++)//遍历每一条边 { Vertex temp=adj[j].next; while(temp!=null) { relax(j,temp.num,temp.weight); temp=temp.next; } } } for(int j=0;j<size;j++)//遍历每一条边判断是否含有权值为负的回路 回路的权值 { //之和为负 Vertex temp=adj[j].next; while(temp!=null) { if(adj[temp.num].weight>adj[j].weight+temp.weight) return false; temp=temp.next; } } return true; } /** * 有向无环图的最短路径问题 * 有环图不能进行拓扑排序 * 利用拓扑排序的顶点顺序只对 每条边进行一次松弛就可以了 * 是线性时间算法 :v+E */ public void dagshorttestpath(int s) { DFS(); int i; //图中每个节点都必须算出距离 源节点s的最短距离 initialzieSource(s); for(i=0;i<topologicalSort.size();i++) { int num=topologicalSort.get(i).num; Vertex temp=adj[num].next; while(temp!=null) { relax(num,temp.num,temp.weight); temp=temp.next; } } } /** * 贪心算法 DIJKSTRA算法 * 要求 :所有边的权重都为非负值。若有负值不满足贪心算法 * S集合为出队列组成的集合,即已经求出最短路径的顶点,然后再对其作为弧头 * 的边进行松弛,在寻找队列中此时距离s最小的值,此对应节点一定是求出最短路径的 * 这种贪心策略一定正确。 */ public void dijkstra(int s) { initialzieSource(s); MinProQueue<Vertex>q=new MinProQueue<Graphic.Vertex>(size); for(int i=0;i<size;i++) q.insert(adj[i]); //S=空; while(!q.isEmpty()) { Vertex u =q.extractMin();//出队列就是为了更新寻找要松弛的边 //S=S∪{u}; Vertex v=u.next; while(v!=null) { relax(u.num,v.num,v.weight); q.decreaseKey(adj[v.num]); v=v.next; } } } public void printPath(int s,int v) { System.out.print("最短路径:"); print(s,v); } public void print(int s,int v) { if(v==s) System.out.print(s+" "); else { print(s, p[v].num); System.out.print(v+" "); } } public static void main(String []args) { Graphic g=new Graphic(5, 9,true); g.insertE(0, 1,6); g.insertE(0, 2,7); g.insertE(1, 2,8); g.insertE(2, 3,9); g.insertE(3, 4,7); //g.insertE(4, 1,-2); g.insertE(1, 4,5); g.insertE(3, 0,2); //g.insertE(2, 4,-3); //g.insertE(1, 3,-4); g.dijkstra(0); g.printPath(0, 3); //System.out.println(g.bellmanFord(0)); }}
最短单源路径图:
1 0
- 图论 邻接链表存储 BFS DFS 拓扑排序 最小生成树 KRUSKAL PRIM
- 第二周---最小生成树(Kruskal,Prim)、拓扑排序
- 数据结构 邻接矩阵+邻接表+bfs+dfs+prim+Kruskal综合
- 最小生成树prim+邻接表优化
- Prim、Kruskal最小生成树
- 最小生成树---Prim---Kruskal
- 最小生成树 Kruskal&&Prim
- 最小生成树 Prim Kruskal
- Kruskal/prim--最小生成树
- 最小生成树 PRIM KRUSKAL
- 图的遍历(dfs、bfs、最短路、最小生成树、拓扑排序)
- 最小生成树Prim算法实现(采用邻接表存储)C++实现
- prim算法构造最小生成树(邻接表和数组两种存储方式实现)
- 最小生成树Prim算法实现(采用邻接表存储)C++实现
- 邻接表的最小生成树kruskal算法
- 图论 之 最小生成树 (Kruskal and Prim)
- 【图论】最小生成树之prim算法与kruskal算法
- 图论--最小生成树总结(Prim&&Kruskal)
- SQL 中的 图 树 层次结构[转自http://blog.csdn.net/feixianxxx/article/details/4753783]
- Cocos2d-x 3.2 android平台新手开发环境配置教程
- 微信大屏幕(一)
- Android API Level与sdk版本对照表
- jquery,js代码摘要
- 图论 邻接链表存储 BFS DFS 拓扑排序 最小生成树 KRUSKAL PRIM
- c语言小游戏 贪吃蛇
- VIM 命令集锦
- ACE线程管理机制
- Android Card Filp Animations
- Intellij IDEA maven clean失败
- 日语学习之沪江N4基础 20141122 -3
- UVA10006----CarmichaelNumbers
- 想成为大牛的首要原则:团队第一,请勿单打独斗