数据结构和算法

目录

数据结构:

实际案例问题:

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

暴力算法:

kmp算法:

汉诺塔问题:

分治算法

八皇后问题:

回溯算法

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

贪心算法

约瑟夫环问题:

最短路径问题:

背包问题

动态规划

排序算法:

查找算法:

树结构:

图结构

稀疏数组:

程序员常用的十大算法:

二分查找算法:

分治算法:

动态规划算法:

KMP算法:

贪心算法:

普里姆算法:

克鲁斯卡尔算法:

迪杰斯特拉算法:

弗洛伊德算法:

马踏棋盘算法:


数据结构:

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

实际案例问题:

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

暴力算法

public class ViolenceMatch {
    public static void main(String[] args) {
        String str1 = "AABBCCABCABACBC";
        String str2 = "ABC";
        int index = violenceMatch(str1, str2);
    }

    public static int violenceMatch(String str1, String str2) {
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();
        int s1Len = s1.length;
        int s2Len = s2.length;
        int i = 0;
        int j = 0;
        while (i < s1Len && j < s2Len) {
            if (s1[i] == s2[j]) {
                // 匹配成功
                i++;
                j++;
            } else {
                i = i - (j - 1);
                j = 0;
            }
        }
        if (j == s2Len) {
            // 匹配成功
            return i - j;
        }
        return -1;
    }
}

KMP算法

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

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

public class KMPAlgorithm {
    public static void main(String[] args) {
        String str1 = "BBC ABCDAB ABCDABCDABDE";
        String str2 = "ABCDABD";
        int[] next = kmpNext(str2);
        int res = kMPSearch(str1, str2, next);
    }

    /**
     * @param str1 母串
     * @param str2 子串
     * @param next 字串的部分匹配表
     * @return
     */
    public static int kMPSearch(String str1, String str2, int[] next) {
        for (int i = 0, j = 0; i < str1.length(); i++) {
            // kmp算法核心
            while (j > 0 && str1.charAt(i) != str2.charAt(j)) {
                j = next[j - 1];
            }
            if (str1.charAt(i) == str2.charAt(j)) {
                j++;
            }
            if (j == str2.length()) { // 找到了
                return i - j + 1;
            }
        }
        return -1;
    }

    /**
     * 获取字符串的部分匹配值
     *
     * @param dest
     * @return
     */
    public static int[] kmpNext(String dest) {
        // 创建一个next数组保存部分匹配值
        int[] next = new int[dest.length()];
        next[0] = 0; // 如果字符串的长度为1,部分匹配值就是0
        for (int i = 1, j = 0; i < dest.length(); i++) {
            while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
                j = next[j - 1];
            }
            // 条件满足时,部分匹配值加一
            if (dest.charAt(i) == dest.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

汉诺塔问题

用到了递归;

分治算法

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

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

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

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

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

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

八皇后问题

要求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])的能放的最大价值;

动态排序代码:

public class KnapsackProblem {
    public static void main(String[] args) {
        int[] w = {1, 4, 3}; // 物品的重量
        int[] val = {1500, 3000, 2000}; // 物品的价值
        int m = 4; // 背包的容量
        int n = val.length; // 物品的个数
        // v[i][j] 表示在前i个物品中,能装入容量为j的背包中的,最大价值
        int[][] v = new int[n + 1][m + 1]; // 表
        int[][] path = new int[n + 1][m + 1];// 记录存放的物品的情况
        // 初始化第一行和第一列
        for (int i = 0; i < v.length; i++) {
            v[i][0] = 0; // 第一列设置为0
        }
        for (int j = 0; j < v[0].length; j++) {
            v[0][j] = 0; // 第一行设置为0
        }
        // 开始动态规划
        for (int i = 1; i < v.length; i++) { // 不处理第一行第一列
            for (int j = 1; j < v[i].length; j++) {
                // 套用公式
                if (w[i - 1] > j) { // 因为i是从1开始的
                    // 当准备加入的新增的商品的容量大于当前背包的容量时,就直接使用上一个单元格的值
                    v[i][j] = v[i - 1][j];
                } else {
                    // 当准备加入的新增的商品的容量小于等于当前背包的容量时
                    // 记录存放的物品的情况
                    if (v[i - 1][j] < val[i - 1] + v[i - 1][j - w[i - 1]]) {
                        v[i][j] = val[i - 1] + v[i - 1][j - w[i - 1]];
                        // 把当前的情况记录到path
                        path[i][j] = 1;
                    } else {
                        v[i][j] = v[i - 1][j];
                    }
                }
            }
        }
        // 遍历展示放入背包的最优解
        int i = path.length - 1;
        int j = path[0].length - 1;
        while (i > 0 && j > 0) {
            if (path[i][j] > 0) {
                System.out.printf("第%d个物品放入背包\n", i);
                j -= w[i - 1];
            }
            i--;
        }
    }
}

排序算法

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

查找算法

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

树结构

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

图结构

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

稀疏数组

目的:压缩二维数组;

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

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

原始的二维数组
稀疏数组

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

