图(JAVA)

目录

  1. 无权图
    1. 两种底层实现
      1. 稠密图-基于矩阵
      2. 稀疏图-基于多个动态数组
    2. 连通分量
    3. 寻找路径
      1. 深度优先寻找路径
      2. 广度优先寻找路径(最短路径)
  2. 带权图
    1. 两种底层实现
      1. 稠密图
      2. 稀疏图
    2. 最小生成树
      1. Prim算法
      2. Kruskal算法
    3. 最短路径
      1. ??
      2. ??

无权图

import java.util.Iterator;

public interface Graph {

    /**
     * 返回顶点数
     * @return
     */
    int pointCount();

    /**
     * 返回边数
     * @return
     */
    int edgeCount();

    /**
     * 为两个顶点添加一条边
     * @param pointA
     * @param pointB
     */
    void addEdge(int pointA, int pointB);

    /**
     * 判断两个顶点间是否有边
     * @param pointA
     * @param pointB
     * @return
     */
    boolean hasEdge(int pointA, int pointB);

    /**
     * 通过指定顶点遍历与其相连顶点的迭代器
     * @param point
     * @return
     */
    Iterator<Integer> pointIterator(int point);
}

两种底层实现

基于矩阵的稠密图

import java.util.Iterator;

/**
 * 稠密图
 * 基于n*n矩阵
 */
public class DenseGraph implements Graph {

    /**
     * 顶点数
     */
    private int pointCount;

    /**
     * 边数
     */
    private int edgeCount;

    /**
     * 是否是有向图
     * true 表示有向图; false 表示无向图
     */
    private boolean directed;

    /**
     * n*n矩阵
     */
    private boolean[][] data;

    /**
     * 构造函数
     * @param pointCount 定点数
     * @param directed   是否有向图
     */
    public DenseGraph(int pointCount, boolean directed) {
        assert pointCount > 0;

        this.pointCount = pointCount;
        this.edgeCount = 0;
        this.directed = directed;
        this.data = new boolean[pointCount][pointCount]; // 默认都是false,表示没有边
    }

    @Override
    public int pointCount() {
        return this.pointCount;
    }

    @Override
    public int edgeCount() {
        return this.edgeCount;
    }

    @Override
    public void addEdge(int pointA, int pointB) {
        if (hasEdge(pointA, pointB)) {
            return;
        }
        data[pointA][pointB] = true;
        this.edgeCount++;
        if (!this.directed) {
            // 如果是无向图,需要指定对称矩阵的另一部分的值
            data[pointB][pointA] = true;
        }
    }

    @Override
    public boolean hasEdge(int pointA, int pointB) {
        assert pointA >= 0 && pointA < this.pointCount;
        assert pointB >= 0 && pointB < this.pointCount;

        return data[pointA][pointB];
    }

