java实现常用图操作

/**
  * <p>Title: 图的遍历、最小生成树、最短路径</p>
  *
  *
  * <p>Description:
  *
  * 采用邻接矩阵做为图存储结构,有权无向图,不相连的值为 -1
  *
  * 图的遍历中深度遍历采用递归方法,广度遍历使用辅助队列
  *
  * 最小生成树采用克鲁斯卡尔(Kruskal)算法,使用一数组记录节点的连通情况
  *
  * 图的最短路径采用迪杰斯特拉(Dijkstra)算法,使用队列记录依次途经的路径
  *
  * 修改:
  * 深度、广度遍历中 在列出第n节点相邻的节点并一一入栈/队时,相邻最近的优先入栈/队
  *
  * </p>
  *
  * <p>Website: www.hartech.cn </p>
  * <p>Page: http://www.hartech.cn/blog/blogview.asp?logID=88 </p>
  *
  * <p>Date: 2006-09-08 </p>
  */
public class Graph {

    // 图邻接矩阵
    private static int[][] arcs;
    // 节点数
    private static int num;
    // 记录是否访问过
    private static boolean[] hasVisit;

    // 记录访问过的前一个节点,用于统计线路总长度
    static int pre;

    // 深度优先遍历,给出图邻接矩阵和开始遍历的节点
    public static void traverse_DFS(int[][] arcs_in, int begin) {
        pre = begin;
        if (arcs_in == null || arcs_in.length == 0 ||
                arcs_in.length != arcs_in[0].length || begin < 0) {
            System.err.println("wrong arcs[][] or begin!");
            return;
        }
        arcs = arcs_in;
        num = arcs.length;
        hasVisit = new boolean[num];

        DFS(begin);
    }

    private static void DFS(int begin) {
        hasVisit[begin] = true;
        Main.Q_DFS.enQueue(begin);
        Main.length_DFS += Main.arcs[pre][begin];
        pre = begin;

        int min, n = 0;
        for (int i = 1; i < num; i++) {
            // 距离最短的优先入栈
            min = Integer.MAX_VALUE;
            for (int j = 0; j < num; j++) {
                if (!hasVisit[j] && arcs[begin][j] != -1 && arcs[begin][j] < min) {
                    min = arcs[begin][j];
                    n = j;
                }
            }
            if (min == Integer.MAX_VALUE) {
                break;
            }
            else {
                DFS(n);
            }
        }
    }

    // 广度优先遍历,给出图邻接矩阵和开始遍历的节点
    public static void traverse_BFS(int[][] arcs_in, int begin) {
        pre = begin;
        if (arcs_in == null || arcs_in.length == 0 ||
                arcs_in.length != arcs_in[0].length || begin < 0) {
            System.err.println("wrong arcs[][] or begin!");
            return;
        }
        arcs = arcs_in;
        num = arcs.length;
        hasVisit = new boolean[num];
        Queue queue = new Queue();

        hasVisit[begin] = true;
        queue.enQueue(begin);
        int temp, min, n = 0;
        while (!queue.isEmpty()) {
            temp = ( (Integer) queue.deQueue()).intValue();
            for (int i = 1; i < num; i++) {
                // 距离最短的优先入队
                min = Integer.MAX_VALUE;
                for (int j = 0; j < num; j++) {
                    if (!hasVisit[j] && arcs[temp][j] != -1 && arcs[temp][j] < min) {
                        min = arcs[temp][j];
                        n = j;
                    }
                }
                if (min == Integer.MAX_VALUE) {
                    break;
                }
                else {
                    hasVisit[n] = true;
                    queue.enQueue(n);
                    Main.Q_BFS.enQueue(n);
                    Main.length_BFS += Main.arcs[pre][n];
                    pre = n;
                }
            }
        }
    }