    int row, col;
    row = col = 11;
    int[][] chessArr1 = new int[row][col];
    chessArr1[1][1] = 1;
    chessArr1[2][3] = 2;
    chessArr1[4][5] = 2;
    
    int count = 0;
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (chessArr1[i][j] != 0) {
                count++;
            }
        }
    }
    int[][] parseArr = new int[count + 1][3];
    parseArr[0][0] = row;
    parseArr[0][1] = col;
    parseArr[0][2] = count;
    count = 1;
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            if (chessArr1[i][j] != 0) {
                parseArr[count][0] = i;
                parseArr[count][1] = j;
                parseArr[count][2] = chessArr1[i][j];
                count++;
            }
        }
    }
    
    // write to io
    String FILE = "D:\\Java\\parseArr.txt";
    BufferedWriter bw = new BufferedWriter(new FileWriter(FILE));
    for (int[] ints : parseArr) {
        bw.write(Arrays.toString(ints));
        bw.newLine();
    }
    bw.close();
    
    // read from io
    BufferedReader br = new BufferedReader(new FileReader(FILE));
    String tmpS = br.readLine();
    String[] tmpSS = tmpS.substring(1, tmpS.length() - 1).split(",");
    int[][] chessArr2 = new int[Integer.parseInt(tmpSS[0])][Integer.parseInt(tmpSS[1].trim())];
    while (true) {
        tmpS = br.readLine();
        if (tmpS == null || tmpS.isEmpty()) break;
        tmpSS = tmpS.substring(1, tmpS.length() - 1).split(",");
        chessArr2[Integer.parseInt(tmpSS[0])][Integer.parseInt(tmpSS[1].trim())] = Integer.parseInt(tmpSS[2].trim());
    }
    br.close();

程序员常用的十大算法:

二分查找算法:

分治算法:

动态规划算法:

KMP算法:

字符串匹配;

贪心算法

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

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

应用案例,集合覆盖问题

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

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

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

import java.util.*;

public class GreedyAlgorithm {
    public static void main(String[] args) {
        // 贪心算法
        // 创建广播电台
        Map<String, HashSet<String>> broadCasts = new HashMap<>();
        // 将各个电台放入到broadCasts
        HashSet<String> hashSet1 = new HashSet<>();
        hashSet1.add("北京");
        hashSet1.add("上海");
        hashSet1.add("天津");
        HashSet<String> hashSet2 = new HashSet<>();
        hashSet2.add("广州");
        hashSet2.add("北京");
        hashSet2.add("深圳");
        HashSet<String> hashSet3 = new HashSet<>();
        hashSet3.add("成都");
        hashSet3.add("上海");
        hashSet3.add("杭州");
        HashSet<String> hashSet4 = new HashSet<>();
        hashSet4.add("上海");
        hashSet4.add("天津");
        HashSet<String> hashSet5 = new HashSet<>();
        hashSet5.add("杭州");
        hashSet5.add("大连");
        broadCasts.put("k1", hashSet1);
        broadCasts.put("k2", hashSet2);
        broadCasts.put("k3", hashSet3);
        broadCasts.put("k4", hashSet4);
        broadCasts.put("k5", hashSet5);
        // 存放所有的地区
        HashSet<String> allAreas = new HashSet<>();
        allAreas.add("北京");
        allAreas.add("上海");
        allAreas.add("天津");
        allAreas.add("广州");
        allAreas.add("深圳");
        allAreas.add("成都");
        allAreas.add("杭州");
        allAreas.add("大连");
        // 存放选择的电台K集合
        List<String> selects = new ArrayList<>();
        // 临时的集合,在遍历的过程中,存放遍历过程中的电台覆盖的地区和当前还没有覆盖的地区的交集
        HashSet<String> tempSet = new HashSet<>();
        // 保存在一次遍历过程中,能够覆盖最大未覆盖地区对应的电台的key
        String maxKey = null;
        // 如果maxKey不等于空,则会加入到selects
        while (allAreas.size() != 0) { // 如果allAreas不为0,则还没有覆盖到所有地区
            maxKey = null;
            for (String key : broadCasts.keySet()) {
                tempSet.clear();
                // 当前电台能够覆盖的地区
                HashSet<String> areas = broadCasts.get(key);
                tempSet.addAll(areas);
                // 求交集,赋值给tempSet
                tempSet.retainAll(allAreas);
                // 如果当前集合包含的未覆盖地区的数量,比maxKey指向的集合的未覆盖的地区还要多
                // 就需要重置maxKey
                // 每次都选择一个最优的K
                if (tempSet.size() > 0 && (maxKey == null || (tempSet.size() > broadCasts.get(maxKey).size()))) {
                    maxKey = key;
                }
            }
            if (maxKey != null) {
                selects.add(maxKey);
                // 将maxKey指向的广播电台覆盖的地区从allAreas清除掉
                allAreas.removeAll(broadCasts.get(maxKey));
            }
        }
    }
}