    @Override
    public Iterator<Integer> pointIterator(int point) {
        assert point >= 0 && point < this.pointCount;

        return new Iterator<Integer>() {

            private int index = -1;

            @Override
            public boolean hasNext() {
                boolean[] allEdge = data[point];
                while (index + 1 < pointCount()) {
                    index++;
                    if (allEdge[index]) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public Integer next() {
                return index;
            }
        };
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
                .append(" has ").append(this.pointCount).append(" " + "points, ")
                .append(this.edgeCount).append(" edges.").append("\n");
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        for (boolean[] line : data) {
            for (boolean edge : line) {
                stringBuilder.append(edge ? 1 : 0).append(' ');
            }
            stringBuilder.append("\n");
        }
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        return stringBuilder.toString();
    }
}

基于多个动态数组的稀疏图

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 稀疏图
 * 基于n条动态数组
 */
public class SparseGraph implements Graph {

    /**
     * 顶点数
     */
    private int pointCount;

    /**
     * 边数
     */
    private int edgeCount;

    /**
     * 是否是有向图
     * true 表示有向图; false 表示无向图
     */
    private boolean directed;

    /**
     * n个动态数组
     */
    private Object[] data;

    public SparseGraph(int pointCount, boolean directed) {
        assert pointCount > 0;

        this.pointCount = pointCount;
        this.edgeCount = 0;
        this.directed = directed;
        this.data = new Object[pointCount];
        for (int i = 0; i < this.data.length; i++) {
            this.data[i] = new ArrayList<Integer>();
        }
    }

    @Override
    public int pointCount() {
        return this.pointCount;
    }

    @Override
    public int edgeCount() {
        return this.edgeCount;
    }

    @Override
    public void addEdge(int pointA, int pointB) {
        assert pointA >= 0 && pointA < this.pointCount;
        assert pointB >= 0 && pointB < this.pointCount;

        // 支持两个顶点间存在多个边
        ((List<Integer>) this.data[pointA]).add(pointB);
        if (pointA != pointB && !directed) {
            ((List<Integer>) this.data[pointB]).add(pointA);
        }
    }

    @Override
    public boolean hasEdge(int pointA, int pointB) {
        assert pointA >= 0 && pointA < this.pointCount;
        assert pointB >= 0 && pointB < this.pointCount;

        return ((List<Integer>) this.data[pointA]).contains(pointB);
    }

    @Override
    public Iterator<Integer> pointIterator(int point) {
        assert point >= 0 && point < this.pointCount;

        return ((List<Integer>) this.data[point]).iterator();
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
                .append(" has ").append(this.pointCount).append(" " + "points, ")
                .append(this.edgeCount).append(" edges.").append("\n");
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        for (int i = 0; i < data.length; i++) {
            stringBuilder.append("point ").append(i).append(" : ").append(data[i]).append("\n");
        }
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        return stringBuilder.toString();
    }
}

连通分量

import java.util.Arrays;
import java.util.Iterator;

/**
 * 连通分量
 */
public class ConnectedComponents {

    private Graph graph;

    /**
     * 顶点是否访问过
     */
    private boolean[] visited;

    /**
     * 顶点所属分组
     * 值相等的顶点是连通的
     */
    private int[] id;

    /**
     * 连通分量个数
     */
    private int connectedComponents;

    public ConnectedComponents(Graph graph) {
        this.graph = graph;
        int pointCount = this.graph.pointCount();
        this.visited = new boolean[pointCount]; // 默认false都没访问过
        this.id = new int[pointCount];
        Arrays.fill(this.id, -1);
        this.connectedComponents = 0;

        // 深度遍历所有顶点
        for (int point = 0; point < pointCount; point++) {
            if (!visited[point]) {
                dfs(point);
                connectedComponents++;
            }
        }
    }

    private void dfs(int point) {
        // 将该顶点的访问状态置为已访问
        visited[point] = true;
        // 将该顶点置为指定分组
        id[point] = connectedComponents;

        // 遍历与该顶点相连的所有点
        Iterator<Integer> pointIterator = graph.pointIterator(point);
        while (pointIterator.hasNext()) {
            int next = pointIterator.next();
            if (!visited[next]) {
                dfs(next);
            }
        }
    }

    /**
     * 返回连通分量个数
     * @return
     */
    public int connectedComponents() {
        return this.connectedComponents;
    }

    /**
     * 判断两个顶点是否相连
     * @param pointA
     * @param pointB
     * @return
     */
    public boolean isConnected(int pointA, int pointB) {
        int pointCount = graph.pointCount();
        assert pointA >= 0 && pointA < pointCount;
        assert pointB >= 0 && pointB < pointCount;

        return id[pointA] == id[pointB];
    }

}

深度优先找路径

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;

/**
 * 深度优先遍历
 */
public class DepthFirstSearch {

    private Graph graph;

    /**
     * 起始顶点
     */
    private int source;

    /**
     * 维护一份映射关系 当前顶点索引 -> 前一个顶点索引
     */
    private int[] fromIndex;

    public DepthFirstSearch(Graph graph, int source) {
        int pointCount = graph.pointCount();
        assert source >= 0 && source < pointCount;

        this.graph = graph;
        this.source = source;
        this.fromIndex = new int[pointCount];
        Arrays.fill(this.fromIndex, -1);

        dfs(source);
    }

    private void dfs(int point) {
        Iterator<Integer> pointIterator = graph.pointIterator(point);
        while (pointIterator.hasNext()) {
            int next = pointIterator.next();
            if (this.source != next && fromIndex[next] == -1) {
                fromIndex[next] = point;
                dfs(next);
            }
        }
    }

    public boolean hasPath(int target) {
        assert target >= 0 && target < this.graph.pointCount();

        return this.fromIndex[target] != -1;
    }

    public List<Integer> path(int target) {
        if (!hasPath(target)) {
            return Arrays.asList();
        }
        Stack<Integer> stack = new Stack<>();
        int fromIndex = target;
        while (fromIndex != -1) {
            stack.push(fromIndex);
            fromIndex = this.fromIndex[fromIndex];
        }

        List<Integer> path = new ArrayList<>();
        while (!stack.isEmpty()) {
            path.add(stack.pop());
        }
        return path;
    }

    public String showPath(int target) {
        if (!hasPath(target)) {
            return "the path from " + this.source + " to " + target + " not exists.";
        }
        List<Integer> path = path(target);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("the path from ").append(this.source).append(" to ").append(target).append(" is: ");
        for (int i = 0; i < path.size(); i++) {
            stringBuilder.append(path.get(i));
            if (i < path.size() - 1) {
                stringBuilder.append(" -> ");
            }
        }
        return stringBuilder.toString();
    }

}

广度优先找路径(无权图的最短路径)

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;

/**
 * 广度优先遍历
 */
public class BreadthFirstSearch {

    private Graph graph;

    /**
     * 起始顶点
     */
    private int source;

    /**
     * 维护一份映射关系 当前顶点索引 -> 前一个顶点索引
     */
    private int[] fromIndex;

    /**
     * 相对于起始顶点的层数
     */
    private int[] level;

    public BreadthFirstSearch(Graph graph, int source) {
        int pointCount = graph.pointCount();
        assert source >= 0 && source < pointCount;

        this.graph = graph;
        this.source = source;
        this.fromIndex = new int[pointCount];
        Arrays.fill(this.fromIndex, -1);
        this.level = new int[pointCount];
        Arrays.fill(this.level, -1);

        this.level[source] = 0;
        bfs(source);
    }

    private void bfs(int source) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(source);

        while (!queue.isEmpty()) {
            int poll = queue.poll();
            Iterator<Integer> pointIterator = this.graph.pointIterator(poll);
            while (pointIterator.hasNext()) {
                int next = pointIterator.next();
                if (next != source && this.fromIndex[next] == -1) {
                    this.fromIndex[next] = poll;
                    this.level[next] = this.level[poll] + 1;
                    queue.add(next);
                }
            }
        }

    }

    public boolean hasPath(int target) {
        assert target >= 0 && target < this.graph.pointCount();

        return fromIndex[target] != -1;
    }

    public List<Integer> path(int target) {
        if (!hasPath(target)) {
            return Arrays.asList();
        }

        Stack<Integer> stack = new Stack<>();
        int point = target;
        while (point != -1) {
            stack.push(point);
            point = this.fromIndex[point];
        }

        List<Integer> path = new ArrayList<>();
        while (!stack.isEmpty()) {
            path.add(stack.pop());
        }

        return path;
    }

    public int level(int target) {
        assert target >= 0 && target < this.graph.pointCount();

        return this.level[target];
    }

    public String showPath(int target) {
        if (!hasPath(target)) {
            return "the path from " + this.source + " to " + target + " not exists.";
        }
        List<Integer> path = path(target);
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("the path from ").append(this.source).append(" to ").append(target).append(" is: ");
        for (int i = 0; i < path.size(); i++) {
            stringBuilder.append(path.get(i));
            if (i < path.size() - 1) {
                stringBuilder.append(" -> ");
            }
        }
        return stringBuilder.toString();
    }

}

带权图

import java.util.Iterator;

/**
 * 加权图
 */
public interface WeightedGraph {