    // 构造最小生成树,采用克鲁斯卡尔(Kruskal)算法
    // 使用一数组记录节点的连通情况
    public static void miniSpanTree(int[][] arcs_in) {
        if (arcs_in == null || arcs_in.length == 0 ||
                arcs_in.length != arcs_in[0].length) {
            System.err.println("wrong arcs[][]!");
            return;
        }
        arcs = arcs_in;
        num = arcs.length;

        // 使用一数组记录节点的连通情况
        // 如 group[0]=0 表示节点未和任何节点连通
        // group[0]=group[3]=group[5]=2 表示节点0、3、5为同一连通图内,该连通图的标识为 2
        int[] group = new int[num];
        boolean finish = false;

        int temp = 0, min, n1 = 0, n2 = 0, groupNum = 0;
        // 到全部group[n]等于同一数值,也就是处于同一连通图才结束
        while (!finish) {

            // 遍历所有路径,无向图,仅遍历矩阵上三角
            // 找出最短的且不在同一连通图的(group[n1]!=group[n2]),或两节点都未加入连通图的
            min = Integer.MAX_VALUE;
            for (int i = 0; i < num; i++) {
                for (int j = i + 1; j < num; j++) {
                    if (arcs[i][j] < min && arcs[i][j] != -1) {
                        if (group[i] != group[j] || (group[i] == 0 && group[j] == 0)) {
                            min = arcs[i][j];
                            n1 = i;
                            n2 = j;
                        }
                    }
                }
            }
            // 无路了
            if (min == Integer.MAX_VALUE) {
                break;
            }

            Main.Q_tree.enQueue(new Dimension(n1, n2));
            Main.length_tree += Main.arcs[n1][n2];

            // 加入连通图组
            if (group[n1] == 0 && group[n2] == 0) {
                groupNum++;
                group[n1] = groupNum;
                group[n2] = groupNum;
            }
            else if (group[n1] != 0 && group[n2] != 0) {
                temp = group[n2];
                for (int i = 0; i < num; i++) {
                    if (group[i] == temp) {
                        group[i] = group[n1];
                    }
                }
            }
            else {
                if (group[n1] == 0) {
                    group[n1] = group[n2];
                }
                else {
                    group[n2] = group[n1];
                }
            }

            // 到全部group[n]等于同一数值且不为0,也就是处于同一连通图才结束
            temp = 1;
            while (temp < num && group[temp] == group[0]) {
                temp++;
            }
            if (group[0] != 0 && temp == num) {
                finish = true;
            }
        }
        if (!finish) {
            System.out.println("图为非强连通图");
        }
    }

    // 图的最短路径,给出图邻接矩阵,起点,终点,打印出途经的节点
    // 采用迪杰斯特拉(Dijkstra)算法
    public static void shortestPath_DIJ(int[][] arcs_in, int begin, int end) {
        if (arcs_in == null || arcs_in.length == 0 ||
                arcs_in.length != arcs_in[0].length) {
            System.err.println("wrong arcs[][]!");
            return;
        }
        arcs = arcs_in;
        num = arcs.length;
        // 标识节点是否已找到最短路径,从begin到n 为finish[n]=true;
        boolean[] finish = new boolean[num];
        // 记录从 begin 到 n 的最短路径为 min[n]
        int[] D = new int[num];
        // 使用队列记录路径途经节点
        Queue[] queue = new Queue[num];

        // 初始化
        for (int i = 0; i < num; i++) {
            D[i] = arcs[begin][i];
            finish[i] = false;
            queue[i] = new Queue();
        }
        finish[begin] = true;
        D[begin] = -1;

        int v = 0, min = 0;
        // 一个一个循环找出最短距离(共num-1个)
        for (int i = 1; i < num; i++) {
            min = Integer.MAX_VALUE;
            // 扫描找出非final集中最小的D[]
            for (int w = 0; w < num; w++) {
                if (!finish[w] && D[w] < min && D[w] != -1) {
                    v = w;
                    min = D[w];
                }
            }

            finish[v] = true;
            // 已找到目标,退出循环
            if (v == end) {
                queue[v].enQueue(v);
                Main.Q_shortest = queue[v].clone();
                Main.length_short = D[v];
                break;
            }

            // 更新各D[]数据
            for (int w = 0; w < num; w++) {
                if (!finish[w] && arcs[v][w] != -1) {
                    if ( (arcs[v][w] + min) < D[w] || D[w] == -1) {
                        D[w] = arcs[v][w] + min;
                        queue[w] = queue[v].clone();
                        queue[w].enQueue(v);
                    }
                }
            }
            queue[v].enQueue(v);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值