普里姆算法

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

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

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

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

public class PrimAlgorithm {
    public static void main(String[] args) {
        // 普利姆算法
        // 用到了邻接矩阵
        char[] data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int verxs = data.length;
        // 邻接矩阵,10000表示不联通
        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 graph = new MGraph(verxs);
        MinTree minTree = new MinTree();
        minTree.createGraph(graph, verxs, data, weight);
        // 打印
        minTree.showGraph(graph);
        // 普利姆算法
        minTree.prim(graph, 0);
    }
}

/**
 * 创建最小生成树
 */
class MinTree {
    /**
     * 创建邻接矩阵
     *
     * @param graph  图对象
     * @param verxs  图对应的顶点的个数
     * @param data   图的各个顶点的值
     * @param weight 图的邻接矩阵
     */
    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[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * ;/
     * 普利姆最小生成树算法
     *
     * @param graph 图
     * @param v     图的第几个顶点开始生成 'A'->0 'B'->1
     */
    public void prim(MGraph graph, int v) {
        // 标记节点是否被访问过
        int[] visited = new int[graph.verxs];
        // 把当前节点标记为已访问
        visited[v] = 1;
        // 用h1和h2记录两个顶点的下标
        int h1 = -1;
        int h2 = -1;
        int minWeight = 10000;
        // 因为又graph.verxs个顶点,所以算法结束后,有graph.verxs-1条边
        for (int k = 1; k < graph.verxs; k++) {
            // 确定每一次生成的子图,和哪个节点的距离最近
            for (int i = 0; i < graph.verxs; i++) { // 遍历已经访问过的节点
                for (int j = 0; j < graph.verxs; j++) { // 遍历所有没有访问过的节点
                    if (visited[i] == 1 && visited[j] == 0 && graph.weight[i][j] < minWeight) {
                        // 已访问
                        minWeight = graph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }
                }
            }
            System.out.println("边<" + graph.data[h1] + "," + graph.data[h2] + "> 权值:" + minWeight);
            // 标记已经访问
            visited[h2] = 1;
            minWeight = 10000;
        }
    }
}

class MGraph {
    /**
     * 表示图的节点的个数
     */
    int verxs;
    /**
     * 存放节点的数据
     */
    char[] data;
    /**
     * 存放边,邻接矩阵
     */
    int[][] weight;

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

克鲁斯卡尔算法

也是最小生成树算法;

修路问题

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

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

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

public class KruskalCase {
    /**
     * 边的个数
     */
    int edgeNum;
    /**
     * 顶点的数组
     */
    char[] vertexs;
    /**
     * 邻接矩阵
     */
    int[][] matrix;
    /**
     * 最大值表示路不通
     */
    static final int INF = Integer.MAX_VALUE;

    public KruskalCase(char[] vertexs, int[][] matrix) {
        // 初始化点和边的数量
        int vlen = vertexs.length;
        // 初始化顶点,复制
        this.vertexs = new char[vlen];
        for (int i = 0; i < vertexs.length; i++) {
            this.vertexs[i] = vertexs[i];
        }
        // 初始化边,复制
        this.matrix = new int[vlen][vlen];
        for (int i = 0; i < vlen; i++) {
            for (int j = 0; j < vlen; j++) {
                this.matrix[i][j] = matrix[i][j];
            }
        }
        // 统计有多少条边
        for (int i = 0; i < vlen; i++) {
            for (int j = i + 1; j < vlen; j++) {
                if (matrix[i][j] != INF) {
                    edgeNum++;
                }
            }
        }
    }