    /**
     * 返回顶点数
     * @return
     */
    int pointCount();

    /**
     * 返回边数
     * @return
     */
    int edgeCount();

    /**
     * 为两个顶点添加一条边
     * @param pointA
     * @param pointB
     * @param weight
     */
    void addEdge(int pointA, int pointB, double weight);

    /**
     * 判断两个顶点间是否有边
     * @param pointA
     * @param pointB
     * @return
     */
    boolean hasEdge(int pointA, int pointB);

    /**
     * 通过指定顶点遍历与其相连顶点的迭代器
     * @param point
     * @return
     */
    Iterator<Edge> pointIterator(int point);

}

/**
 * 边
 */
class Edge {
    private int pointA;
    private int pointB;
    private double weight;

    public Edge(int pointA, int pointB, double weight) {
        this.pointA = pointA;
        this.pointB = pointB;
        this.weight = weight;
    }

    /**
     * 顶点A
     * @return
     */
    public int pointA() {
        return this.pointA;
    }

    /**
     * 顶点B
     * @return
     */
    public int pointB() {
        return this.pointB;
    }

    /**
     * 权重
     * @return
     */
    public double weight() {
        return this.weight;
    }

    /**
     * 返回另一个顶点
     * @param point
     * @return
     */
    public int otherPoint(int point) {
        assert point == this.pointA || point == this.pointB;
        return point == this.pointA ? this.pointB : this.pointA;
    }

