随笔 - 171  文章 - 0  评论 - 0  阅读 - 62466

分布图最短路径算法比较

用户维护好仓区的点和线,生成分布图时,用户任意选取两个点,后端求出当前最短路径。

假设图G(m, n),m个顶点,n条边

算法对比:

  1. floyd算法
    时间复杂度o(m3)
    缺点:时间复杂度过高
  2. dijkstra算法
    时间复杂度o(m2),使用优先队列可以降到o(m * logm)
    邻接矩阵存储:适合稠密图
    邻接表存储:适合稀疏图
    缺点:不适合负权场景

项目业务场景不存在边为负权,并且大部分为稀疏图,所以采用dijkstra算法,使用邻接表存储各个节点离初始点的最短距离,通过给点增加前置节点属性保存最短路径轨迹。

点对象:

复制代码
public class Point extends BaseComponent {
    private static final long serialVersionUID = 4160598807426369809L;

    /**
     * 所属组件id
     */
    private Long componentId;

    private String uuid;

    /**
     * 所属组件类型
     */
    private int componentType;

    private List<Line> lineList;

    /**
     * 初始节点到当前节点最短路径的上一个节点
     */
    private Point prePoint;

    /**
     * 初始节点到该节点的最短距离
     */
    private Double minDistance;
复制代码

基础组件对象

复制代码
public class BaseComponent implements Serializable {

    private static final long serialVersionUID = 1L;

    protected Long id;

    /**
     * 左上角X坐标
     */
    protected Double rendX;

    /**
     * 左上角Y坐标
     */
    protected Double rendY;

    /**
     * 配送中心编号
     */
    protected Integer distributionNo;
    /**
     * 配送中心名称
     */
    protected String distributionName;

    /**
     * 仓号
     */
    protected Integer wareNo;

    /**
     * 仓库名称
     */
    protected String wareName;

    /**
     * 0 删除 1 新增  2更新
     */
    protected Integer operateType;

    /**
     * 修改人
     */
    protected String updateUser;

    /**
     * 创建人
     */
    protected String createUser;

    /**
     * 创建时间
     */
    protected Date createTime;

    /**
     * 修改时间
     */
    protected Date updateTime;

    /**
     * 删除标识(0:删除 1:正常)
     */
    protected Integer yn;

    /**
     * 时间戳
     */
    protected Date ts;
复制代码

不使用优先队列:

复制代码
    private List<String> findMinDistance(List<Point> pointList, String startUUid, String endUUid) {
        Point startPoint = null;
        for (Point point : pointList) {
            if (point.getUuid().equals(startUUid)) {
                startPoint = point;
                break;
            }
        }
        if (startPoint != null) {
            //起点到各个点的最短距离
            Map<String, Double> pointDistanceMap = new HashMap<>();
            //起点到各个点的最短路径的前置节点
            Map<String, String> prePointMap = new HashMap<>();
            //各个点的遍历状态
            Map<String, Boolean> pointStateMap = new HashMap<>();
            pointStateMap.put(startUUid, false);
            //顶点关联的线
            Map<String, List<Line>> pointLineMap = pointList.stream()
                    .filter(point -> CollectionUtils.isNotEmpty(point.getLineList()))
                    .collect(Collectors.toMap(Point::getUuid, Point::getLineList, (v1, v2) -> v2));
            //初始化起点的边
            for (Line line : startPoint.getLineList()) {
                prePointMap.put(line.getUuid(), startUUid);
                pointDistanceMap.put(line.getUuid(), line.getDistance());
            }
            if (!pointDistanceMap.isEmpty()) {
                //离初始节点距离最近点
                Map.Entry<String, Double> minDistancePoint = pointDistanceMap.entrySet().stream().min(Comparator.comparing(Map.Entry::getValue)).get();
                //离初始节点距离最近点的uuid
                String minDistancePointUUid = minDistancePoint.getKey();
                //minDistancePointUUid.equals(endUUid)说明走了到终点 pointDistanceMap.isEmpty()说明走不到终点
                while (!minDistancePointUUid.equals(endUUid) && !pointDistanceMap.isEmpty()) {
                    for (Line line : pointLineMap.get(minDistancePointUUid)) {
                        String currentLineEndPointUUid = line.getUuid();
                        //当前线的终点没有走过
                        if (pointStateMap.get(currentLineEndPointUUid) == null || pointStateMap.get(currentLineEndPointUUid)) {
                            //当前线的终点到起点的已有路径距离
                            Double currentEndPointDistance = pointDistanceMap.get(currentLineEndPointUUid);
                            //如果从当前线走,初始点到该线终点的距离 = 该线起点到初始点的最短距离 + 该线距离
                            Double distance = minDistancePoint.getValue() + line.getDistance();
                            if (currentEndPointDistance == null) {
                                prePointMap.put(currentLineEndPointUUid, minDistancePointUUid);
                                pointDistanceMap.put(currentLineEndPointUUid, distance);
                            } else {
                                //当前线的终点到起点的已有路径距离 > 从当前线走,初始点到该线终点的距离
                                if (currentEndPointDistance > distance) {
                                    //更改终点前置节点
                                    prePointMap.put(currentLineEndPointUUid, minDistancePointUUid);
                                    //更改终点最短距离
                                    pointDistanceMap.put(currentLineEndPointUUid, distance);
                                }
                            }
                        }
                    }
                    //去掉当前最短距离的节点
                    pointDistanceMap.remove(minDistancePointUUid);
                    //设置状态为不可走
                    pointStateMap.put(minDistancePointUUid, false);
                    if (!pointDistanceMap.isEmpty()) {
                        //找到离初始节点距离最近点
                        minDistancePoint = pointDistanceMap.entrySet().stream().min(Comparator.comparing(Map.Entry::getValue)).get();
                        //离初始节点距离最近点的uuid
                        minDistancePointUUid = minDistancePoint.getKey();
                    }
                }
                if (minDistancePointUUid.equals(endUUid)) {
                    List<String> list = new ArrayList<>();
                    while (prePointMap.containsKey(endUUid)) {
                        list.add(endUUid);
                        endUUid = prePointMap.get(endUUid);
                    }
                    list.add(endUUid);
                    Collections.reverse(list);
                    return list;
                }

            }
        }

        return null;
    }

