数据结构与算法16 - 其他算法

1. 分治算法

步骤
1. 问题拆分成多个小问题,相互独立
2. 若问题较简单可直接解,否则递归的解各个子问题
3. 将各个子问题合并为原问题的解
使用分治思想的算法
排序:合并、归并排序
二分搜索、大整数乘法、棋盘覆盖
线性时间选择、最接近点对问题、循环赛日程表
汉诺塔
1.1 汉诺塔
package top.linruchang.algorithm.commonAlgorithms;

/**
 * @Classname HanoiTower
 * @Description
 * @Date 2020/4/16 22:33
 * @Created by lrc
 */
public class HanoiTower {


    public static void show(int plateNum, char firstTower, char secondTower, char thirdTower) {

        if(plateNum == 1) {
            System.out.println("第1个盘从 " + firstTower + " => " + thirdTower);
        }else {

            show(plateNum-1, firstTower, thirdTower, secondTower);

            System.out.println("第"+ plateNum +"个盘从 " + firstTower + " => " + thirdTower);

            show(plateNum-1, secondTower, firstTower, thirdTower);
        }

    }

    public static void main(String[] args) {
        HanoiTower.show(1, 'a', 'b', 'c');

        System.out.println("\n========================\n");

        HanoiTower.show(2, 'a', 'b', 'c');

        System.out.println("\n========================\n");

        HanoiTower.show(3, 'a', 'b', 'c');

        System.out.println("\n========================\n");

        HanoiTower.show(4, 'a', 'b', 'c');

    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lCG5cmSc-1587795995180)(en-resource://database/32894:1)]


2. 动态规划

步骤
1. 问题拆分成多个小问题,不是相互独立
2. 每个小问题的小解很可能基于其上一个小问题的解
3. 方式:通过填表的方式进行逐步推进,获取最优解

2.1 背包问题 - 固定容器的背包,如何能装入总价值最大的东西
背包问题分裂
1. 01背包 - 即不可装入重复的东西
2. 无限背包 - 即可以装入重复的东西 - 【无限背包可以转01背包进行处理】

2.1.1 01背包 - 不可装入重复的东西

背包可容纳为4KG的东西 - 物品不可重复

物品重量( KG )价格
吉他11500
音响43000
电脑32000


物品 \ 重量( KG )01234
00000
吉他01500150015001500
音响01500150015003000
电脑01500150020002000+1500

填表思路 - 进行动态规划

# 表格中的行排表示当前列对应的最大容量所能装的东西( 从上到下的东西 )

# 如
    (吉他,1KG):表示背包为1KG,物品有吉他 - 最大装入的价值是什么
    
    (音响、1KG):表示背包为1KG,物品有吉他、音响 - 最大装入的价值是什么
    
    (音响、4KG):表示背包为4KG,物品有吉他、音响 - 最大装入的价值是什么
    
    (电脑,1KG):表示背包为1KG,物品有吉他、音响、电脑  - 最大的装入价值是什么

    (电脑,3KG):表示背包为3KG,物品有吉他、音响、电脑  - 最大的装入价值是什么


package top.linruchang.algorithm.commonAlgorithms;

/**
 * 动态规划案例
 *
 * @Classname DynamicProgrammingDemo
 * @Description
 * @Date 2020/4/17 11:21
 * @Created by lrc
 */
public class DynamicProgrammingDemo {


    int[][] value;
    String[][] maxValueThings;
    int[] prices;
    int[] weights;
    String[] thingsName;


    DynamicProgrammingDemo() {

        prices = new int[]{1500, 300, 2000};

        weights = new int[]{1, 4, 3};

        thingsName = new String[]{"吉他", "音响", "电脑"};

        maxValueThings = new String[4][5];
        value = new int[4][5];
        for (int i = 0; i < 4; i++) {
            if (i == 0) {
                for (int j = 0; j < 5; j++) {
                    value[i][j] = 0;
                    maxValueThings[i][j] = "";
                }
            }
            value[i][0] = 0;
            maxValueThings[i][0] = "";
        }
    }


    public void dynamicMethod() {


        for (int i = 1; i < 4; i++) {
            for (int j = 1; j < 5; j++) {
                
                //如果当前物品的重量大于列对应的重量,则直接取上一个空格的结果
                if(weights[i-1] > j) {
                    value[i][j] = value[i-1][j];
                    maxValueThings[i][j] = maxValueThings[i-1][j];

                    //如果当前物品的重量小于等于列对应的重量,直接取上一个空格的结果与 当前物品+对应减少重量的空格结果
                }else {

                    int lastCurrentPrice =  value[i-1][j];
                    int currentCurrentPrice = prices[i-1] + value[i-1][j - weights[i-1]];
                    int maxValue = Math.max(lastCurrentPrice, currentCurrentPrice);


                    value[i][j] = maxValue;

                    if(maxValue == lastCurrentPrice) {
                        maxValueThings[i][j] = maxValueThings[i-1][j];
                    }else {


                        maxValueThings[i][j] = thingsName[i-1] + maxValueThings[i-1][j - weights[i-1]];
                    }

                }
            }
        }

        
        //打印表格的各个总价值
        for(int i = 0; i<value.length; i++) {
            for(int j = 0; j < value[i].length; j++) {
                System.out.printf("%-5s ", value[i][j]);
            }
            System.out.println("\n");
        }

        //打印表格的每个对应的物品
        for(int i = 0; i<maxValueThings.length; i++) {
            for(int j = 0; j < maxValueThings[i].length; j++) {
                System.out.printf("%-5s " , maxValueThings[i][j] );
            }
            System.out.println("\n");
        }

    }


    public static void main(String[] args) {
        DynamicProgrammingDemo dynamic = new DynamicProgrammingDemo();

        dynamic.dynamicMethod();
    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3OVYT2v-1587795995186)(en-resource://database/32902:1)]


3. KMP、暴力匹配算法

1. 解决模式串在文本串是否出现过,如果出现过,返回最早出现在文本串的位置

2. KMP思想: 通过一个next数组,保存模式串中前后最长公共子序列的长度,每次回溯时,通过next数组找到,前面匹配过的位置(index = index + (已匹配字符数 - next[i])),省去了大量的计算时间



[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7WSUQkr-1587795995188)(en-resource://database/32910:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fc5nouea-1587795995191)(en-resource://database/32912:1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pPmfs2kt-1587795995194)(en-resource://database/32914:1)]


next()数组例子

// next数值为:前缀=后缀  前缀的最大长度

// 需要查找的字符:ABCDABD
next[] 数组为  { 0, 0, 0, 0, 1, 2, 0 }


// 需要查找的字符:abadcababa
next[] 数组为  { 0, 0, 1, 0, 0, 1, 2, 3, 2, 3  }


// 遇到不匹配时,主字符串指针移动的下一步为 
index = index + ( pattern匹配到的字符长度L - next[L-1] )


StringMatching.java

/**
 * 暴力匹配字符串
 *
 * @Classname StringMatching
 * @Description
 * @Date 2020/4/17 16:29
 * @Created by lrc
 */
public class StringMatching {

  /*  String matchedStr;
    String pattern;

    StringMatching(String matchedStr, String pattern) {
        this.matchedStr = matchedStr;
        this.pattern = pattern;
    }
*/

    public static int violentMatch(String matchedStr, String pattern) {

        char[] matchedStrcChars = matchedStr.toCharArray();
        char[] patternChars = pattern.toCharArray();


        int index = 0;
        while (index < matchedStr.length()) {

            //如果主字符串剩余能被匹配的字符小于 被匹配的字符串长度,则直接退出
            if (matchedStr.length() - index < pattern.length()) {
                break;
            }

            int currentIndex = index;
            int index2 = 0;
            while (true) {

                //匹配字符时,主字符串、被匹配字符串指针都向前移
                if (matchedStrcChars[currentIndex] == patternChars[index2]) {
                    index2++;
                    currentIndex++;
                } else {

                    //一旦不匹配时,被匹配字符串指针重新开始,主字符串为上次记录的位置下一个指针
                    index++;
                    index2 = 0;
                    break;
                }

                if (index2 == pattern.length()) {
                    return index;
                }

            }


        }

        return -1;

    }


    public static int kmpMatch(String matchedStr, String pattern) {

        char[] matchedStrcChars = matchedStr.toCharArray();
        char[] patternChars = pattern.toCharArray();

        int[] next = getNext(pattern);

        int index = 0;
        while (index < matchedStr.length()) {

            //如果主字符串剩余能被匹配的字符小于 被匹配的字符串长度,则直接退出
            if (matchedStr.length() - index < pattern.length()) {
                break;
            }

            int currentIndex = index;
            int index2 = 0;
            while (true) {

                //匹配字符时,主字符串、被匹配字符串指针都向前移
                if (matchedStrcChars[currentIndex] == patternChars[index2]) {
                    index2++;
                    currentIndex++;
                } else {

                    //一旦不匹配时,被匹配字符串指针重新开始,主字符串为上次记录的位置下一个指针

                    //这里使用next数组进行移动主字符串得指针
                    //index = index + (已经匹配成功得pattern长度 - next对应得值)
                 if (next[index2] != 0) {
                        index += (index2 + 1) - next[index2];
                    } else {
                        index++;
                    }


                    index2 = 0;
                    break;
                }

                if (index2 == pattern.length()) {
                    return index;
                }

            }

        }
        return -1;
    }

    public static void main(String[] args) {


        System.out.println(StringMatching.violentMatch("BBC ABCDAB ABCDABCDABDE", "ABCDABD"));
        System.out.println(StringMatching.kmpMatch("BBC ABCDAB ABCDABCDABDE", "ABCDABD"));

        System.out.println(Arrays.toString("rrewr".toCharArray()));


        System.out.println("rrewr".toCharArray()[0] == "rrewr".toCharArray()[1]);

  /*      int[] next = getNext("abadcababa");
        next = getNext("abadcababa");
        System.out.println(Arrays.toString(next));*/


    }


    public static int[] getNext(String pattern) {
        int[] next = new int[pattern.length()];
        next[0] = 0;
        for (int i = 1, j = 0; i < next.length; i++) {

            //这步实在是看不太懂
            while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) {
                j = next[j - 1];
            }

            if (pattern.charAt(i) == pattern.charAt(j)) {
                j++;
            }

            next[i] = j;
        }
        return next;
    }


}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iykau3LZ-1587795995201)(en-resource://database/32904:1)]


4. 贪心算法 - 解决集合覆盖问题

贪心算法:指在对问题进行求解时,在每一步选择中都采取最好或者最优(即最有利)的选择,从而希望能够导致结果是最好或者最优的算法

注意:能得到结果,但并不是最好的 - 例如下面的电台问题,能筛选到能覆盖所有地区的地台,但并不能保证这个电台组合是价钱最便宜的


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yqZ0iCNq-1587795995204)(en-resource://database/32928:1)]

思路

  1. 查找每个电台在需要覆盖地区集合S的覆盖数 - 并记录下来
  2. 扫描完所有电台的覆盖数 - 从中找到覆盖数最大的电台,并且记录该电台到集合A中
  3. 选中最大覆盖数的电台则将 该电台的覆盖地区 从 集合S中剔除
  4. 重复上述步骤,直到S的容量为0
  5. 集合A中的电台即可以覆盖所有地区

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xGiq8e52-1587795995207)(en-resource://database/32930:1)]


/**
 * @Classname GreedyAlgorithm
 * @Description
 * @Date 2020/4/18 9:20
 * @Created by lrc
 */
public class GreedyAlgorithm {

    Map<String, Set<String>> broadcastingStation;
    Set<String> coverageAreas;

    GreedyAlgorithm(Map<String, Set<String>> broadcastingStation, HashSet<String> coverageAreas ) {
        this.broadcastingStation = broadcastingStation;
        this.coverageAreas = coverageAreas;
    }


    public ArrayList<String> getStation() {

        // 电台需要覆盖的地区
        HashSet<String> copyCoverageAreas = new HashSet<>(coverageAreas);

        // 每个电台 所有地区 需要放入到这个集合进行跟copyCoverageAreas进行并集比较
        HashSet<String> tempArea = new HashSet<>();

        // 记录每次循环 每个电台的覆盖地区数
        HashMap<String, Integer> coverageAreasNumStation = new HashMap<>();

        // 记录哪个电台覆盖的地区最广
        String maxKey = null;

        // 每次循环,将上面的maxKey记录到这里面 - 结果返回
        ArrayList<String> resultStation = new ArrayList<>(broadcastingStation.size());


        // 如果并没有覆盖完所有地区,则再次循环
        while(copyCoverageAreas.size() != 0) {

            //获取电台
            Set<String> stations = broadcastingStation.keySet();
            Iterator<String> iterator = stations.iterator();


            while(iterator.hasNext()) {
                String station = iterator.next();

                //将当前电台覆盖的地区放入临时集合
                tempArea.addAll(broadcastingStation.get(station));

                // tempArea copyCoverageAreas的并集 放入到 tempArea中
                tempArea.retainAll(copyCoverageAreas);

                // 记录当前电台覆盖的地区数,并放入coverageAreasNumStation记录
                Integer overrideAreaNum = tempArea.size();
                coverageAreasNumStation.put(station, overrideAreaNum);

                if(maxKey == null || (coverageAreasNumStation.get(maxKey) < coverageAreasNumStation.get(station) )) {
                    maxKey = station;
                }

                tempArea.clear();
            }


            resultStation.add(maxKey);

            // 选中最大的覆盖地区数,并将从broadcastingStation中剔除出来 - 等待下次循环
            if(maxKey != null) {
                copyCoverageAreas.removeAll(broadcastingStation.get(maxKey));
            }

            maxKey = null;

        }

        return resultStation;
    }


    public static void main(String[] args) {
        
        //构造测试数据
        Set<String> set1 = new HashSet<>();
        set1.add("北京");
        set1.add("上海");
        set1.add("天津");


        Set<String> set2 = new HashSet<>();
        set2.add("广州");
        set2.add("北京");
        set2.add("深圳");

        Set<String> set3 = new HashSet<>();
        set3.add("成都");
        set3.add("上海");
        set3.add("杭州");

        Set<String> set4 = new HashSet<>();
        set4.add("天津");
        set4.add("上海");

        Set<String> set5 = new HashSet<>();
        set5.add("杭州");
        set5.add("大连");

        Map<String, Set<String>> broadcastingStation = new HashMap<>();
        broadcastingStation.put("k1", set1);
        broadcastingStation.put("k2", set2);
        broadcastingStation.put("k3", set3);
        broadcastingStation.put("k4", set4);
        broadcastingStation.put("k5", set5);


        String[] areas = {"北京", "上海", "天津", "广州", "深圳", "成都", "上海", "杭州", "大连"};
        HashSet<String> coverageAreas = new HashSet<>(Arrays.asList(areas));

        GreedyAlgorithm greedyAlgorithm =  new GreedyAlgorithm(broadcastingStation, coverageAreas);

        
        //查看结果
        System.out.println(greedyAlgorithm.getStation());

    }

}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ttd93Joe-1587795995211)(en-resource://database/32926:1)]


5. 图

5.1 最小生成树(所有结点都有路可走) - Minimum Spanning Tree

概念

  1. 从一个带权的无向连通图 - 生成一颗树 - 边上权的总和为最小
最小生成树特点
1. N个顶点、N-1条边
2. 必须包含全部顶点
3. 生成树的边全部来自 提供的无向连通图中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UOvNlPoZ-1587795995214)(en-resource://database/33046:1)]


5.1.1 普里姆算法

思路:

  1. 选取某个结点为起点V,
  2. 将该结点放入一个容器中,
  3. 比较以该容器中的结点为边,
  4. 比较得到最小的边,即为需要连接的边,并将结点放入容器中
  5. 循环2,3,4 — 直到容器中包括所有结点**



只画了前三次循环的步骤 - 后面几次循环就不画了,实在太难画了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOiHWADQ-1587795995218)(en-resource://database/32936:1)]


/**
 * @Classname PrimAlgorithm
 * @Description
 * @Date 2020/4/18 15:30
 * @Created by lrc
 */
public class PrimAlgorithm {


    public static void main(String[] args) {

        //1, 结点信息
        String[] vertexs = {"A", "B", "C", "D", "E", "F", "G"};
        Integer n = vertexs.length;

        // 2. 创建带5个结点的无序带权图
        UndirectedWeightGraph graph = new UndirectedWeightGraph(n);

        // 3. 添加结点
        for (String vertex : vertexs) {
            graph.insertVertex(vertex);
        }

        // 4. 添加结点之间的权值 - 这里的权值>0表示两结点之间可以进行修路且修路的长度  =0表示不可以进行修路
        graph.insertWeight(0, 1, 5);
        graph.insertWeight(0, 2, 7);
        graph.insertWeight(0, 6, 2);
        graph.insertWeight(1, 6, 3);
        graph.insertWeight(1, 3, 9);
        graph.insertWeight(2, 4, 8);
        graph.insertWeight(3, 5, 4);
        graph.insertWeight(4, 6, 4);
        graph.insertWeight(4, 5, 5);
        graph.insertWeight(5, 6, 6);


        // 5. 显示图的矩阵
        //System.out.println(graph.getGraph());
        graph.showGrpah();


        PrimAlgorithm primAlgorithm = new PrimAlgorithm();


        primAlgorithm.prim(graph , 0);

    }


    /**
     *
     * @param graph 带有权值的无向图
     * @param n  从哪个结点开始修路 - A -> 0, B -> 1等等
     */
    public void prim(UndirectedWeightGraph graph, int n) {

        //获取结点的数量
        Integer vertexNum = graph.getVertexsNum();

        //标记是否选中该结点
        boolean[] visted = new boolean[vertexNum];

        //全部结点都标记为fasle
        for(int i = 0; i<vertexNum; i++) {
            visted[i] = false;
        }

        //存储准备修路的结点
        ArrayList<Integer> vistedVertex = new ArrayList<>(vertexNum);
        vistedVertex.add(n);


        //修路长度
        int sumLength = 0;

        while(vistedVertex.size() < vertexNum) {

            //记录下一次修的路径
            int firstVertext = -1;
            int secondVertext = -1;

            //标记当前结点的边的最短路径
            Integer minWeight = 0;

            // 获取已经准备修路的其他最小边
            Integer size = vistedVertex.size();
            for(int i = 0; i< size; i++) {

                for(int j = 0; j<vertexNum; j++) {

                    if(vistedVertex.contains(j) == false) {

                        Integer curWeight =  graph.getWeight(vistedVertex.get(i), j);

                        if((minWeight == 0 && curWeight != 0) || (curWeight != 0 && minWeight > curWeight)) {
                            minWeight = curWeight;
                            firstVertext = vistedVertex.get(i);
                            secondVertext = j;
                        }
                    }

                }

            }

            
            if(secondVertext != -1) {

                System.out.printf("修路:%s => %s 长度:%s\n",graph.getVertex(firstVertext), graph.getVertex(secondVertext), minWeight);
                sumLength = sumLength + minWeight;
                vistedVertex.add(secondVertext);

            }


        }


        System.out.println("修路总长度:" + sumLength);


    }


}




//无向带权图
class UndirectedWeightGraph {

    //存储结点
    ArrayList<String> vertexs;

    // 存储结点之间的权值 - 结点之间的关系 -- 矩阵
    int[][] edges;

    // 存储图中边的个数
    int numOfEdges;




    //初始化 - 结点个数
    UndirectedWeightGraph(int vertexsNum) {

        this.vertexs = new ArrayList<>(vertexsNum);
        this.edges = new int[vertexsNum][vertexsNum];
        this.numOfEdges = 0;

    }

    //获取结点
    public String getVertex(int n) {
        return vertexs.get(n);
    }


    //获取边的个数
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //获取结点个数
    public int getVertexsNum() {
        return this.vertexs.size();
    }

    //获取结点根据索引序号
    public String getVertexByIndex(int index) {
        return vertexs.get(index);
    }

    //添加新结点
    public void insertVertex(String vertex) {
        this.vertexs.add(vertex);
    }

    //获取两结点之间的边的权值 "A结点表示0" "B节点表示1"
    public int getWeight(int firstVertex, int secondVertext) {
        return edges[firstVertex][secondVertext];
    }


    //获取矩阵 - 即把数组Edges权值图打印出来
    public String getGraph() {
        return Arrays.deepToString(edges);
    }

    //显示矩阵
    public void showGrpah() {
        for (int[] edgeWeight : edges) {
            System.out.println(Arrays.toString(edgeWeight));
        }
    }


    //添加结点之间的关系 - 即权值 - 因为无向图的关系必须赋值数组中两个元素相同的权值
    public void insertWeight(int firstVertex, int secondVertext, int weight) {
        edges[firstVertex][secondVertext] = weight;
        edges[secondVertext][firstVertex] = weight;
        numOfEdges++;
    }


}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NedyCB1d-1587795995222)(en-resource://database/32932:1)]


5.1.2 克鲁斯卡尔算法
  • 思想:
    1. 将所有边按权值从小到大排列
    1. 逐一加入上述的边,每加入一次需要验证是否构成回路,构成回路则剔除该边
    1. 遍历完①步骤所有边 - 筛选剩下的边即是最小生成树

绿线即是最小生成树
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4fxj1VGq-1587795995225)(en-resource://database/33042:1)]



KruskalAlgorithm.java

/**
 * @Classname KruskalAlgorithm
 * @Description
 * @Date 2020/4/18 18:49
 * @Created by lrc
 */
public class KruskalAlgorithm {

    public static void main(String[] args) {

        //1, 结点信息
        String[] vertexs = {"A", "B", "C", "D", "E", "F", "G"};
        Integer n = vertexs.length;

        // 2. 创建带5个结点的无序带权图
        MyGraph graph = new MyGraph(n);

        // 3. 添加结点
        for (String vertex : vertexs) {
            graph.insertVertex(vertex);
        }

        // 4. 添加结点之间的权值 - 这里的权值>0表示两结点之间可以进行修路且修路的长度  =0表示不可以进行修路
        graph.insertWeight(0, 1, 12);
        graph.insertWeight(0, 5, 16);
        graph.insertWeight(0, 6, 14);

        graph.insertWeight(1, 5, 7);
        graph.insertWeight(1, 2, 10);

        graph.insertWeight(2, 5, 6);
        graph.insertWeight(2, 3, 3);
        graph.insertWeight(2, 4, 5);

        graph.insertWeight(3, 4, 4);

        graph.insertWeight(4, 5, 2);
        graph.insertWeight(4, 6, 8);

        graph.insertWeight(5, 6, 9);




        // 5. 显示图的矩阵
        //System.out.println(graph.getGraph());
        graph.showGrpah();

        // 6. 最小生成树的边
        List<MyGraph.EdgeObject> miniSpanningTree = kruskal(graph);

        System.out.println("\n最小生成树的边=================\n");
        for(MyGraph.EdgeObject edge : miniSpanningTree) {
            System.out.println(edge);
        }


    }


    public static List<MyGraph.EdgeObject>  kruskal(MyGraph graph) {

        //排序图所有边的权值  - 升序排列
        List<MyGraph.EdgeObject> edges = graph.getEdgeObjects();
        Collections.sort(edges);

        //图中所有边升序后的情况
        System.out.println("\n图的所有边");
        for(MyGraph.EdgeObject edge : edges) {
            System.out.println(edge);
        }

        //动态变化的最小生成树 - 每个结点的终点坐标
        int[] ends = new int[graph.getVertexsNum()];

        //最小生成树的边
        List<MyGraph.EdgeObject> miniSpanningTree = new ArrayList<>();

        //遍历图的所有边,找到符合最小生成树的边
        for(MyGraph.EdgeObject edge : edges) {

            // 获取当前边的 两结点的坐标
            int startVertxIndex = edge.startVertexIndex;
            int endVertxIndex = edge.endVertexIndex;

            // 获取这两个结点的终点坐标
            int endVertex1 = endVertex(ends, startVertxIndex);
            int endVertex2 = endVertex(ends, endVertxIndex);

            // 两个两结点的终点坐标不一致,说明符合最小生成树的边
            if(endVertex1 != endVertex2) {
                ends[endVertex1] = endVertex2;
                miniSpanningTree.add(edge);
            }
        }

        return miniSpanningTree;
    }


    //这步实在看不懂 - 有点牛逼 - 利用结点的传递性进行赋终点坐标
    public static int endVertex(int[] ends, int vertextIndex) {

        while(ends[vertextIndex] != 0) {
            vertextIndex = ends[vertextIndex];
        }
        return vertextIndex;

    }


}



//无向带权图
class MyGraph {

    //存储结点
    ArrayList<String> vertexs;

    // 存储结点之间的权值 - 结点之间的关系 -- 矩阵
    int[][] edges;

    // 存储图中边的个数
    int numOfEdges;

    List<EdgeObject> edgeObjects ;


    //初始化 - 结点个数
    MyGraph(int vertexsNum) {

        this.vertexs = new ArrayList<>(vertexsNum);
        this.edges = new int[vertexsNum][vertexsNum];
        this.numOfEdges = 0;
        this.edgeObjects = new ArrayList<>();

    }


    // 图的边对象
    protected class EdgeObject implements Comparable<EdgeObject> {

        //结点名字
        String startVertex;
        //结点索引
        Integer startVertexIndex;

        String endVertex;
        Integer endVertexIndex;

        Integer weight;


        @Override
        public String toString() {
            return "edgesObject{" +
                    "startVertex='" + startVertex + '\'' +
                    ", endVertex='" + endVertex + '\'' +
                    ", weight=" + weight +
                    '}';
        }

        @Override
        public int compareTo(EdgeObject edgesObject) {
            return this.weight - edgesObject.weight;
        }
    }


    //获取结点
    public String getVertex(int n) {
        return vertexs.get(n);
    }

    public List<EdgeObject> getEdgeObjects() {
        return this.edgeObjects;
    }


    //获取边的个数
    public int getNumOfEdges() {
        return numOfEdges;
    }

    //获取结点个数
    public int getVertexsNum() {
        return this.vertexs.size();
    }

    //获取结点根据索引序号
    public String getVertexByIndex(int index) {
        return vertexs.get(index);
    }

    //添加新结点
    public void insertVertex(String vertex) {
        this.vertexs.add(vertex);




    }

    //获取两结点之间的边的权值 "A结点表示0" "B节点表示1"
    public int getWeight(int firstVertex, int secondVertext) {
        return edges[firstVertex][secondVertext];
    }


    //获取矩阵 - 即把数组Edges权值图打印出来
    public String getGraph() {
        return Arrays.deepToString(edges);
    }

    //显示矩阵
    public void showGrpah() {
        for (int[] edgeWeight : edges) {
            System.out.println(Arrays.toString(edgeWeight));
        }
    }


    //添加结点之间的关系 - 即权值 - 因为无向图的关系必须赋值数组中两个元素相同的权值
    public void insertWeight(int firstVertex, int secondVertext, int weight) {
        edges[firstVertex][secondVertext] = weight;
        edges[secondVertext][firstVertex] = weight;
        numOfEdges++;


        EdgeObject edgesObject = new EdgeObject();
        edgesObject.startVertex = vertexs.get(firstVertex);
        edgesObject.startVertexIndex = firstVertex;
        edgesObject.endVertex = vertexs.get(secondVertext);
        edgesObject.endVertexIndex = secondVertext;
        edgesObject.weight = weight;

        edgeObjects.add(edgesObject);


    }


}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ZuiPnDe-1587795995230)(en-resource://database/33044:1)]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值