    @Override
    public String toString() {
        return pointA + "--" + weight + "-->" + pointB;
    }
}

带权图的两种底层实现

稠密带权图

import java.util.Iterator;
import java.util.Objects;

/**
 * 稠密图
 * 基于n*n矩阵
 */
public class WeightedDenseGraph implements WeightedGraph {

    /**
     * 顶点数
     */
    private int pointCount;

    /**
     * 边数
     */
    private int edgeCount;

    /**
     * 是否是有向图
     * true 表示有向图; false 表示无向图
     */
    private boolean directed;

    /**
     * n*n矩阵
     */
    private Edge[][] data;

    /**
     * 构造函数
     * @param pointCount 顶点数
     * @param directed   是否有向图
     */
    public WeightedDenseGraph(int pointCount, boolean directed) {
        assert pointCount > 0;

        this.pointCount = pointCount;
        this.edgeCount = 0;
        this.directed = directed;
        this.data = new Edge[pointCount][pointCount]; // 默认都是NULL,表示没有边
    }

    @Override
    public int pointCount() {
        return this.pointCount;
    }

    @Override
    public int edgeCount() {
        return this.edgeCount;
    }

    @Override
    public void addEdge(int pointA, int pointB, double weight) {
        if (hasEdge(pointA, pointB)
                && data[pointA][pointB].weight() == weight) {
            return;
        }
        data[pointA][pointB] = new Edge(pointA, pointB, weight);
        this.edgeCount++;
        if (!this.directed) {
            // 如果是无向图,需要指定对称矩阵的另一部分的值
            data[pointB][pointA] = new Edge(pointB, pointA, weight);
        }
    }

    @Override
    public boolean hasEdge(int pointA, int pointB) {
        assert pointA >= 0 && pointA < this.pointCount;
        assert pointB >= 0 && pointB < this.pointCount;

        return Objects.nonNull(data[pointA][pointB]);
    }