    /**
     * 打印邻接矩阵
     */
    public void print() {
        System.out.println("邻接矩阵为:");
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = 0; j < vertexs.length; j++) {
                System.out.printf("%10d\t", matrix[i][j]);
            }
            System.out.println(); // 换行
        }
    }

    /**
     * 对边进行排序
     *
     * @param eData
     */
    private void sortEdges(EData[] eData) {
        for (int i = 0; i < eData.length - 1; i++) {
            for (int j = 0; j < eData.length - 1 - i; j++) {
                if (eData[j].weight > eData[j + 1].weight) {
                    EData t = eData[j];
                    eData[j] = eData[j + 1];
                    eData[j + 1] = t;
                }
            }
        }
    }

    /**
     * @param ch 顶点的下标
     * @return 顶点对应的下标
     */
    public int getPosition(char ch) {
        for (int i = 0; i < vertexs.length; i++) {
            if (vertexs[i] == ch) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 获取图中的边,放到EData[]数组中,后面我们需要遍历该数组
     * 通过matrix邻接矩阵来获取
     *
     * @return
     */
    private EData[] getEdges() {
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for (int i = 0; i < vertexs.length; i++) {
            for (int j = i + 1; j < vertexs.length; j++) {
                // 有多少条边
                if (matrix[i][j] != INF) {
                    edges[index++] = new EData(vertexs[i], vertexs[j], matrix[i][j]);
                }
            }
        }
        return edges;
    }

    /**
     * 获取下标为i的顶点的终点的下标,用于后面判断两个顶点的终点是否相同
     *
     * @param ends 数组对应了各个顶点对应的终点是哪个
     * @param i    表示的传入的顶点对应的下标
     * @return
     */
    private int getEnd(int[] ends, int i) {
        while (ends[i] != 0) {
            i = ends[i];
        }
        return i;
    }

    public void kruskal() {
        // 最后结果数组的索引
        int index = 0;
        // 用于保存已有最小生成树,中的每个顶点在最小生成树中的终点
        int[] ends = new int[edgeNum];
        // 保存最后的最小生成树
        EData[] rets = new EData[edgeNum];
        // 获取图中所有的边的集合,一共有12条边
        EData[] edges = getEdges();
        sortEdges(edges);
        // 遍历edges数组,并且确认不回路
        for (int i = 0; i < edges.length; i++) {
            // 获取第i条边的起点
            int p1 = getPosition(edges[i].start);
            // 获取第i条边的终点
            int p2 = getPosition(edges[i].end);
            // 获取p1这个顶点在已有最小生成树中的终点,(重要)
            int m = getEnd(ends, p1);
            // 获取p2这个顶点在已有最小生成树中的终点
            int n = getEnd(ends, p2);
            // 没有构成回路
            if (m != n) {
                // 设置m在最小生成树中的终点
                ends[m] = n;
                // 有一条边加入rets数组
                rets[index++] = edges[i];
            }
        }
    }

    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        // 0,0 表示自己到自己的距离
        int[][] matrix = {
                {0, 12, INF, INF, INF, 16, 14},
                {12, 0, 10, INF, INF, 7, INF},
                {INF, 10, 0, 3, 5, 6, INF},
                {INF, INF, 3, 0, 4, INF, INF},
                {INF, INF, 5, 4, 0, 2, 8},
                {16, 7, 6, INF, 2, 0, 9},
                {14, INF, INF, INF, 8, 9, 0}
        };
        KruskalCase kruskalCase = new KruskalCase(vertexs, matrix);
        kruskalCase.print();
        EData[] edges = kruskalCase.getEdges();
        kruskalCase.sortEdges(edges);
        kruskalCase.kruskal();
    }
}

/**
 * 对象的实例表示一条边
 * {'A','B',12}
 */
class EData {
    /**
     * 边的起点
     */
    char start;
    /**
     * 边的终点
     */
    char end;
    /**
     * 边的权值
     */
    int weight;

    public EData(char start, char end, int weight) {
        this.start = start;
        this.end = end;
        this.weight = weight;
    }

