tarjan算法用来求有向图和无向图的强连通分量,强连通分量的概念请自行百度。
此代码来源于华为软挑的题目,在有向带权图中找长度为3-7的环路;
tarjan算法伪代码:
tarjan(u){
DFN[u]=Low[u]=++Index // 为节点u设定次序编号和Low初值
Stack.push(u) // 将节点u压入栈中
for each (u, v) in E // 枚举每一条边
if (v is not visted) // 如果节点v未被访问过
tarjan(v) // 继续向下找
Low[u] = min(Low[u], Low[v])
else if (v in S) // 如果节点u还在栈内
Low[u] = min(Low[u], DFN[v])
if (DFN[u] == Low[u]) // 如果节点u是强连通分量的根
repeat v = S.pop // 将v退栈,为该强连通分量中一个顶点
print v
until (u== v)
}
Java实现:
//链式前向星方式存图
class Edge {
public int next; //相同起点的下一条边的位置
public int end; //边的终点
public int w; //权重
public Edge(int next, int v, int w) {
this.next = next;
this.end = v;
this.w = w;
}
public String toString() {
return " " + next + " " + end + " " + w;
}
}
public class Graph {
final int MaxEdges = 2800000;
public Edge[] edges = new Edge[MaxEdges];
public int cnt;
public List<List<Integer>> path;
public LinkedHashSet<String> strPath;
private String inputFileName;
private String outputFileName;
private Logger logger;
final private int MinLen = 3; //环的最小长度
final private int MaxLen = 7; //环的最大长度
Map<Integer, Integer> head = new HashMap<>();
Set<Integer> visited = new HashSet<>();
Set<Integer> endNodesSet;
public int dfsCount = 0;
/*-------------------------------- tarjan 变量--------------------------------------------*/
int visitTime = 0;
Deque<Integer> stack = new ArrayDeque<>();
Set<Integer> stackSet = new HashSet<>();
public List<List<Integer>> tarRes = new LinkedList<>();
Map<Integer, Integer> dfn = new HashMap<>(head.size());
Map<Integer, Integer> low = new HashMap<>(head.size());
Set<Integer> tarVisited = new HashSet<>();
int resCnt = 0;
public Graph(String inputFileName, String outputFileName){
init(inputFileName, outputFileName);
try {
loadFile();
} catch (IOException e) {
logger.info("Fail: loadFile...");
}
//---------------------------tarjan------------------------
// tarjan();
// ---------- dfs --------------
// findLoop();
// output();
// System.out.println("dfs调用次数:" + dfsCount);
}
private void Tarjan() {
long s = System.currentTimeMillis();
for (int node : head.keySet()) {
if (!endNodesSet.contains(node) || tarVisited.contains(node)) continue;
tarjan(node);
}
long e = System.currentTimeMillis();
System.out.println("tarjan time: " + (double) (e - s) / 1000);
System.out.println("强连通分量个数:" + tarRes.size());
sort(tarRes);
output("src/data/Tarjan.txt", tarRes);
}
public void init(String inputFileName, String outputFileName) {
logger = Logger.getLogger("Graph");
cnt = 0;
path = new LinkedList<>();
strPath = new LinkedHashSet<>();
this.inputFileName = inputFileName;
this.outputFileName = outputFileName;
endNodesSet = new HashSet<>();
}
//读取文件,按照链式前向星的方法为图添加边
public void loadFile() throws IOException {
long startTime = System.currentTimeMillis();
System.out.println("inputFile: " + inputFileName);
File f = new File(inputFileName);
InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream(f), "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String lineText = null;
while ((lineText = bufferedReader.readLine()) != null) {
String[] data = lineText.split(",");
addEdge(Integer.parseInt(data[0]), Integer.parseInt(data[1]), Integer.parseInt(data[2]));
}
long endTime = System.currentTimeMillis();
System.out.println("read file and create graph: " + (double) (endTime - startTime) / 1000);
System.out.println("edges num: " + cnt);
}
/*
* @Description add edge set
* @param null
* @Return
* @Author