    @Override
    public Iterator<Edge> pointIterator(int point) {
        assert point >= 0 && point < this.pointCount;

        return new Iterator<Edge>() {

            private int index = -1;

            private Edge[] allEdge = data[point];

            @Override
            public boolean hasNext() {
                while (index + 1 < pointCount()) {
                    index++;
                    if (Objects.nonNull(allEdge[index])) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public Edge next() {
                return allEdge[index];
            }
        };
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
                .append(" has ").append(this.pointCount).append(" " + "points, ")
                .append(this.edgeCount).append(" edges.").append("\n");
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        for (Edge[] line : data) {
            for (Edge edge : line) {
                stringBuilder.append(Objects.nonNull(edge) ? edge.weight() : "NULL").append(' ');
            }
            stringBuilder.append("\n");
        }
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        return stringBuilder.toString();
    }
}

稀疏带权图

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

/**
 * 稀疏图
 * 基于n条动态数组
 */
public class WeightedSparseGraph implements WeightedGraph {

    /**
     * 顶点数
     */
    private int pointCount;

    /**
     * 边数
     */
    private int edgeCount;

    /**
     * 是否是有向图
     * true 表示有向图; false 表示无向图
     */
    private boolean directed;

    /**
     * n个动态数组
     */
    private List<Edge>[] data;

    public WeightedSparseGraph(int pointCount, boolean directed) {
        assert pointCount > 0;

        this.pointCount = pointCount;
        this.edgeCount = 0;
        this.directed = directed;
        this.data = new List[pointCount];
        for (int i = 0; i < this.data.length; i++) {
            this.data[i] = new ArrayList<>();
        }
    }

    @Override
    public int pointCount() {
        return this.pointCount;
    }

    @Override
    public int edgeCount() {
        return this.edgeCount;
    }

    @Override
    public void addEdge(int pointA, int pointB, double weight) {
        assert pointA >= 0 && pointA < this.pointCount;
        assert pointB >= 0 && pointB < this.pointCount;

        // 支持两个顶点间存在多个边
        this.data[pointA].add(new Edge(pointA, pointB, weight));
        this.edgeCount++;
        if (pointA != pointB && !directed) {
            this.data[pointB].add(new Edge(pointB, pointA, weight));
        }
    }

    @Override
    public boolean hasEdge(int pointA, int pointB) {
        assert pointA >= 0 && pointA < this.pointCount;
        assert pointB >= 0 && pointB < this.pointCount;

        return this.data[pointA].contains(pointB);
    }

    @Override
    public Iterator<Edge> pointIterator(int point) {
        assert point >= 0 && point < this.pointCount;

        return this.data[point].iterator();
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
                .append(" has ").append(this.pointCount).append(" " + "points, ")
                .append(this.edgeCount).append(" edges.").append("\n");
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        for (int i = 0; i < data.length; i++) {
            stringBuilder.append("point ").append(i).append(" : ").append(data[i]).append("\n");
        }
        for (int i = 0; i < this.pointCount; i++) {
            stringBuilder.append("~@");
        }
        stringBuilder.append("\n");
        return stringBuilder.toString();
    }
}

最小生成树

import java.util.List;

/**
 * 最小生成树
 */
public interface MinimumSpanningTree {

    /**
     * 获取某带权图的最小生成树
     * @return
     */
    List<Edge> mst();

    /**
     * 最小生成树相应的权值
     * @return
     */
    double mstWeight();

}

Prim算法实现最小生成树

理论基础:切分定理

切分定理:给定任意切分,横切边中权值最小的边必然属于最小生成树

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;

/**
 * 最小生成树
 * Minimum Spanning Tree
 */
public class LazyPrimMST {

    /**
     * 带权图
     */
    private WeightedGraph graph;

    /**
     * 存储已遍历边的小顶堆
     */
    private PriorityQueue<Edge> minHeap;

    /**
     * 顶点的访问情况
     */
    private boolean[] visited;

    /**
     * 最小生成树
     */
    private List<Edge> mst;

    /**
     * 最小生成树的权重
     */
    private double mstWeight;

    public LazyPrimMST(WeightedGraph graph) {
        int pointCount = graph.pointCount();
        this.graph = graph;
        this.minHeap = new PriorityQueue<>((e1, e2) -> {
            if (e1.weight() == e2.weight()) {
                return 0;
            }
            if (e1.weight() > e2.weight()) {
                return 1;
            }
            return -1;
        });
        this.visited = new boolean[pointCount]; // 默认都未访问过
        this.mst = new ArrayList<>(pointCount - 1); // 最小生成树的边数为顶点数减一
        this.mstWeight = 0.0D;

        visit(0);
        while (!this.minHeap.isEmpty()) {
            Edge edge = this.minHeap.poll();
            if (visited[edge.pointA()] == !visited[edge.pointB()]) {
                this.mst.add(edge);
                if (visited[edge.pointA()]) {
                    visit(edge.pointB());
                } else {
                    visit(edge.pointA());
                }
            }
        }
        for (Edge edge : this.mst) {
            this.mstWeight = new BigDecimal(this.mstWeight).add(new BigDecimal(edge.weight()))
                    .setScale(2, BigDecimal.ROUND_DOWN)
                    .doubleValue();
        }
    }

    private void visit(int point) {
        visited[point] = true;
        Iterator<Edge> pointIterator = this.graph.pointIterator(point);
        while (pointIterator.hasNext()) {
            Edge edge = pointIterator.next();
            if (visited[edge.otherPoint(point)]) {
                continue;
            }
            this.minHeap.add(edge);
        }
    }

    public List<Edge> mst() {
        return this.mst;
    }

    public double mstWeight() {
        return this.mstWeight;
    }
}

优化后的Prim算法,需要用到索引堆(JAVA)

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import com.cedar.algorithms.heap.IndexHeap;

/**
 * 最小生成树
 * Minimum Spanning Tree
 */
public class PrimMST implements MinimumSpanningTree {

    /**
     * 带权图
     */
    private WeightedGraph graph;

    /**
     * 存储已遍历边的小顶堆
     */
    private IndexHeap<Edge> minHeap;

    /**
     * 边比较器
     */
    private Comparator<Edge> edgeComparator;

    /**
     * 顶点的访问情况
     */
    private boolean[] visited;

    /**
     * 最小生成树
     */
    private List<Edge> mst;

    /**
     * 最小生成树的权重
     */
    private double mstWeight;

    public PrimMST(WeightedGraph graph) {
        int pointCount = graph.pointCount();
        this.graph = graph;
        this.edgeComparator = (e1, e2) -> {
            if (e1.weight() == e2.weight()) {
                return 0;
            }
            if (e1.weight() > e2.weight()) {
                return 1;
            }
            return -1;
        };
        // 需要一个小顶堆
        this.minHeap = new IndexHeap<>(pointCount, Collections.reverseOrder(this.edgeComparator));
        this.visited = new boolean[pointCount]; // 默认都未访问过
        this.mst = new ArrayList<>(pointCount - 1); // 最小生成树的边数为顶点数减一
        this.mstWeight = 0.0D;

        visit(0);
        while (!this.minHeap.isEmpty()) {
            int edgeIndex = this.minHeap.extractTopIndex();
            this.mst.add(this.minHeap.getElement(edgeIndex));
            visit(edgeIndex);
        }
        for (Edge edge : this.mst) {
            this.mstWeight = new BigDecimal(this.mstWeight).add(new BigDecimal(edge.weight()))
                    .setScale(2, BigDecimal.ROUND_DOWN)
                    .doubleValue();
        }
    }

    private void visit(int point) {
        visited[point] = true;
        Iterator<Edge> pointIterator = this.graph.pointIterator(point);
        while (pointIterator.hasNext()) {
            Edge edge = pointIterator.next();
            int otherPoint = edge.otherPoint(point);
            if (visited[otherPoint]) {
                continue;
            }
            Edge heapEdge = this.minHeap.getElement(otherPoint);
            if (heapEdge == null) {
                this.minHeap.insert(otherPoint, edge);
            } else if (this.edgeComparator.compare(edge, heapEdge) < 0) {
                this.minHeap.change(otherPoint, edge);
            }
        }
    }

    @Override
    public List<Edge> mst() {
        return this.mst;
    }

    @Override
    public double mstWeight() {
        return this.mstWeight;
    }
}

Kruskal算法实现最小生成树

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;

import com.cedar.algorithms.uf.UnionFind;
import com.cedar.algorithms.uf.UnionFindBaseSizePathCompressionV1;

public class KruskalMST implements MinimumSpanningTree {

    /**
     * 带权图
     */
    private WeightedGraph graph;

    /**
     * 存储所有边的小顶堆
     */
    private PriorityQueue<Edge> minHeap;

    /**
     * 边比较器
     */
    private Comparator<Edge> edgeComparator;

    /**
     * 并查集
     */
    private UnionFind unionFind;

    /**
     * 最小生成树
     */
    private List<Edge> mst;

    /**
     * 最小生成树的权重
     */
    private double mstWeight;

    public KruskalMST(WeightedGraph graph) {
        int pointCount = graph.pointCount();
        int edgeCount = graph.edgeCount();
        this.graph = graph;
        this.unionFind = new UnionFindBaseSizePathCompressionV1(pointCount); // 并查集
        this.mst = new ArrayList<>(pointCount - 1);
        this.mstWeight = 0.0D;
        this.edgeComparator = (e1, e2) -> {
            if (e1.weight() == e2.weight()) {
                return 0;
            }
            if (e1.weight() > e2.weight()) {
                return 1;
            }
            return -1;
        };
        this.minHeap = new PriorityQueue<>(edgeCount, this.edgeComparator);
        for (int i = 0; i < pointCount; i++) {
            Iterator<Edge> edgeIterator = this.graph.pointIterator(i);
            while (edgeIterator.hasNext()) {
                Edge next = edgeIterator.next();
                // 注意,这里只处理无向图
                if (next.pointA() < next.pointB()) {
                    this.minHeap.add(next);
                }
            }
        }
        while (!this.minHeap.isEmpty()) {
            Edge poll = this.minHeap.poll();
            // 使用并查集
            if (this.unionFind.isConnected(poll.pointA(), poll.pointB())) {
                continue;
            }
            this.mst.add(poll);
            this.unionFind.union(poll.pointA(), poll.pointB());
        }
        for (Edge edge : this.mst) {
            this.mstWeight = new BigDecimal(Double.toString(this.mstWeight))
                    .add(new BigDecimal(Double.toString(edge.weight()))).doubleValue();
        }
    }

    @Override
    public List<Edge> mst() {
        return this.mst;
    }

    @Override
    public double mstWeight() {
        return this.mstWeight;
    }

}

最短路径

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值