tarjan算法非递归实现求强连通分量

本文介绍了使用Tarjan算法求解有向图和无向图的强连通分量,该算法源于华为软件挑战赛的一个题目,目标是在带权重的有向图中寻找长度为3-7的环路。文章提供了Java和C++的实现,其中C++版本采用了一维存储图并实现了非递归的Tarjan算法。
摘要由CSDN通过智能技术生成

 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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值