    @Override
    public String toString() {
        return "EData{" +
                "start=" + start +
                ", end=" + end +
                ", weight=" + weight +
                '}';
    }
}

迪杰斯特拉算法

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

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

public class DijkstraAlgorithm {
}

class Graph {
    /**
     * 顶点数组
     */
    char[] vertex;
    /**
     * 邻接矩阵
     */
    int[][] matrix;

    public Graph(char[] vertex, int[][] matrix) {
        this.vertex = vertex;
        this.matrix = matrix;
    }

    /**
     * 显示图
     */
    public void showGraph() {
        for (int[] link : matrix) {
            System.out.println(Arrays.toString(link));
        }
    }

    /**
     * 已经访问的顶点的集合
     */
    VisitedVertex vv;

    /**
     * 迪杰斯特拉算法
     *
     * @param index 出发点,已经访问的节点
     */
    public void dsj(int index) {
        vv = new VisitedVertex(vertex.length, index);
        // 更新index下标的顶点到周围的顶点的距离和前驱顶点
        update(index);
        for (int j = 1; j < vertex.length; j++) {
            index = vv.updateArr();
            update(index);
        }
    }

    /**
     * 更新index下标顶点到周围的顶点的距离,和周围顶点的前驱顶点
     *
     * @param index
     */
    private void update(int index) {
        int len = 0;
        for (int j = 0; j < matrix[index].length; j++) {
            // len:出发顶点到index顶点的距离 + 从index顶点到j顶点的距离的 合
            len = vv.getDis(index) + matrix[index][j];
            if (!vv.in(j) && len < vv.getDis(j)) {
                vv.updatePre(j, index);
                vv.updateDis(j, len);
            }
        }
    }

    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[][] matrix = new int[vertexs.length][vertexs.length];
        final int N = 65535;
        matrix[0] = new int[]{N, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, N, N, 9, N, N, 3};
        matrix[2] = new int[]{7, N, N, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, N, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, N, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, N, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, N};
        Graph graph = new Graph(vertexs, matrix);
        graph.showGraph();
        // 迪杰斯特拉算法,从G 6这个点到其他节点的距离
        graph.dsj(2);
    }
}

/**
 * 已访问的顶点的集合
 */
class VisitedVertex {
    /**
     * 1已访问 0未访问
     */
    int[] already_arr;
    /**
     * 每一个下标对应的值为前一个顶点,会动态更新
     */
    int[] pre_visited;
    /**
     * 记录出发顶点到其他所有顶点的最短距离,会动态更新
     */
    int[] dis;

    /**
     * @param length 表示顶点的个数
     * @param index  出发顶点
     */
    public VisitedVertex(int length, int index) {
        this.already_arr = new int[length];
        this.pre_visited = new int[length];
        this.dis = new int[length];
        // 初始化dis数组
        Arrays.fill(dis, 65535);
        // 设置出发顶点被访问过
        this.already_arr[index] = 1;
        // 设置出发顶点的访问距离为0
        this.dis[index] = 0;
    }

    /**
     * 判断index下标对应的顶点是否被访问过
     *
     * @param index
     * @return 如果访问过就返回true
     */
    public boolean in(int index) {
        return already_arr[index] == 1;
    }

    /**
     * 更新出发顶点到index顶点的距离
     *
     * @param index
     * @param len
     */
    public void updateDis(int index, int len) {
        dis[index] = len;
    }

    /**
     * 更新顶点pre的前驱为index的顶点
     *
     * @param pre
     * @param index
     */
    public void updatePre(int pre, int index) {
        pre_visited[pre] = index;
    }

    /**
     * 返回出发顶点到index顶点的距离
     *
     * @param index
     * @return
     */
    public int getDis(int index) {
        return dis[index];
    }

    /**
     * 继续选择并返回新的未访问的访问顶点
     *
     * @return
     */
    public int updateArr() {
        int min = 65535, index = 0;
        for (int i = 0; i < already_arr.length; i++) {
            // 没有被访问
            if (already_arr[i] == 0 && dis[i] < min) {
                min = dis[i];
                index = i;
            }
        }
        // 标记已访问
        already_arr[index] = 1;
        return index;
    }
}

弗洛伊德算法

也是求最小路径的算法;

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

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

