• 数据结构和算法


    目录

    数据结构:

    实际案例问题:

    判断子字符串在母字符串中第一次出现的位置:

    暴力算法:

    kmp算法:

    汉诺塔问题:

    分治算法

    八皇后问题:

    回溯算法

    马踏棋盘算法(骑士周游问题):

    贪心算法

    约瑟夫环问题:

    最短路径问题:

    背包问题

    动态规划

    排序算法:

    查找算法:

    树结构:

    图结构

    稀疏数组:

    程序员常用的十大算法:

    二分查找算法:

    分治算法:

    动态规划算法:

    KMP算法:

    贪心算法:

    普里姆算法:

    克鲁斯卡尔算法:

    迪杰斯特拉算法:

    弗洛伊德算法:

    马踏棋盘算法:


    数据结构:

    数据结构和算法——数据结构-CSDN博客

    实际案例问题:

    判断子串在母串中第一次出现的位置

    暴力算法

    1. public class ViolenceMatch {
    2. public static void main(String[] args) {
    3. String str1 = "AABBCCABCABACBC";
    4. String str2 = "ABC";
    5. int index = violenceMatch(str1, str2);
    6. }
    7. public static int violenceMatch(String str1, String str2) {
    8. char[] s1 = str1.toCharArray();
    9. char[] s2 = str2.toCharArray();
    10. int s1Len = s1.length;
    11. int s2Len = s2.length;
    12. int i = 0;
    13. int j = 0;
    14. while (i < s1Len && j < s2Len) {
    15. if (s1[i] == s2[j]) {
    16. // 匹配成功
    17. i++;
    18. j++;
    19. } else {
    20. i = i - (j - 1);
    21. j = 0;
    22. }
    23. }
    24. if (j == s2Len) {
    25. // 匹配成功
    26. return i - j;
    27. }
    28. return -1;
    29. }
    30. }

    KMP算法

    寻找前缀和后缀的共有元素(部分匹配值);

    1、先得到子串的部分匹配表;

    1. public class KMPAlgorithm {
    2. public static void main(String[] args) {
    3. String str1 = "BBC ABCDAB ABCDABCDABDE";
    4. String str2 = "ABCDABD";
    5. int[] next = kmpNext(str2);
    6. int res = kMPSearch(str1, str2, next);
    7. }
    8. /**
    9. * @param str1 母串
    10. * @param str2 子串
    11. * @param next 字串的部分匹配表
    12. * @return
    13. */
    14. public static int kMPSearch(String str1, String str2, int[] next) {
    15. for (int i = 0, j = 0; i < str1.length(); i++) {
    16. // kmp算法核心
    17. while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
    18. j = next[j - 1];
    19. }
    20. if (str1.charAt(i) == str2.charAt(j)) {
    21. j++;
    22. }
    23. if (j == str2.length()) { // 找到了
    24. return i - j + 1;
    25. }
    26. }
    27. return -1;
    28. }
    29. /**
    30. * 获取字符串的部分匹配值
    31. *
    32. * @param dest
    33. * @return
    34. */
    35. public static int[] kmpNext(String dest) {
    36. // 创建一个next数组保存部分匹配值
    37. int[] next = new int[dest.length()];
    38. next[0] = 0; // 如果字符串的长度为1,部分匹配值就是0
    39. for (int i = 1, j = 0; i < dest.length(); i++) {
    40. while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
    41. j = next[j - 1];
    42. }
    43. // 条件满足时,部分匹配值加一
    44. if (dest.charAt(i) == dest.charAt(j)) {
    45. j++;
    46. }
    47. next[i] = j;
    48. }
    49. return next;
    50. }
    51. }

    汉诺塔问题

    用到了递归;

    分治算法

    1、如果只有一个盘,A->C;

    2、如果n>=2,我们总是可以看作是2个盘,最下面的盘和上面的盘;

    1)先把最上面的盘,A->B;

    2)再把最下面的盘,A->C;

    3)把B塔的所有盘,从B盘移动到C盘;

    1. /**
    2. * 汉诺塔移动的方法
    3. * 分治算法
    4. *
    5. * @param num 有几个盘需要移动
    6. * @param a 第一个盘
    7. * @param b
    8. * @param c
    9. */
    10. public static void hanoiTower(int num, char a, char b, char c) {
    11. // 1、如果只有一个盘,A->C;
    12. if (num == 1) {
    13. System.out.println("第1个盘:" + a + "->" + c);
    14. } else {
    15. // 2、如果n>=2,我们总是可以看作是2个盘,最下面的盘和上面的盘;
    16. // 1)先把最上面的所有盘,A->B;
    17. hanoiTower(num - 1, a, c, b);
    18. // 2)再把最下面的盘,A->C;
    19. System.out.println("第"+num+"个盘:" + a + "->" + c);
    20. // 3)把B塔的所有盘,从B盘移动到C盘;
    21. hanoiTower(num - 1, b, a, c);
    22. }
    23. }

    八皇后问题

    要求8x8个格子,不能同横、竖、斜;

    回溯算法

    马踏棋盘算法

    也叫骑士周游问题,要求马在任意一个位置,每个格子只能走一次,使马把8x8个格子全部走完;

    图的深度优化便利算法(DFS)

    贪心算法

    约瑟夫环问题

    也叫丢手帕问题;

    最短路径问题

    背包问题

    动态规划

    下一个阶段的求解是建立在上一个阶段的解的基础上的;

    01背包:背包内的物品不能重复;

    完全背包:可以重复;

    1)v[i][0] = v[0][j] = 0; // 表示填入表的第一行和和第一列的值是0;

    2)当w[i] > j时,v[i][j] = v[i - 1][j]; // 当准备加入的新增的商品的容量大于当前背包的容量时,就直接使用上一个单元格的值;

    3)当w[i] <= j时,v[i][j] = max { v[i - 1][j], v[i - 1][j - w[i]] + v[i] } ;// 当准备加入的新增的商品的容量小于等于当前背包的容量;
    // 装入的方式应该是求一个最大值:
    v[i - 1][j] 上一个单元格装入的最大值;
    v[i] 当前商品的价值;

    w[i] 新增商品(下标为i的物品)的容量;

    j 当前背包的容量;

    v[i - 1][j - w[i]] 可以找到剩余空间(j-w[i])的能放的最大价值;

    动态排序代码:

    1. public class KnapsackProblem {
    2. public static void main(String[] args) {
    3. int[] w = {1, 4, 3}; // 物品的重量
    4. int[] val = {1500, 3000, 2000}; // 物品的价值
    5. int m = 4; // 背包的容量
    6. int n = val.length; // 物品的个数
    7. // v[i][j] 表示在前i个物品中,能装入容量为j的背包中的,最大价值
    8. int[][] v = new int[n + 1][m + 1]; // 表
    9. int[][] path = new int[n + 1][m + 1];// 记录存放的物品的情况
    10. // 初始化第一行和第一列
    11. for (int i = 0; i < v.length; i++) {
    12. v[i][0] = 0; // 第一列设置为0
    13. }
    14. for (int j = 0; j < v[0].length; j++) {
    15. v[0][j] = 0; // 第一行设置为0
    16. }
    17. // 开始动态规划
    18. for (int i = 1; i < v.length; i++) { // 不处理第一行第一列
    19. for (int j = 1; j < v[i].length; j++) {
    20. // 套用公式
    21. if (w[i - 1] > j) { // 因为i是从1开始的
    22. // 当准备加入的新增的商品的容量大于当前背包的容量时,就直接使用上一个单元格的值
    23. v[i][j] = v[i - 1][j];
    24. } else {
    25. // 当准备加入的新增的商品的容量小于等于当前背包的容量时
    26. // 记录存放的物品的情况
    27. if (v[i - 1][j] < val[i - 1] + v[i - 1][j - w[i - 1]]) {
    28. v[i][j] = val[i - 1] + v[i - 1][j - w[i - 1]];
    29. // 把当前的情况记录到path
    30. path[i][j] = 1;
    31. } else {
    32. v[i][j] = v[i - 1][j];
    33. }
    34. }
    35. }
    36. }
    37. // 遍历展示放入背包的最优解
    38. int i = path.length - 1;
    39. int j = path[0].length - 1;
    40. while (i > 0 && j > 0) {
    41. if (path[i][j] > 0) {
    42. System.out.printf("第%d个物品放入背包\n", i);
    43. j -= w[i - 1];
    44. }
    45. i--;
    46. }
    47. }
    48. }

    排序算法

    数据结构和算法——排序算法-CSDN博客

    查找算法

    数据结构和算法——查找算法-CSDN博客

    树结构

    数据结构和算法——树结构-CSDN博客

    图结构

    数据结构和算法——图结构-CSDN博客

    稀疏数组

    目的:压缩二维数组;

    当一个二维数组中大部分元素为0,或者都为同一个值时,可以用稀疏数组来保持该数组。

    把行和列和值的记录在一个小规模的数组中,从而缩小程序的规模;

    原始的二维数组
    稀疏数组

    普通数组与稀疏数组转换的代码: 

    1. int row, col;
    2. row = col = 11;
    3. int[][] chessArr1 = new int[row][col];
    4. chessArr1[1][1] = 1;
    5. chessArr1[2][3] = 2;
    6. chessArr1[4][5] = 2;
    7. int count = 0;
    8. for (int i = 0; i < row; i++) {
    9. for (int j = 0; j < col; j++) {
    10. if (chessArr1[i][j] != 0) {
    11. count++;
    12. }
    13. }
    14. }
    15. int[][] parseArr = new int[count + 1][3];
    16. parseArr[0][0] = row;
    17. parseArr[0][1] = col;
    18. parseArr[0][2] = count;
    19. count = 1;
    20. for (int i = 0; i < row; i++) {
    21. for (int j = 0; j < col; j++) {
    22. if (chessArr1[i][j] != 0) {
    23. parseArr[count][0] = i;
    24. parseArr[count][1] = j;
    25. parseArr[count][2] = chessArr1[i][j];
    26. count++;
    27. }
    28. }
    29. }
    30. // write to io
    31. String FILE = "D:\\Java\\parseArr.txt";
    32. BufferedWriter bw = new BufferedWriter(new FileWriter(FILE));
    33. for (int[] ints : parseArr) {
    34. bw.write(Arrays.toString(ints));
    35. bw.newLine();
    36. }
    37. bw.close();
    38. // read from io
    39. BufferedReader br = new BufferedReader(new FileReader(FILE));
    40. String tmpS = br.readLine();
    41. String[] tmpSS = tmpS.substring(1, tmpS.length() - 1).split(",");
    42. int[][] chessArr2 = new int[Integer.parseInt(tmpSS[0])][Integer.parseInt(tmpSS[1].trim())];
    43. while (true) {
    44. tmpS = br.readLine();
    45. if (tmpS == null || tmpS.isEmpty()) break;
    46. tmpSS = tmpS.substring(1, tmpS.length() - 1).split(",");
    47. chessArr2[Integer.parseInt(tmpSS[0])][Integer.parseInt(tmpSS[1].trim())] = Integer.parseInt(tmpSS[2].trim());
    48. }
    49. br.close();

    程序员常用的十大算法:

    二分查找算法:

    分治算法:

    动态规划算法:

    KMP算法:

    字符串匹配

    贪心算法

    贪心算法的结果不一定是最优解;

    每一次选择都是一次最优解;

    应用案例,集合覆盖问题

    1、遍历所有电台,找到一个覆盖了最多未覆盖地区的电台;

    2、将这些电台加入到如ArrayList等集合,想办法把该电台覆盖的地区在下次比较时去掉;

    3、重复第一步直到覆盖了全部的地区;

    1. import java.util.*;
    2. public class GreedyAlgorithm {
    3. public static void main(String[] args) {
    4. // 贪心算法
    5. // 创建广播电台
    6. Map> broadCasts = new HashMap<>();
    7. // 将各个电台放入到broadCasts
    8. HashSet hashSet1 = new HashSet<>();
    9. hashSet1.add("北京");
    10. hashSet1.add("上海");
    11. hashSet1.add("天津");
    12. HashSet hashSet2 = new HashSet<>();
    13. hashSet2.add("广州");
    14. hashSet2.add("北京");
    15. hashSet2.add("深圳");
    16. HashSet hashSet3 = new HashSet<>();
    17. hashSet3.add("成都");
    18. hashSet3.add("上海");
    19. hashSet3.add("杭州");
    20. HashSet hashSet4 = new HashSet<>();
    21. hashSet4.add("上海");
    22. hashSet4.add("天津");
    23. HashSet hashSet5 = new HashSet<>();
    24. hashSet5.add("杭州");
    25. hashSet5.add("大连");
    26. broadCasts.put("k1", hashSet1);
    27. broadCasts.put("k2", hashSet2);
    28. broadCasts.put("k3", hashSet3);
    29. broadCasts.put("k4", hashSet4);
    30. broadCasts.put("k5", hashSet5);
    31. // 存放所有的地区
    32. HashSet allAreas = new HashSet<>();
    33. allAreas.add("北京");
    34. allAreas.add("上海");
    35. allAreas.add("天津");
    36. allAreas.add("广州");
    37. allAreas.add("深圳");
    38. allAreas.add("成都");
    39. allAreas.add("杭州");
    40. allAreas.add("大连");
    41. // 存放选择的电台K集合
    42. List selects = new ArrayList<>();
    43. // 临时的集合,在遍历的过程中,存放遍历过程中的电台覆盖的地区和当前还没有覆盖的地区的交集
    44. HashSet tempSet = new HashSet<>();
    45. // 保存在一次遍历过程中,能够覆盖最大未覆盖地区对应的电台的key
    46. String maxKey = null;
    47. // 如果maxKey不等于空,则会加入到selects
    48. while (allAreas.size() != 0) { // 如果allAreas不为0,则还没有覆盖到所有地区
    49. maxKey = null;
    50. for (String key : broadCasts.keySet()) {
    51. tempSet.clear();
    52. // 当前电台能够覆盖的地区
    53. HashSet areas = broadCasts.get(key);
    54. tempSet.addAll(areas);
    55. // 求交集,赋值给tempSet
    56. tempSet.retainAll(allAreas);
    57. // 如果当前集合包含的未覆盖地区的数量,比maxKey指向的集合的未覆盖的地区还要多
    58. // 就需要重置maxKey
    59. // 每次都选择一个最优的K
    60. if (tempSet.size() > 0 && (maxKey == null || (tempSet.size() > broadCasts.get(maxKey).size()))) {
    61. maxKey = key;
    62. }
    63. }
    64. if (maxKey != null) {
    65. selects.add(maxKey);
    66. // 将maxKey指向的广播电台覆盖的地区从allAreas清除掉
    67. allAreas.removeAll(broadCasts.get(maxKey));
    68. }
    69. }
    70. }
    71. }

    普里姆算法

    最小生成树MST问题,也叫最修路问题;

    1)给一个带权的无向连通图,如何选取一棵生成树,使树上所有的边上权的总和为最小;

    2)N个顶点,一定有N-1条边;

    3)最小生成树主要是普里姆算法和克鲁斯卡尔算法;

    1. public class PrimAlgorithm {
    2. public static void main(String[] args) {
    3. // 普利姆算法
    4. // 用到了邻接矩阵
    5. char[] data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    6. int verxs = data.length;
    7. // 邻接矩阵,10000表示不联通
    8. int[][] weight = {
    9. {10000, 5, 7, 10000, 10000, 10000, 2},
    10. {5, 10000, 10000, 9, 10000, 10000, 3},
    11. {7, 10000, 10000, 10000, 8, 10000, 10000},
    12. {10000, 9, 10000, 10000, 10000, 4, 10000},
    13. {10000, 10000, 8, 10000, 10000, 5, 4},
    14. {10000, 10000, 10000, 4, 5, 10000, 6},
    15. {2, 3, 10000, 10000, 4, 6, 10000}
    16. };
    17. MGraph graph = new MGraph(verxs);
    18. MinTree minTree = new MinTree();
    19. minTree.createGraph(graph, verxs, data, weight);
    20. // 打印
    21. minTree.showGraph(graph);
    22. // 普利姆算法
    23. minTree.prim(graph, 0);
    24. }
    25. }
    26. /**
    27. * 创建最小生成树
    28. */
    29. class MinTree {
    30. /**
    31. * 创建邻接矩阵
    32. *
    33. * @param graph 图对象
    34. * @param verxs 图对应的顶点的个数
    35. * @param data 图的各个顶点的值
    36. * @param weight 图的邻接矩阵
    37. */
    38. public void createGraph(MGraph graph, int verxs, char[] data, int[][] weight) {
    39. for (int i = 0; i < verxs; i++) {
    40. graph.data[i] = data[i];
    41. for (int j = 0; j < verxs; j++) {
    42. graph.weight[i][j] = weight[i][j];
    43. }
    44. }
    45. }
    46. /**
    47. * 显示图的邻接矩阵
    48. */
    49. public void showGraph(MGraph graph) {
    50. for (int[] link : graph.weight) {
    51. System.out.println(Arrays.toString(link));
    52. }
    53. }
    54. /**
    55. * ;/
    56. * 普利姆最小生成树算法
    57. *
    58. * @param graph 图
    59. * @param v 图的第几个顶点开始生成 'A'->0 'B'->1
    60. */
    61. public void prim(MGraph graph, int v) {
    62. // 标记节点是否被访问过
    63. int[] visited = new int[graph.verxs];
    64. // 把当前节点标记为已访问
    65. visited[v] = 1;
    66. // 用h1和h2记录两个顶点的下标
    67. int h1 = -1;
    68. int h2 = -1;
    69. int minWeight = 10000;
    70. // 因为又graph.verxs个顶点,所以算法结束后,有graph.verxs-1条边
    71. for (int k = 1; k < graph.verxs; k++) {
    72. // 确定每一次生成的子图,和哪个节点的距离最近
    73. for (int i = 0; i < graph.verxs; i++) { // 遍历已经访问过的节点
    74. for (int j = 0; j < graph.verxs; j++) { // 遍历所有没有访问过的节点
    75. if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight) {
    76. // 已访问
    77. minWeight = graph.weight[i][j];
    78. h1 = i;
    79. h2 = j;
    80. }
    81. }
    82. }
    83. System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + "> 权值:" + minWeight);
    84. // 标记已经访问
    85. visited[h2] = 1;
    86. minWeight = 10000;
    87. }
    88. }
    89. }
    90. class MGraph {
    91. /**
    92. * 表示图的节点的个数
    93. */
    94. int verxs;
    95. /**
    96. * 存放节点的数据
    97. */
    98. char[] data;
    99. /**
    100. * 存放边,邻接矩阵
    101. */
    102. int[][] weight;
    103. public MGraph(int verxs) {
    104. this.verxs = verxs;
    105. data = new char[verxs];
    106. weight = new int[verxs][verxs];
    107. }
    108. }

    克鲁斯卡尔算法

    也是最小生成树算法;

    修路问题

    具体做法:首先构造一个只含n个顶点的森林,然后依权值从小到大从联通网中选择边加入到森林中,并使森林不产生回路,直到森林变成一棵树为止;

    问题(1)对图的所有的边按照权值的大小进行排序;

    问题(2)将边添加到最小生成树中,判断是否形成了回路;

    1. public class KruskalCase {
    2. /**
    3. * 边的个数
    4. */
    5. int edgeNum;
    6. /**
    7. * 顶点的数组
    8. */
    9. char[] vertexs;
    10. /**
    11. * 邻接矩阵
    12. */
    13. int[][] matrix;
    14. /**
    15. * 最大值表示路不通
    16. */
    17. static final int INF = Integer.MAX_VALUE;
    18. public KruskalCase(char[] vertexs, int[][] matrix) {
    19. // 初始化点和边的数量
    20. int vlen = vertexs.length;
    21. // 初始化顶点,复制
    22. this.vertexs = new char[vlen];
    23. for (int i = 0; i < vertexs.length; i++) {
    24. this.vertexs[i] = vertexs[i];
    25. }
    26. // 初始化边,复制
    27. this.matrix = new int[vlen][vlen];
    28. for (int i = 0; i < vlen; i++) {
    29. for (int j = 0; j < vlen; j++) {
    30. this.matrix[i][j] = matrix[i][j];
    31. }
    32. }
    33. // 统计有多少条边
    34. for (int i = 0; i < vlen; i++) {
    35. for (int j = i + 1; j < vlen; j++) {
    36. if (matrix[i][j] != INF) {
    37. edgeNum++;
    38. }
    39. }
    40. }
    41. }
    42. /**
    43. * 打印邻接矩阵
    44. */
    45. public void print() {
    46. System.out.println("邻接矩阵为:");
    47. for (int i = 0; i < vertexs.length; i++) {
    48. for (int j = 0; j < vertexs.length; j++) {
    49. System.out.printf("%10d\t", matrix[i][j]);
    50. }
    51. System.out.println(); // 换行
    52. }
    53. }
    54. /**
    55. * 对边进行排序
    56. *
    57. * @param eData
    58. */
    59. private void sortEdges(EData[] eData) {
    60. for (int i = 0; i < eData.length - 1; i++) {
    61. for (int j = 0; j < eData.length - 1 - i; j++) {
    62. if (eData[j].weight > eData[j + 1].weight) {
    63. EData t = eData[j];
    64. eData[j] = eData[j + 1];
    65. eData[j + 1] = t;
    66. }
    67. }
    68. }
    69. }
    70. /**
    71. * @param ch 顶点的下标
    72. * @return 顶点对应的下标
    73. */
    74. public int getPosition(char ch) {
    75. for (int i = 0; i < vertexs.length; i++) {
    76. if (vertexs[i] == ch) {
    77. return i;
    78. }
    79. }
    80. return -1;
    81. }
    82. /**
    83. * 获取图中的边,放到EData[]数组中,后面我们需要遍历该数组
    84. * 通过matrix邻接矩阵来获取
    85. *
    86. * @return
    87. */
    88. private EData[] getEdges() {
    89. int index = 0;
    90. EData[] edges = new EData[edgeNum];
    91. for (int i = 0; i < vertexs.length; i++) {
    92. for (int j = i + 1; j < vertexs.length; j++) {
    93. // 有多少条边
    94. if (matrix[i][j] != INF) {
    95. edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
    96. }
    97. }
    98. }
    99. return edges;
    100. }
    101. /**
    102. * 获取下标为i的顶点的终点的下标,用于后面判断两个顶点的终点是否相同
    103. *
    104. * @param ends 数组对应了各个顶点对应的终点是哪个
    105. * @param i 表示的传入的顶点对应的下标
    106. * @return
    107. */
    108. private int getEnd(int[] ends, int i) {
    109. while (ends[i] != 0) {
    110. i = ends[i];
    111. }
    112. return i;
    113. }
    114. public void kruskal() {
    115. // 最后结果数组的索引
    116. int index = 0;
    117. // 用于保存已有最小生成树,中的每个顶点在最小生成树中的终点
    118. int[] ends = new int[edgeNum];
    119. // 保存最后的最小生成树
    120. EData[] rets = new EData[edgeNum];
    121. // 获取图中所有的边的集合,一共有12条边
    122. EData[] edges = getEdges();
    123. sortEdges(edges);
    124. // 遍历edges数组,并且确认不回路
    125. for (int i = 0; i < edges.length; i++) {
    126. // 获取第i条边的起点
    127. int p1 = getPosition(edges[i].start);
    128. // 获取第i条边的终点
    129. int p2 = getPosition(edges[i].end);
    130. // 获取p1这个顶点在已有最小生成树中的终点,(重要)
    131. int m = getEnd(ends, p1);
    132. // 获取p2这个顶点在已有最小生成树中的终点
    133. int n = getEnd(ends, p2);
    134. // 没有构成回路
    135. if (m != n) {
    136. // 设置m在最小生成树中的终点
    137. ends[m] = n;
    138. // 有一条边加入rets数组
    139. rets[index++] = edges[i];
    140. }
    141. }
    142. }
    143. public static void main(String[] args) {
    144. char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    145. // 0,0 表示自己到自己的距离
    146. int[][] matrix = {
    147. {0, 12, INF, INF, INF, 16, 14},
    148. {12, 0, 10, INF, INF, 7, INF},
    149. {INF, 10, 0, 3, 5, 6, INF},
    150. {INF, INF, 3, 0, 4, INF, INF},
    151. {INF, INF, 5, 4, 0, 2, 8},
    152. {16, 7, 6, INF, 2, 0, 9},
    153. {14, INF, INF, INF, 8, 9, 0}
    154. };
    155. KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
    156. kruskalCase.print();
    157. EData[] edges = kruskalCase.getEdges();
    158. kruskalCase.sortEdges(edges);
    159. kruskalCase.kruskal();
    160. }
    161. }
    162. /**
    163. * 对象的实例表示一条边
    164. * {'A','B',12}
    165. */
    166. class EData {
    167. /**
    168. * 边的起点
    169. */
    170. char start;
    171. /**
    172. * 边的终点
    173. */
    174. char end;
    175. /**
    176. * 边的权值
    177. */
    178. int weight;
    179. public EData(char start, char end, int weight) {
    180. this.start = start;
    181. this.end = end;
    182. this.weight = weight;
    183. }
    184. @Override
    185. public String toString() {
    186. return "EData{" +
    187. "start=" + start +
    188. ", end=" + end +
    189. ", weight=" + weight +
    190. '}';
    191. }
    192. }

    迪杰斯特拉算法

    最短路径算法,是图的广度遍历;

    求出一个顶点到其他顶点的距离;

    1. public class DijkstraAlgorithm {
    2. }
    3. class Graph {
    4. /**
    5. * 顶点数组
    6. */
    7. char[] vertex;
    8. /**
    9. * 邻接矩阵
    10. */
    11. int[][] matrix;
    12. public Graph(char[] vertex, int[][] matrix) {
    13. this.vertex = vertex;
    14. this.matrix = matrix;
    15. }
    16. /**
    17. * 显示图
    18. */
    19. public void showGraph() {
    20. for (int[] link : matrix) {
    21. System.out.println(Arrays.toString(link));
    22. }
    23. }
    24. /**
    25. * 已经访问的顶点的集合
    26. */
    27. VisitedVertex vv;
    28. /**
    29. * 迪杰斯特拉算法
    30. *
    31. * @param index 出发点,已经访问的节点
    32. */
    33. public void dsj(int index) {
    34. vv = new VisitedVertex(vertex.length, index);
    35. // 更新index下标的顶点到周围的顶点的距离和前驱顶点
    36. update(index);
    37. for (int j = 1; j < vertex.length; j++) {
    38. index = vv.updateArr();
    39. update(index);
    40. }
    41. }
    42. /**
    43. * 更新index下标顶点到周围的顶点的距离,和周围顶点的前驱顶点
    44. *
    45. * @param index
    46. */
    47. private void update(int index) {
    48. int len = 0;
    49. for (int j = 0; j < matrix[index].length; j++) {
    50. // len:出发顶点到index顶点的距离 + 从index顶点到j顶点的距离的 合
    51. len = vv.getDis(index) + matrix[index][j];
    52. if (!vv.in(j) && len < vv.getDis(j)) {
    53. vv.updatePre(j, index);
    54. vv.updateDis(j, len);
    55. }
    56. }
    57. }
    58. public static void main(String[] args) {
    59. char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    60. int[][] matrix = new int[vertexs.length][vertexs.length];
    61. final int N = 65535;
    62. matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
    63. matrix[1] = new int[]{5, N, N, 9, N, N, 3};
    64. matrix[2] = new int[]{7, N, N, N, 8, N, N};
    65. matrix[3] = new int[]{N, 9, N, N, N, 4, N};
    66. matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
    67. matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
    68. matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
    69. Graph graph = new Graph(vertexs, matrix);
    70. graph.showGraph();
    71. // 迪杰斯特拉算法,从G 6这个点到其他节点的距离
    72. graph.dsj(2);
    73. }
    74. }
    75. /**
    76. * 已访问的顶点的集合
    77. */
    78. class VisitedVertex {
    79. /**
    80. * 1已访问 0未访问
    81. */
    82. int[] already_arr;
    83. /**
    84. * 每一个下标对应的值为前一个顶点,会动态更新
    85. */
    86. int[] pre_visited;
    87. /**
    88. * 记录出发顶点到其他所有顶点的最短距离,会动态更新
    89. */
    90. int[] dis;
    91. /**
    92. * @param length 表示顶点的个数
    93. * @param index 出发顶点
    94. */
    95. public VisitedVertex(int length, int index) {
    96. this.already_arr = new int[length];
    97. this.pre_visited = new int[length];
    98. this.dis = new int[length];
    99. // 初始化dis数组
    100. Arrays.fill(dis, 65535);
    101. // 设置出发顶点被访问过
    102. this.already_arr[index] = 1;
    103. // 设置出发顶点的访问距离为0
    104. this.dis[index] = 0;
    105. }
    106. /**
    107. * 判断index下标对应的顶点是否被访问过
    108. *
    109. * @param index
    110. * @return 如果访问过就返回true
    111. */
    112. public boolean in(int index) {
    113. return already_arr[index] == 1;
    114. }
    115. /**
    116. * 更新出发顶点到index顶点的距离
    117. *
    118. * @param index
    119. * @param len
    120. */
    121. public void updateDis(int index, int len) {
    122. dis[index] = len;
    123. }
    124. /**
    125. * 更新顶点pre的前驱为index的顶点
    126. *
    127. * @param pre
    128. * @param index
    129. */
    130. public void updatePre(int pre, int index) {
    131. pre_visited[pre] = index;
    132. }
    133. /**
    134. * 返回出发顶点到index顶点的距离
    135. *
    136. * @param index
    137. * @return
    138. */
    139. public int getDis(int index) {
    140. return dis[index];
    141. }
    142. /**
    143. * 继续选择并返回新的未访问的访问顶点
    144. *
    145. * @return
    146. */
    147. public int updateArr() {
    148. int min = 65535, index = 0;
    149. for (int i = 0; i < already_arr.length; i++) {
    150. // 没有被访问
    151. if (already_arr[i] == 0 && dis[i] < min) {
    152. min = dis[i];
    153. index = i;
    154. }
    155. }
    156. // 标记已访问
    157. already_arr[index] = 1;
    158. return index;
    159. }
    160. }

    弗洛伊德算法

    也是求最小路径的算法;

    效率没有迪杰斯特拉算法快;

    计算每个顶点到其他各个顶点的最短路径;

    1. public class FloydAlgorithm {
    2. public static void main(String[] args) {
    3. char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
    4. final int N = 65535;
    5. int[][] matrix = new int[vertexs.length][vertexs.length];
    6. matrix[0] = new int[]{0, 5, 7, N, N, N, 2};
    7. matrix[1] = new int[]{5, 0, N, 9, N, N, 2};
    8. matrix[2] = new int[]{7, N, 0, N, 8, N, N};
    9. matrix[3] = new int[]{N, 9, N, 0, N, 4, N};
    10. matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};
    11. matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};
    12. matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};
    13. Graph graph = new Graph(vertexs.length, matrix, vertexs);
    14. graph.floyd();
    15. graph.show();
    16. }
    17. }
    18. /**
    19. * 创建图
    20. */
    21. class Graph {
    22. /**
    23. * 存放顶点的数组
    24. */
    25. char[] vertex;
    26. /**
    27. * 保存从各个顶点出发,到其他顶点的距离
    28. */
    29. int[][] dis;
    30. /**
    31. * 保存到达目标顶点的前驱顶点
    32. */
    33. int[][] pre;
    34. /**
    35. * 构造方法
    36. *
    37. * @param length 多少个顶点
    38. * @param matrix 邻接表
    39. * @param vertex 顶点的数组
    40. */
    41. public Graph(int length, int[][] matrix, char[] vertex) {
    42. this.vertex = vertex;
    43. this.dis = matrix;
    44. this.pre = new int[length][length];
    45. for (int i = 0; i < length; i++) {
    46. Arrays.fill(pre[i], i);
    47. }
    48. }
    49. /**
    50. * 显示pre和dis数组
    51. */
    52. public void show() {
    53. for (int k = 0; k < dis.length; k++) {
    54. // 先将pre数组输出
    55. for (int i = 0; i < dis.length; i++) {
    56. System.out.print(vertex[pre[k][i]] + " ");
    57. }
    58. System.out.println();
    59. // 输出dis数组
    60. for (int i = 0; i < dis.length; i++) {
    61. System.out.print("(" + vertex[k] + "到" + vertex[i] + "的最短路径是" + dis[k][i] + ") ");
    62. }
    63. System.out.println();
    64. System.out.println();
    65. }
    66. }
    67. /**
    68. * 弗洛伊德算法
    69. */
    70. public void floyd() {
    71. // 变量的距离
    72. int len = 0;
    73. // 对中间顶点遍历,k就是中间顶点的下标
    74. for (int k = 0; k < dis.length; k++) {
    75. // 从i顶点开始出发['A','B']
    76. for (int i = 0; i < dis.length; i++) { // 行
    77. for (int j = 0; j < dis.length; j++) {
    78. // 从顶点i出发,经过k中间节点,到达顶点j的距离
    79. len = dis[i][k] + dis[k][j];
    80. if (dis[i][j] > len) {
    81. dis[i][j] = len;
    82. // 前驱顶点
    83. pre[i][j] = pre[k][j];
    84. }
    85. }
    86. }
    87. }
    88. }
    89. }

    马踏棋盘算法

    也叫骑士周游问题,回溯算法或贪心算法,是图的深度优先搜索的应用;

    1. public class HorseChessBoard {
    2. /**
    3. * 棋盘的列
    4. */
    5. static int X;
    6. /**
    7. * 棋盘的行
    8. */
    9. static int Y;
    10. /**
    11. * 根据当前的位置,计算马还能走哪些位置
    12. *
    13. * @param curPoint
    14. * @return
    15. */
    16. static ArrayList next(Point curPoint) {
    17. ArrayList ps = new ArrayList<>();
    18. Point p1 = new Point();
    19. if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
    20. ps.add(new Point(p1));
    21. }
    22. if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
    23. ps.add(new Point(p1));
    24. }
    25. if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {
    26. ps.add(new Point(p1));
    27. }
    28. if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0) {
    29. ps.add(new Point(p1));
    30. }
    31. if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {
    32. ps.add(new Point(p1));
    33. }
    34. if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {
    35. ps.add(new Point(p1));
    36. }
    37. if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {
    38. ps.add(new Point(p1));
    39. }
    40. if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {
    41. ps.add(new Point(p1));
    42. }
    43. return ps;
    44. }
    45. /**
    46. * 根据当前这一步的所有下一步的选择位置,进行非递减排序
    47. *
    48. * @param ps
    49. */
    50. static void sort(ArrayList ps) {
    51. ps.sort(new Comparator() {
    52. @Override
    53. public int compare(Point o1, Point o2) {
    54. int count1 = next(o1).size();
    55. int count2 = next(o2).size();
    56. return count1 - count2;
    57. }
    58. });
    59. }
    60. /**
    61. * 标记棋盘是否被访问过
    62. */
    63. static boolean visited[];
    64. /**
    65. * 标记棋盘的所有位置是否都被访问过了
    66. */
    67. static boolean finished;
    68. /**
    69. * 完成骑士周游问题的算法
    70. *
    71. * @param chessBoard 棋盘
    72. * @param row 马当前的位置,从0开始
    73. * @param column 马当前的位置,从0开始
    74. * @param step 第几步,从1开始
    75. */
    76. static void traversalChessboard(int[][] chessBoard, int row, int column, int step) {
    77. chessBoard[row][column] = step;
    78. // 标记已访问
    79. visited[row * X + column] = true;
    80. ArrayList ps = next(new Point(column, row));
    81. // 非递减排序,贪心思想
    82. sort(ps);
    83. while (!ps.isEmpty()) {
    84. // 取出一个可以走的位置
    85. Point p = ps.remove(0);
    86. // 该点未访问
    87. if (!visited[p.y * X + p.x]) {
    88. traversalChessboard(chessBoard, p.y, p.x, step + 1);
    89. }
    90. }
    91. // 1、棋盘还没有走完
    92. // 2、棋盘走完了,但还在回溯
    93. if (step < X * Y && !finished) {
    94. chessBoard[row][column] = 0;
    95. visited[row * X + column] = false;
    96. } else {
    97. finished = true;
    98. }
    99. }
    100. public static void main(String[] args) {
    101. X = 8;
    102. Y = 8;
    103. int row = 1;
    104. int column = 1;
    105. int[][] chessBoard = new int[Y][X];
    106. visited = new boolean[X * Y];
    107. long startTime = System.currentTimeMillis();
    108. traversalChessboard(chessBoard, row - 1, column - 1, 1);
    109. long endTime = System.currentTimeMillis() - startTime;
    110. }
    111. }

     

    参考视频:【尚硅谷】数据结构与算法(Java数据结构与算法)_哔哩哔哩_bilibili

  • 相关阅读:
    几行代码实现用Python输出表情包
    MobileNetV3
    Ps:锁定图层
    OneFlow学习笔记:从Python到C++调用过程分析
    jenkins搭建
    C++学习第十课--构造函数详解、explicit与初始化列表笔记
    前端面试第一周快速复盘,不标准的面试经验分享 (一)
    Toxel 与 PCPC II
    rabbitmq安装部署和常用命令
    uniapp的小程序中使用web-view进行相互传参,并监听web-view的返回键
  • 原文地址:https://blog.csdn.net/m0_58961367/article/details/133514195