package Algorithms;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Stack;
public class Graphic {
public static class 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;
}
}
private int size;//图的顶点数
private boolean isDirection;//是否为有向图true,
private boolean visit[];
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,boolean isDirection){
adj=new Vertex[verNum];
p=new Vertex[verNum];
size=verNum;
this.isDirection=isDirection;
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, 0, null);
}
}
/**
* 创建图 只插入所有节点即可创建 图的连接表表示
* x y分别表示弧头,和 弧尾
*/
public void insertE(int x,int y)
{
Vertex temp=adj[x];
while(temp.next!=null)
temp=temp.next;
temp.next=new Vertex(y, 0, null);
if(isDirection==false)
{
if(adj[y]==null)
adj[y]=new Vertex(y, 0, null);
Vertex temp1=adj[y];
while(temp1.next!=null)
temp1=temp1.next;
temp1.next=new Vertex(x, 0, null);
}
}
/** 广度优先遍历
* @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);
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]);
}
}
}
/**
* 拓扑排序(对于无环图)
* 深度优先遍历的按 结束时间从大到小排序
* 结束时间大的表示 为最前面的节点
*/
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]);
}
}
public static void main(String []args)
{
Graphic g=new Graphic(9, true);
g.insertE(0, 1);
g.insertE(0, 6);
g.insertE(1, 6);
g.insertE(1, 2);
g.insertE(7, 6);
g.insertE(5, 2);
g.insertE(5, 4);
g.insertE(4, 3);
g.insertE(2, 3);
g.insertE(8, 8);
//g.DFS();
g.topologicalSort();
}
}