public class FloydAlgorithm {
    public static void main(String[] args) {
        char[] vertexs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        final int N = 65535;
        int[][] matrix = new int[vertexs.length][vertexs.length];
        matrix[0] = new int[]{0, 5, 7, N, N, N, 2};
        matrix[1] = new int[]{5, 0, N, 9, N, N, 2};
        matrix[2] = new int[]{7, N, 0, N, 8, N, N};
        matrix[3] = new int[]{N, 9, N, 0, N, 4, N};
        matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};
        matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};
        matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};
        Graph graph = new Graph(vertexs.length, matrix, vertexs);
        graph.floyd();
        graph.show();
    }
}

/**
 * 创建图
 */
class Graph {
    /**
     * 存放顶点的数组
     */
    char[] vertex;
    /**
     * 保存从各个顶点出发,到其他顶点的距离
     */
    int[][] dis;
    /**
     * 保存到达目标顶点的前驱顶点
     */
    int[][] pre;

    /**
     * 构造方法
     *
     * @param length 多少个顶点
     * @param matrix 邻接表
     * @param vertex 顶点的数组
     */
    public Graph(int length, int[][] matrix, char[] vertex) {
        this.vertex = vertex;
        this.dis = matrix;
        this.pre = new int[length][length];
        for (int i = 0; i < length; i++) {
            Arrays.fill(pre[i], i);
        }
    }

    /**
     * 显示pre和dis数组
     */
    public void show() {
        for (int k = 0; k < dis.length; k++) {
            // 先将pre数组输出
            for (int i = 0; i < dis.length; i++) {
                System.out.print(vertex[pre[k][i]] + " ");
            }
            System.out.println();
            // 输出dis数组
            for (int i = 0; i < dis.length; i++) {
                System.out.print("(" + vertex[k] + "到" + vertex[i] + "的最短路径是" + dis[k][i] + ") ");
            }
            System.out.println();
            System.out.println();
        }
    }

    /**
     * 弗洛伊德算法
     */
    public void floyd() {
        // 变量的距离
        int len = 0;
        // 对中间顶点遍历,k就是中间顶点的下标
        for (int k = 0; k < dis.length; k++) {
            // 从i顶点开始出发['A','B']
            for (int i = 0; i < dis.length; i++) { // 行
                for (int j = 0; j < dis.length; j++) {
                    // 从顶点i出发,经过k中间节点,到达顶点j的距离
                    len = dis[i][k] + dis[k][j];
                    if (dis[i][j] > len) {
                        dis[i][j] = len;
                        // 前驱顶点
                        pre[i][j] = pre[k][j];
                    }
                }
            }
        }
    }
}

马踏棋盘算法

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

public class HorseChessBoard {
    /**
     * 棋盘的列
     */
    static int X;
    /**
     * 棋盘的行
     */
    static int Y;

    /**
     * 根据当前的位置,计算马还能走哪些位置
     *
     * @param curPoint
     * @return
     */
    static ArrayList<Point> next(Point curPoint) {
        ArrayList<Point> ps = new ArrayList<>();
        Point p1 = new Point();
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        return ps;
    }

    /**
     * 根据当前这一步的所有下一步的选择位置,进行非递减排序
     *
     * @param ps
     */
    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();
                return count1 - count2;
            }
        });
    }

    /**
     * 标记棋盘是否被访问过
     */
    static boolean visited[];
    /**
     * 标记棋盘的所有位置是否都被访问过了
     */
    static boolean finished;

    /**
     * 完成骑士周游问题的算法
     *
     * @param chessBoard 棋盘
     * @param row        马当前的位置,从0开始
     * @param column     马当前的位置,从0开始
     * @param step       第几步,从1开始
     */
    static void traversalChessboard(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 p = ps.remove(0);
            // 该点未访问
            if (!visited[p.y * X + p.x]) {
                traversalChessboard(chessBoard, p.y, p.x, step + 1);
            }
        }
        // 1、棋盘还没有走完
        // 2、棋盘走完了,但还在回溯
        if (step < X * Y && !finished) {
            chessBoard[row][column] = 0;
            visited[row * X + column] = false;
        } else {
            finished = true;
        }
    }

    public static void main(String[] args) {
        X = 8;
        Y = 8;
        int row = 1;
        int column = 1;
        int[][] chessBoard = new int[Y][X];
        visited = new boolean[X * Y];
        long startTime = System.currentTimeMillis();
        traversalChessboard(chessBoard, row - 1, column - 1, 1);
        long endTime = System.currentTimeMillis() - startTime;
    }
}

 

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒋劲豪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值