数据结构:第五章(二)

视频链接:https://www.bilibili.com/video/BV1HQ4y1d7th

视频范围P184 - P193

1.普里姆算法

7个村庄(A,B,C,D,E,F,G),现在需要修路把7个村庄连通,各个村庄的距离用边线表示(权),比如A-B距离5公里,问:如何修路保证各个村庄都能连通,并且总的修建公路总里程最短?
在这里插入图片描述
普利姆(Prim)算法求最小生成树,也就是在包含n 个顶点的连通图中,找出只有(n-1)条边包含所有n个顶点的连通子图,也就是所谓的极小连通子图

图类:

package kruskal.prim;

public class MGraph {
    //顶点数量
    int verxs;
    //顶点
    char[] data;
    //每条边的权重
    int[][] weight;

    public MGraph(int verxs) {
        this.verxs = verxs;
        data = new char[verxs];
        weight = new int[verxs][verxs];
    }
}

最小树类:

package kruskal.prim;

import java.util.Arrays;

public class MinTree {
    public void createGraph(MGraph graph,int verxs,char[] data,int[][] weight){
        for (int i = 0; i < verxs; i++) {
            graph.data[i] = data[i];
            for (int j = 0; j < verxs; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
    }

    //查看邻接矩阵
    public void showGraph(MGraph graph){
        for (int[] data : graph.weight){
            System.out.println(Arrays.toString(data));
        }
    }

    //从v进去
    public void prim(MGraph graph,int v){
        //标记被访问过的,0表示没有访问过,1表示访问过
        int[] visied = new int[graph.verxs];
        visied[v] = 1;

        int x = -1;
        int y = -1;

        int minWeight = 10000;

        //最小生成树 边 = 顶点 - 1
        for (int i = 1; i < graph.verxs; i++) {
            //被访问过的
            for (int j = 0; j < graph.verxs; j++) {
                //没有被访问过的
                for (int k = 0; k < graph.verxs; k++) {
                    if (visied[j] == 1 && visied[k] == 0 && graph.weight[j][k] < minWeight){
                        minWeight = graph.weight[j][k];
                        x = j;
                        y = k;
                    }
                }
            }
            System.out.println(graph.data[x] + "--->" + graph.data[y] + " = " + minWeight);
            visied[y] = 1;
            minWeight = 10000;
        }
    }
}

测试类:

package kruskal.prim;

public class PrimAlgorithm {
    public static void main(String[] args) {
        char[] data = {'A','B','C','D','E','F','G'};

        int verxs = data.length;

        int[][] weight = {
                {10000,5,7,10000,10000,10000,2},
                {5,10000,10000,9,10000,10000,3},
                {7,10000,10000,10000,8,10000,10000},
                {10000,9,10000,10000,10000,4,10000},
                {10000,10000,8,10000,10000,5,4},
                {10000,10000,10000,4,5,10000,6},
                {2,3,10000,10000,4,6,10000},
        };

        MGraph mGraph = new MGraph(verxs);
        MinTree minTree = new MinTree();
        minTree.createGraph(mGraph,verxs,data,weight);

        minTree.showGraph(mGraph);
        minTree.prim(mGraph,0);
    }
}

运行结果:

在这里插入图片描述

2.克鲁斯卡尔算法[此代码有问题!]

克鲁斯卡尔算法的核心思想是:在带权连通图中,不断地在边集合中找到最小的边,如果该边满足得到最小生成树的条件,就将其构造,直到最后得到一颗最小生成树。
注意:当顶点数和边数不是特别大的时候可以用克鲁斯卡尔算法
使用克鲁斯卡尔算法生成最小生成树

边类

package kruskal.krusk;

public class Edge implements Comparable<Edge>{
    int begin;
    int end;
    int weight;

    @Override
    public int compareTo(Edge o) {
        return this.weight -  o.weight;
    }
}

图类

package kruskal.krusk;

public class Graph {
    Edge[] edges;
    int[][] array;
}

测试类

package kruskal.krusk;

import java.util.Arrays;

public class KruskalAlg {
    public static void main(String[] args) {
        Graph graph = new Graph();
        int[][] array = new int[7][7];

        //手动初始化数组
        for (int i = 0; i < 7; i++) {
            for (int j = 0; j < 7; j++) {
                if (i == j){
                    array[i][j] = 0;
                }else{
                    array[i][j] = Integer.MAX_VALUE;
                }
            }
        }

        array[0][1] = 7;
        array[0][3] = 5;
        array[1][2] = 8;
        array[1][3] = 9;
        array[2][4] = 5;
        array[3][4] = 15;
        array[4][5] = 8;
        array[4][6] = 9;
        array[5][6] = 11;

        graph.array = array;
        int k = 0;

        //初始化边的数组
        Edge[] edge = new Edge[9];
        for (int i = 0; i < edge.length; i++) {
            Edge edge1 = new Edge();
            edge[i] = edge1;
        }

        //将边的数组传入begin end weight
        for (int i = 0; i < 6; i++) {
            for (int j = i + 1; j < 7; j++) {
                if (array[i][j] < Integer.MAX_VALUE){
                    edge[k].begin = i;
                    edge[k].end = j;
                    edge[k].weight = array[i][j];
                    k++;
                }
            }
        }

        graph.edges = edge;
        Arrays.sort(edge);
        kruskal(graph);

    }

    //是否会构成回路
    public static int findParentRoot(int target,int[] parent){
        while (parent[target] > 0){
            target = parent[target];
        }
        return target;
    }

    //kruskal算法
    public static void kruskal(Graph graph){
        Edge[] edges = graph.edges;
        int[][] arr = graph.array;
        int[] parent = new int[7];

        for (int i = 0; i < edges.length; i++) {
            Edge edge = edges[i];
            int begin = findParentRoot(edge.begin, parent);
            int end = findParentRoot(edge.end, parent);

            if (begin != end){
                System.out.println(String.format("(%d,%d)---->%d",begin,end, edge.weight));
                parent[begin] = end;
            }
        }
    }
}

在这里插入图片描述

运行结果:
在这里插入图片描述

3.迪杰斯特拉算法

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个结点到其他结点的最短路径。它的主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
在这里插入图片描述

package kruskal;

public class DijstraAlgorithm {

    public static int MaxValue = 100000;

    public static void main(String[] args) {
        //顶点数
        int vertex = 7;

        //边数
        int edge = 10;

        //权重
        int[][] matrix = new int[vertex][vertex];

        for (int i = 0; i < vertex; i++) {
            for (int j = 0; j < vertex; j++) {
                matrix[i][j] = MaxValue;
            }
        }

        matrix[0][1] = 6;
        matrix[1][2] = 5;
        matrix[0][3] = 2;
        matrix[3][1] = 7;
        matrix[3][4] = 5;
        matrix[1][5] = 3;
        matrix[4][5] = 5;
        matrix[5][2] = 3;
        matrix[4][6] = 1;

        //原顶点 入口
        int source = 0;

        dijstra(matrix,source);
    }

    //迪杰斯特拉算法
    public static void dijstra(int[][] matrix,int source){

        //最短路径的长度
        int[] shorttest = new int[matrix.length];

        //判断是否能够算出该顶点的最短距离 0没有对比过 1对比过
        int[] visited = new int[matrix.length];

        //拼接有效的路径
        String[] path = new String[matrix.length];

        //初始化路径
        for (int i = 0; i < matrix.length; i++) {
            path[i] = new String(source + "--->" + i);
        }

        //原点的有效距离是0
        shorttest[source] = 0;
        //有效的路径
        visited[source] = 1;

        for (int i = 1; i < matrix.length; i++) {
            int min = Integer.MAX_VALUE;
            int index = -1;
            for (int j = 0; j < matrix.length; j++) {
                if (visited[j] == 0 && matrix[source][j] < min){
                    min = matrix[source][j];
                    index = j;
                }
            }
            shorttest[index] = min;
            visited[index] = 1;

            for (int j = 0; j < matrix.length; j++) {
                if (visited[j] == 0 && matrix[source][index] + matrix[index][j] < matrix[source][j]){
                    matrix[source][j] = matrix[source][index] + matrix[index][j];
                    path[j] = path[index] + "--->" + j;
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            if (i != source){
                if (shorttest[i] == MaxValue){
                    System.out.println(source + "到" + i + "不可以直接到达");
                }else {
                    System.out.println(source + "到" + i + "的最短路径是:" + path[i] + "最短距离是:" + shorttest[i]);
                }
            }
        }
    }
}

运行结果:
在这里插入图片描述

4.弗洛伊德算法

弗洛伊德算法选取某个节点k作为i到j需要经过的中间节点,通过比较d(i,k)+d(k,j)和现有d(i,j)的大小,将较小值更新为路径长度,对k节点的选取进行遍历,以得到在经过所有节点时i到j的最短路径长度,通过不断加入中间点的方式更新最短路径。同时在 path 数组中存储i到j所经过的中间节点k,用于最后递归调用输出路径结果。

package kruskal;

public class FloydAlgorithm {
    public static int MaxValue = 100000;

    //中间结点存放的数组
    public static int[][] path;

    public static void main(String[] args) {
        //顶点数
        int vertex = 7;

        //边数
        int edge = 10;

        //权重
        int[][] matrix = new int[vertex][vertex];

        for (int i = 0; i < vertex; i++) {
            for (int j = 0; j < vertex; j++) {
                matrix[i][j] = MaxValue;
            }
        }

        path = new int[matrix.length][matrix.length];

        matrix[0][1] = 6;
        matrix[1][2] = 5;
        matrix[0][3] = 2;
        matrix[3][1] = 7;
        matrix[3][4] = 5;
        matrix[1][5] = 3;
        matrix[4][5] = 5;
        matrix[5][2] = 3;
        matrix[4][6] = 1;

        floyd(matrix);
    }

    public static void floyd(int[][] matrix){
        //防止int类型数组中默认值是0,因为顶点也存在0的数据
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                path[i][j] = -1;
            }
        }

        //计算i通过中间顶点k到达j的路径
        for (int k = 0; k < matrix.length; k++) {
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix.length; j++) {
                    if (matrix[i][k] + matrix[k][j] < matrix[i][j]){
                        matrix[i][j] = matrix[i][k] + matrix[k][j];
                        path[i][j] = k;
                    }
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                if (i != j){
                    if (matrix[i][j] == MaxValue){
                        System.out.println(i + "到" + j + "不可以直接到达");
                    }else {
                        System.out.println(i + "到" + j + "的最短路径是:" + matrix[i][j]);
                        System.out.print("最短路径:"+ i + "----->");
                        searchPath(i,j);
                        System.out.println(j);
                    }
                }
            }
        }
    }

    public static void searchPath(int i,int j){
        int m = path[i][j];
        if (m == -1){
            return;
        }
        searchPath(i,m);
        System.out.print(m + "--->");
        searchPath(m,j);
    }
}

运行结果:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.马踏棋盘算法

马踏棋盘算法也被称为骑士周游问题
将马随机放在国际象棋的8×8棋盘 Board[0-7] [0-7]的某个方格中,马按走棋规则(马走日字)进行移动。要求每个方格只进入一次,走遍棋盘上全部 64个方格

package kruskal;

import java.awt.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class HorseChessboard {
    private static int X;
    private static int Y;
    //判断是否已经访问过
    private static boolean[] visited;

    //表示棋盘中的所有的位置是否都被标记了
    private static boolean finished;

    public static void main(String[] args) {
        //描述棋盘
        X = 8;
        Y = 8;
        int row = 1;
        int column = 1;

        int[][] chessBoard = new int[X][Y];
        visited = new boolean[X*Y];

        long start = System.currentTimeMillis();
        chessboard(chessBoard,row - 1,column - 1,1);
        long end = System.currentTimeMillis();

        System.out.println(end - start);//输出为:12
    }

    public static void chessboard(int[][] chessBoard,int row,int column,int step){
        chessBoard[row][column] = step;
        visited[row * X +column] = true;

        //每跳一步,都要计算当前所在的位置下一步要跳的位置数量,可以存储在集合中去
        ArrayList<Point> ps = next(new Point(column, row));

        //预判未来要跳的次数,减少回溯
        sort(ps);

        while (!ps.isEmpty()){
            Point nextPiont = ps.remove(0);
            if (!visited[nextPiont.y * X + nextPiont.x]){
                chessboard(chessBoard,nextPiont.y,nextPiont.x,step + 1);
            }
        }

        //计算当前已经走的次数是否已经走完了,完成任务
        if (step < X*Y && !finished){
            chessBoard[row][column] = 0;
            visited[row * X + column] = false;
        }else {
            finished = true;
        }

    }

    /**
     *
     * @param curPoint 当前所在的位置
     * @return 下一次跳的位置集合
     */
    public static ArrayList<Point> next(Point curPoint){
        ArrayList<Point> ps = new ArrayList<>();
        Point point = new Point();

        if ((point.x = curPoint.x - 2) >= 0 && (point.y = curPoint.y - 1) >= 0){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x - 1) >= 0 && (point.y = curPoint.y - 2) >= 0){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x + 1) < X  && (point.y = curPoint.y - 2) >= 0){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x + 2) < X  && (point.y = curPoint.y - 1) >= 0){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x + 2) < X  && (point.y = curPoint.y + 1) < Y){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x + 1) < X  && (point.y = curPoint.y + 2) < Y){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x - 1) >= 0  && (point.y = curPoint.y + 2) < Y){
            ps.add(new Point(point));
        }

        if ((point.x = curPoint.x - 2) >= 0  && (point.y = curPoint.y + 1) < Y){
            ps.add(new Point(point));
        }
        return ps;
    }

    public static void sort(ArrayList<Point> ps){
        ps.sort(new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                int count1 = next(o1).size();
                int count2 = next(o2).size();

                if (count1 < count2){
                    return -1;
                }else if (count1 == count2){
                    return 0;
                }else {
                    return 1;
                }
            }
        });
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值