    public static void main(String[] args) {
        List<Point> pointList = new ArrayList<>();
        Point pointA = new Point("A");
        Point pointB = new Point("B");
        Point pointC = new Point("C");
        Point pointD = new Point("D");
        Point pointE = new Point("E");
        Point pointF = new Point("F");
        Point pointG = new Point("G");
        List<Line> lineListA = new ArrayList<>();
        lineListA.add(new Line("B", 5D));
        lineListA.add(new Line("C", 2D));
        pointA.setLineList(lineListA);
        List<Line> lineListB = new ArrayList<>();
        lineListB.add(new Line("A", 5D));
        lineListB.add(new Line("D", 1D));
        lineListB.add(new Line("E", 6D));
        pointB.setLineList(lineListB);
        List<Line> lineListC = new ArrayList<>();
        lineListC.add(new Line("A", 2D));
        lineListC.add(new Line("D", 6D));
        lineListC.add(new Line("F", 8D));
        pointC.setLineList(lineListC);
        List<Line> lineListD = new ArrayList<>();
        lineListD.add(new Line("B", 6D));
        lineListD.add(new Line("C", 1D));
        lineListD.add(new Line("E", 1D));
        lineListD.add(new Line("F", 2D));
        pointD.setLineList(lineListD);
        List<Line> lineListE = new ArrayList<>();
        lineListE.add(new Line("B", 6D));
        lineListE.add(new Line("D", 1D));
        lineListE.add(new Line("G", 7D));
        pointE.setLineList(lineListE);
        List<Line> lineListF = new ArrayList<>();
        lineListF.add(new Line("C", 8D));
        lineListF.add(new Line("D", 2D));
        lineListF.add(new Line("G", 3D));
        pointF.setLineList(lineListF);
        List<Line> lineListG = new ArrayList<>();
        lineListG.add(new Line("F", 3D));
        lineListG.add(new Line("E", 7D));
        pointG.setLineList(lineListG);
        pointList.add(pointA);
        pointList.add(pointB);
        pointList.add(pointC);
        pointList.add(pointD);
        pointList.add(pointE);
        pointList.add(pointF);
        pointList.add(pointG);

        List list = new MapServiceImpl().findMinDistance(pointList, "A", "G");
        if (!list.isEmpty()) {
            list.forEach(
                    str -> {
                        System.out.println(str + " ");
                    }
            );
        }
    }




复制代码

使用优先队列:

复制代码
public List<String> findMinDistanceNew(List<Point> pointList, String startUUid, String endUUid) {
        Point startPoint = null;
        for (Point point : pointList) {
            if (point.getUuid().equals(startUUid)) {
                startPoint = point;
                break;
            }
        }
        if (startPoint != null) {
            //起点到各个点的最短距离
            Map<String, Double> pointDistanceMap = new HashMap<>(pointList.size());
            //起点到各个点的最短路径的前置节点
            Map<String, String> prePointMap = new HashMap<>(pointList.size());
            //各个点的遍历状态
            Map<String, Boolean> pointStateMap = new HashMap<>(pointList.size());
            pointStateMap.put(startUUid, false);
            //顶点关联的线
            Map<String, List<Line>> pointLineMap = new HashMap<>(pointList.size());
            pointList.forEach(
                    point -> {
                        if (CollectionUtils.isNotEmpty(point.getLineList())) {
                            pointLineMap.put(point.getUuid(), point.getLineList());
                        }
                    }
            );
            Queue<Point> pointDistanceQueue = new PriorityQueue(pointLineMap.size(), new Comparator<Point>() {
                @Override
                public int compare(Point o1, Point o2) {
                    return Double.compare(o1.getMinDistance(), o2.getMinDistance());
                }
            });
            //初始化起点的边
            for (Line line : startPoint.getLineList()) {
                prePointMap.put(line.getUuid(), startUUid);
                pointDistanceMap.put(line.getUuid(), line.getDistance());
                pointDistanceQueue.offer(new Point(line.getUuid(), line.getDistance()));
            }
            if (!pointDistanceQueue.isEmpty()) {
                //离初始节点距离最近点+去掉当前最短距离的节点
                Point minPoint = pointDistanceQueue.poll();
                //离初始节点距离最近点的uuid
                String minDistancePointUUid = minPoint.getUuid();
                //minDistancePointUUid.equals(endUUid)说明走到了终点 pointDistanceQueue.isEmpty()说明走不到终点
                while (!minDistancePointUUid.equals(endUUid) && !pointDistanceQueue.isEmpty()) {
                    for (Line line : pointLineMap.get(minDistancePointUUid)) {
                        String currentLineEndPointUUid = line.getUuid();
                        //当前线的终点没有走过
                        if (pointStateMap.get(currentLineEndPointUUid) == null || pointStateMap.get(currentLineEndPointUUid)) {
                            //当前线的终点到起点的已有路径距离
                            Double currentEndPointDistance = pointDistanceMap.get(currentLineEndPointUUid);
                            //如果从当前线走,初始点到该线终点的距离 = 该线起点到初始点的最短距离 + 该线距离
                            Double distance = minPoint.getMinDistance() + line.getDistance();
                            if (currentEndPointDistance == null) {
                                prePointMap.put(currentLineEndPointUUid, minDistancePointUUid);
                                pointDistanceQueue.offer(new Point(line.getUuid(), distance));
                                pointDistanceMap.put(currentLineEndPointUUid, distance);
                            } else {
                                //当前线的终点到起点的已有路径距离 > 从当前线走,初始点到该线终点的距离
                                if (currentEndPointDistance > distance) {
                                    //更改终点前置节点
                                    prePointMap.put(currentLineEndPointUUid, minDistancePointUUid);
                                    //更改终点最短距离
                                    pointDistanceMap.put(currentLineEndPointUUid, distance);
                                    pointDistanceQueue.offer(new Point(line.getUuid(), distance));
                                }
                            }
                        }
                    }
                    //设置状态为不可走
                    pointStateMap.put(minDistancePointUUid, false);
                    if (!pointDistanceQueue.isEmpty()) {
                        //找到离初始节点距离最近点
                        minPoint = pointDistanceQueue.poll();
                        //离初始节点距离最近点的uuid
                        minDistancePointUUid = minPoint.getUuid();
                    }
                }
                if (minDistancePointUUid.equals(endUUid)) {
                    List<String> list = new ArrayList<>();
                    while (prePointMap.containsKey(endUUid)) {
                        list.add(endUUid);
                        endUUid = prePointMap.get(endUUid);
                    }
                    list.add(endUUid);
                    Collections.reverse(list);
                    return list;
                }

            }
        }

        return null;
    }
复制代码

使用优先队列对比测试(空间换时间)

图G(7,10)

代码预热后测试(类加载是按需加载,第一次跑会进行类加载过程,会消耗较多时间,所以先空跑两次预热后再进行时间的统计):

 
调用次数 10 1000 2000
优化前消耗时间 1ms 28ms 35ms
优化后消耗时间 <1ms 12ms 15ms
posted on   zhengbiyu  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示