个人学习之程序员常用算法

本文详细介绍了多种算法的实现,包括二分查找、汉诺塔回归、动态规划求解0-1背包问题、KMP字符串匹配、普里姆构建最小生成树、迪杰斯特拉求最短路径以及弗洛伊德算法。这些算法在程序员日常工作中有着广泛应用,对于理解算法思想和提高编程能力大有裨益。

声明:感谢尚硅谷的公开资料和视频

程序员常用算法

二分查找算法(非递归)

image-20210813153051604

package algorithm.search;

/**
 * 二分查找 循环方式实现
 */
public class BinarySearchNoRecursion {
    public static void main(String[] args) {
        int[] arr =  {1,3, 8, 10, 11, 67, 100};
        int i = binarySearch(arr, 8);
        System.out.println("返回的下标为:"+i);
    }

    /**
     * @param arr    待查找的数组
     * @param target 查询的目标
     * @return 查到返回下标,未找到,返回-1
     */
    public static int binarySearch(int[] arr, int target) {
        int left = 0;
        int right = arr.length-1;
        while (left <= right){
            // 说明继续查找
            int mid = (left+right)/2;
            if(arr[mid] == target){
                return mid;
            }else if(arr[mid] > target){
                // 升序情况下,向左查找
                right = mid-1;
            }else{
                // 升序情况下,向右查找
                left = left+1;
            }
        }
        return -1;
    }
}

分治算法

image-20210813153245546

image-20210813153301332

image-20210813153332318

package algorithm.divideAndConquer;

/**
 * 汉诺塔
 */
public class HanoiTower {
    public static void main(String[] args) {
        hanoiTower(5,'A','B','C');
    }

    // 汉诺塔的移动方法
    // 使用分治算法
    public static void hanoiTower(int num, char a, char b, char c) {
        // 如果只有一个盘
        if (1 == num) {
            System.out.println("第1个盘从" + a + "->" + c);
        } else {
            // 如果n >= 2 情况,最下边盘2,上面所有盘都看作1
            // 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);
        }
    }
}

动态规划算法

image-20210813153506766

image-20210813153518899

image-20210813153543455

package algorithm.dynamicProgramming;

import static java.lang.Math.max;

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 i = 0; i < v[0].length; i++) {
            v[0][i] = 0;
        }
        // 根据前面得到的公式动态规划处理
        for (int i = 1; i < v.length; i++) {// 不处理第一行
            for (int j = 1; j < v[0].length; j++) { // 不处理第一列
                // 公式
                if (w[i - 1] > j) {// 因为我们是从1开始的,那原来公式中w[i]修改为w[i-1]
                    v[i][j] = v[i - 1][j];
                } else {
                    // 因为i从1 开始
                    //v[i][j] = Math.max(v[i - 1][j], val[i - 1] + v[i - 1][j - w[i - 1]]);
                    // 为了记录商品存放背包的情况,不能直接使用上面的公式,用if条件替代
                    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];
                    }
                }
            }
            System.out.println();
        }
        // 输出
        for (int i = 0; i < v.length; i++) {
            for (int j = 0; j < v[i].length; j++) {
                System.out.print(v[i][j] + " ");
            }
            System.out.println();
        }
        System.out.println("========================");
        // 输出我们
        // 最后放入的那些商品
        // 这样输出,会出现冗余数据
        /*
        for (int i = 0; i < path.length; i++) {
            for (int j = 0; j < path[i].length; j++) {
                if(path[i][j] == 1){
                    System.out.printf("第%d个商品放入到背包",i);
                }
            }
            System.out.println();
        }*/
        int i = path.length - 1;// 行的最大下标
        int j = path[0].length - 1;// 列的最大下标
        while (i>0){ // 从path = 最后开始找
            if(path[i][j] == 1){
                System.out.printf("第%d个商品放入到背包\n",i);
                j -= w[i-1];
            }
            i--;
        }

    }
}

KMP算法

image-20210813153652139

image-20210813153716588

package algorithm.knuthMorrisPratt;

import java.util.Arrays;

/**
 * kmp算法
 */
public class KMPAlgorithm {
    public static void main(String[] args) {
        String str1 = "BBC ABCDAB ABCDABCDABDE";
        String str2 = "ABCDABD";
        int[] kmpNext = kmpNext(str2);
        System.out.println("next=" + Arrays.toString(kmpNext));
        int index = kmpSearch(str1,str2,kmpNext);
        System.out.println("index = "+index);
    }
    // kmp搜索算法

    /**
     * @param str1 原字符串
     * @param str2 子串
     * @param next 字串对应的部分匹配表
     * @return -1 表示未匹配到,返回第一次匹配到的位置
     */
    public static int kmpSearch(String str1, String str2, int[] next) {
        // 遍历
        for (int i = 0, j = 0; i < str1.length(); i++) {
            // str1.charAt(i) != str2.charAt(j)时,调整j的大小
            // 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;
    }

    // 获取到一个字符串(字串的)的部分匹配值
    public static int[] kmpNext(String dest) {
        // 创建一个next数组保存部分匹配值
        int[] next = new int[dest.length()];
        // 如果字符串长度为1,部分匹配值就是0
        next[0] = 0;
        for (int i = 1, j = 0; i < dest.length(); i++) {
            //dest.charAt(i) == dest.charAt(j)不满足时,需要从next[j-1]获取新的j
            // 直到我们发现有dest.charAt(i) == dest.charAt(j)才退出
            // 这是kmp算法的核心
            while (j > 0 && dest.charAt(i) != dest.charAt(j)) {
                j = next[j - 1];
            }
            // dest.charAt(i) == dest.charAt(j)满足时,部分匹配值+1
            if (dest.charAt(i) == dest.charAt(j)) {
                j++;
            }
            next[i] = j;
        }
        return next;
    }
}

贪心算法

image-20210813153938399

image-20210813153956064

image-20210813154009739

package algorithm.greedyAlgorithm;

import java.util.*;

/**
 * 贪心算法
 */
public class GreedyAlgorithm {
    public static void main(String[] args) {
        // 创建广播电台
        Map<String, HashSet<String>> broadcasts = new HashMap<>();
        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("杭州");
        // 创建Arraylist,存放选择的电台集合
        List<String> selects = new ArrayList<>();
        // 定义一个临时集合,在遍历过程中,电台覆盖的地区和当前还没有覆盖地区的交集
        Set<String> tempSet = new HashSet<>();
        // 定义maxKey,保存再一次遍历过程中,能够覆盖最大未覆盖地区对应的电台的key
        String maxKey = null;
        // maxKey 不为空,则会加入到selects
        while (0 != allAreas.size()){
            // 不为0 则未覆盖所有地区
            // 每进行一次while,需要将maxKey置空
            maxKey = null;
            // 遍历broadcasts
            for (String key :  broadcasts.keySet()){
                // 每进行一次,都要置空
                tempSet.clear();
                HashSet<String> arears = broadcasts.get(key);
                tempSet.addAll(arears);
                // tempSet和allAreas 求交集,交集赋值给tempSet
                tempSet.retainAll(allAreas);
                // 如果当前集合包含未覆盖的地区的数量,比maxKey指向的集合未覆盖的地区还多
                // 就需要重置maxKey
                if(0 < tempSet.size()
                        && (null == maxKey || tempSet.size() > broadcasts.get(maxKey).size())){
                    maxKey = key;
                }
            }
            if(null != maxKey){
                // maxKey不为空,就应该将maxKey加入到selects
                selects.add(maxKey);
                // 将maxKey指向的广播电台从allAreas中去掉
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }
        System.out.println("得到选择结果是"+selects);
    }
}

普里姆算法

image-20210813154132032

image-20210813154148383

image-20210813154225649

package algorithm.prim;

import java.util.Arrays;

/**
 * 普利姆算法,解决最短路径问题
 */
public class Prim {
    public static void main(String[] args) {
        // 测试看看图是否创建OK
        char[] data = new char[]{'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int verxs = data.length;
        //邻接矩阵关系创建 10000表示两个点不直联
        int[][] weight = new int[][]{
                {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 mGraph = new MGraph(verxs);
        // 创建Mintree对象
        MinTree minTree = new MinTree();
        minTree.createGraph(mGraph, verxs, data, weight);
        // 遍历展示
        minTree.showGraph(mGraph);
        // 测试prim算法
        minTree.prim(mGraph,1);
    }
}

// 创建最小生成树 -> 村庄的图
class MinTree {
    // 创建图的邻接矩阵

    /**
     * @param mGraph 图对象
     * @param verxs  图对应的顶点个数
     * @param data   图的顶点的值
     * @param weight 图的邻接矩阵
     */
    public void createGraph(MGraph mGraph, int verxs, char data[], int[][] weight) {
        int i, j;
        for (i = 0; i < verxs; i++) {// 顶点
            mGraph.data[i] = data[i];
            for (j = 0; j < verxs; j++) {
                mGraph.weight[i][j] = weight[i][j];
            }

        }
    }

    // 显示图的邻接矩阵
    public void showGraph(MGraph graph) {
        for (int[] link : graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }
    // 编写一个prim算法。得到最小生成树

    /**
     * @param graph 图
     * @param verx  开始的顶点
     */
    public void prim(MGraph graph, int verx) {
        // 标价顶点是否被访问
        int[] visited = new int[graph.verxs];
        // 把当前节点标记为已经访问
        visited[verx] = 1;
        // h1 h2是记录顶点的下表
        int h1 = -1;
        int h2 = -1;
        int minWeight = 10000;//默认为不直连
        for (int k = 1; k < graph.verxs; k++) { // 有n个顶点,就有n-1条边
            // 确定每一次生成的子图和哪个节点距离最近
            for (int i = 0; i < graph.verxs; i++) {// i表示被访问的节点
                for (int j = 0; j < graph.verxs; j++) {// j 表示未被访问的节点
                    if(visited[i] ==1 && visited[j] ==0 && graph.weight[i][j] <minWeight){
                        // 替换minWeight(寻找已经访问的节点和违背访问的节点的的权值)
                        minWeight = graph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }
                }
            }
            //找到一条边是最小
            System.out.println("边<"+graph.data[h1]+","+graph.data[h2]+">权值:"+minWeight);
            visited[h2]=1;
            // 重置minWeight
            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];
    }
}

克鲁斯卡尔算法

image-20210813154408004

image-20210813154424502

package algorithm.kruskal;

import java.util.Arrays;

/**
 * 克鲁斯卡尔算法解决公交车问题
 */
public class KruskalCase {
    private int edgeNum;// 边的个数
    private char[] vertxs;// 顶点数组
    private int[][] matrix;// 邻接矩阵
    // 使用Integer最大值,表示两个点不能连通
    private static final int INF = Integer.MAX_VALUE;

    public static void main(String[] args) {
        char[] vertxs = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int matrix[][] = {
                /*A*//*B*//*C*//*D*//*E*//*F*//*G*/
                /*A*/ {0, 12, INF, INF, INF, 16, 14},
                /*B*/ {12, 0, 10, INF, INF, 7, INF},
                /*C*/ {INF, 10, 0, 3, 5, 6, INF},
                /*D*/ {INF, INF, 3, 0, 4, INF, INF},
                /*E*/ {INF, INF, 5, 4, 0, 2, 8},
                /*F*/ {16, 7, 6, INF, 2, 0, 9},
                /*G*/ {14, INF, INF, INF, 8, 9, 0}};
        KruskalCase kruskalCase = new KruskalCase(vertxs, matrix);
        // 遍历
        kruskalCase.print();
        /*EData[] edges = kruskalCase.getEdges();
        // 排序前
        System.out.println(Arrays.toString(edges));
        System.out.println("排序后===================");
        kruskalCase.sortEdges(edges);
        System.out.println(Arrays.toString(edges));*/
        System.out.println("克鲁斯卡尔算法最小生成树");
        kruskalCase.kruskal();
    }

    public KruskalCase(char[] vertxs, int[][] matrix) {
        // 初始化顶点个数
        int vlen = vertxs.length;
        // 初始化顶点,采用复制拷贝的方式
        this.vertxs = new char[vlen];
        for (int i = 0; i < vertxs.length; i++) {
            this.vertxs[i] = vertxs[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 (INF != this.matrix[i][j]) {
                    edgeNum++;
                }
            }
        }
    }

    public void kruskal() {
        int index = 0;// 表示最后结果数组的索引
        int[] ends = new int[edgeNum];// 用于保存“已有最小生成树”的每个顶点最小生成树的终点
        // 创建结果数组,保存最小生成树
        EData[] rets = new EData[edgeNum];
        // 获取图中的所有的边
        EData[] edges = getEdges();
        System.out.println("图的边的集合=" + Arrays.toString(edges) + " 共" + edges.length + "边");
        // 首先排序 按照边的权值升序排序
        sortEdges(edges);
        // 遍历edges[] 将边添加到最小生成树中时,准备加入边判断是否形成了回路。没有构成回路,就加入到rets,否则不让加入
        for (int i = 0; i < edgeNum; 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) {// 没有构成回路
                ends[m] = n;// 设置m在“已有最小生成树”中的终点
                rets[index++] = edges[i];// 有一条边加入到rets
            }
        }
        // 统计并打印最小生成树 输出rets
        System.out.println("最小生成树");
        for (int i = 0; i < index; i++) {
            System.out.println(rets[i]);
        }

    }

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

    // 对边进行排序 冒泡排序,升序
    public void sortEdges(EData[] edges) {
        for (int i = 0; i < edges.length; i++) {
            for (int j = 0; j < edges.length - 1 - i; j++) {
                if (edges[j].weight > edges[j + 1].weight) {
                    EData temp = edges[j];
                    edges[j] = edges[j + 1];
                    edges[j + 1] = temp;
                }
            }
        }
    }

    /**
     * @param ch 顶点的值
     * @return 顶点所在的下标
     */
    private int getPosition(char ch) {
        for (int i = 0; i < vertxs.length; i++) {
            if (ch == vertxs[i]) {
                return i;
            }
        }
        return -1;
    }

    /**
     * 功能: 获取图中边,放到EData[] 数组中,后面我们需要遍历该数组
     * 是通过matrix 邻接矩阵来获取
     * EData[] 形式 [['A','B', 12], ['B','F',7], .....]
     *
     * @return
     */
    private EData[] getEdges() {
        int index = 0;
        EData[] edges = new EData[edgeNum];
        for (int i = 0; i < vertxs.length; i++) {
            for (int j = i + 1; j < vertxs.length; j++) {
                if (INF != matrix[i][j]) {
                    // 将边加入到edges中
                    edges[index++] = new EData(vertxs[i], vertxs[j], matrix[i][j]);
                }
            }
        }
        return edges;
    }

    /**
     * 功能:获取下标为i的顶点的终点,用于判断两个顶点的终点是否相同
     *
     * @param ends 记录了各个顶点的对应的终点,ends数组是在数组遍历过程中,逐步形成
     * @param i    表示传入的顶点对应的下标
     * @return 返回就是下标为i的这个顶点对应的重点的下标
     */
    private int getEnd(int[] ends, int i) {
        while (0 != ends[i]) {
            i = ends[i];
        }
        return i;
    }
}

// 创建一个类EData,对象实例表示一条边
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;
    }
    // 重写toString方法,便于输出

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

迪杰斯特拉算法

image-20210813154551192

image-20210813154607252

image-20210813154626491

package algorithm.dijkstra;

import java.util.Arrays;

/**
 * Dijkstra迪杰斯特拉算法
 */
public class Dijkstra {
    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        //邻接矩阵
        int[][] matrix = new int[vertex.length][vertex.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 graph = new Graph(vertex, matrix);
        //测试, 看看图的邻接矩阵是否ok
        //graph.showGraph();
        graph.dijkstra(6);
        graph.showDijkstra();

    }
}

class Graph {
    private char[] vertex;// 顶点数组
    private int[][] matrix;// 邻接矩阵
    private VisitedVertex vv;// 已经访问的顶点的集合

    // 构造图
    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));
        }
    }
    //显示结果
    public void showDijkstra() {
        vv.show();
    }

    // dijkstra 迪杰斯特拉算法

    /**
     * @param index 表示出发顶点的下标
     */
    public void dijkstra(int index) {
        vv = new VisitedVertex(vertex.length, index);
        update(index); //更新index下标顶点到周围顶点的距离和周围顶点的前驱顶点
        for (int j = 0; j < vertex.length; j++) {
            index = vv.updateArr();// 选择并返回新的访问结点
            update(index); //更新index下标顶点到周围顶点的距离和周围顶点的前驱顶点
        }
    }

    // 更新index下标顶点到周围顶点的距离和周围顶点的前驱顶点
    private void update(int index) {
        int len = 0;
        // 根据遍历我们的邻接矩阵 matrix[index]
        for (int j = 0; j < matrix[index].length; j++) {
            len = vv.getDis(index) + matrix[index][j];
            if (!vv.in(j) && len < vv.getDis(j)) {
                // 如果j这个顶点没有被访问过,并且len小于出发顶点到j的距离。就需要更新
                vv.updatePre(j, index);// 更新j顶点到的前驱为index顶点
                vv.updateDis(j, len);// 更新出发顶点到j顶点的距离
            }
        }
    }
}

//已访问顶点集合
class VisitedVertex {
    //记录各个顶点是否访问过  1表示访问过,0未访问,会动态更新
    public int[] already_arr;
    //每个下标对应的值为前一个顶点下标, 会动态更新
    public int[] pre_visited;
    //记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就会记录G到其它顶点的距离,会动态更新,求的最短距离就会存放到dis
    public 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;
        // 设置到出发顶点的访问距离
        this.dis[index] = 0;
    }

    /**
     * 功能:判断顶点是否被访问过
     *
     * @param index
     * @return 访问过true 没访问过false
     */
    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;
    }

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

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

    // 继续选择并返回新的访问点
    public int updateArr() {
        int min = 65535, index = 0;
        for (int i = 0; i < already_arr.length; i++) {
            if (0 == already_arr[i] && dis[i] < min) {
                min = dis[i];
                index = i;
            }
        }
        // 更新index被访问过
        already_arr[index] = 1;
        return index;
    }
    //显示最后的结果
    //即将三个数组的情况输出
    public void show() {

        System.out.println("==========================");
        //输出already_arr
        for(int i : already_arr) {
            System.out.print(i + " ");
        }
        System.out.println();
        //输出pre_visited
        for(int i : pre_visited) {
            System.out.print(i + " ");
        }
        System.out.println();
        //输出dis
        for(int i : dis) {
            System.out.print(i + " ");
        }
        System.out.println();
        //为了好看最后的最短距离,我们处理
        char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
        int count = 0;
        for (int i : dis) {
            if (i != 65535) {
                System.out.print(vertex[count] + "("+i+") ");
            } else {
                System.out.println("N ");
            }
            count++;
        }
        System.out.println();

    }
}

弗洛伊德算法

image-20210813154728330

image-20210813154741303

package algorithm.floyd;

import java.util.Arrays;

/**
 * 弗洛伊德(Floyd)算法
 */
public class Floyd {
    public static void main(String[] args) {
        // 测试看看图是否创建成功
        char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
        //创建邻接矩阵
        int[][] matrix = new int[vertex.length][vertex.length];
        final int N = 65535;
        matrix[0] = new int[] { 0, 5, 7, N, N, N, 2 };
        matrix[1] = new int[] { 5, 0, N, 9, N, N, 3 };
        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 graph = new Graph(vertex.length, matrix, vertex);
        graph.floyd();
        graph.show();
    }
}

// 创建图
class Graph{
    private char[] vertex;// 顶点数组
    private int[][] dis;// 保存从各个顶点出发到其他顶点的距离,最后的接过,也是保留在数组中
    private 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];
        // 对pre数组初始化,存放的是前驱结点的下标
        for (int i = 0; i < length; i++) {
            Arrays.fill(pre[i], i);
        }
    }
    // 显示dis数组和pre数组
    public void show(){
        //为了显示便于阅读,我们优化一下输出
        char[] vertex = { 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
        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();

        }

    }

    /**
     * 弗洛伊德(Floyd)算法
     */
    public void floyd(){
        int len=0;// 变量保存距离
        //对中间顶点遍历, k 就是中间顶点的下标 [A, B, C, D, E, F, G]
        for (int k = 0; k < dis.length; k++) {
            //从i顶点开始出发 [A, B, C, D, E, F, G]
            for (int i = 0; i < dis.length; i++) {
                //到达j顶点 // [A, B, C, D, E, F, G]
                for (int j = 0; j < dis.length; j++) {
                    len = dis[i][k] + dis[k][j];// => 求出从i 顶点出发,经过 k中间顶点,到达 j 顶点距离
                    if(len < dis[i][j]) {//如果len小于 dis[i][j]
                        dis[i][j] = len;//更新距离
                        pre[i][j] = pre[k][j];//更新前驱顶点
                    }
                }
            }
        }
    }
}

马踏棋盘算法

image-20210813154955428

image-20210813155011843

package algorithm.knightTraveledAround;

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

/**
 * 马踏棋盘游戏
 */
public class KnightTraveledAround {
    private static int x; // 棋盘行
    private static int y; // 棋盘列
    // 创建一个数组,标记棋盘各个位置是否被访问
    private static boolean visited[];
    // 使用一个属性,标记是否棋盘的所有位置都被访问
    private static boolean finished;// true 表示成功

    public static void main(String[] args) {
        System.out.println("骑士周游算法,开始运行~~");
        //测试骑士周游算法是否正确
        x = 8;
        y = 8;
        int row = 1; //马儿初始位置的行,从1开始编号
        int column = 1; //马儿初始位置的列,从1开始编号
        //创建棋盘
        int[][] chessboard = new int[x][y];
        visited = new boolean[x * y];//初始值都是false
        //测试一下耗时
        long start = System.currentTimeMillis();
        traversalChessboard(chessboard, row - 1, column - 1, 1);
        long end = System.currentTimeMillis();
        System.out.println("共耗时: " + (end - start) + " 毫秒");

        //输出棋盘的最后情况
        for(int[] rows : chessboard) {
            for(int step: rows) {
                System.out.print(step + "\t");
            }
            System.out.println();
        }
    }

    /**
     * 完成骑士周游问题的算法
     *
     * @param chessboard 棋盘
     * @param row        马儿当前的位置的行 从0开始
     * @param column     马儿当前的位置的列  从0开始
     * @param step       是第几步 ,初始位置就是第1步
     */
    public 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));
        //对ps进行排序,排序的规则就是对ps的所有的Point对象的下一步的位置的数目,进行非递减排序
        sort(ps);
        // 遍历ps
        while (!ps.isEmpty()) {
            Point p = ps.remove(0);// 取出下一个可以走的位置
            // 判断该点是否被访问
            if (!visited[p.y * x + p.x]) {//说明还没被访问过
                traversalChessboard(chessboard, p.y, p.x, step + 1);
            }
        }
        //判断马儿是否完成了任务,使用   step 和应该走的步数比较 ,
        //如果没有达到数量,则表示没有完成任务,将整个棋盘置0
        //说明: step < X * Y  成立的情况有两种
        //1. 棋盘到目前位置,仍然没有走完
        //2. 棋盘处于一个回溯过程
        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 p1 = new Point();
        //表示马儿可以走5这个位置
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走6这个位置
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走7这个位置
        if ((p1.x = curPoint.x + 1) < x && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走0这个位置
        if ((p1.x = curPoint.x + 2) < x && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走1这个位置
        if ((p1.x = curPoint.x + 2) < x && (p1.y = curPoint.y + 1) < y) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走2这个位置
        if ((p1.x = curPoint.x + 1) < x && (p1.y = curPoint.y + 2) < y) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走3这个位置
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < y) {
            ps.add(new Point(p1));
        }
        //判断马儿可以走4这个位置
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < y) {
            ps.add(new Point(p1));
        }
        return ps;
    }

    //根据当前这个一步的所有的下一步的选择位置,进行非递减排序, 减少回溯的次数
    public static void sort(ArrayList<Point> ps){
        ps.sort(new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                // 获取o1的下一步的所有位置个数
                int count1 = next(o1).size();
                // 获取o2的下一步的所有位置个数
                int count2 = next(o2).size();
                if(count1  < count2){
                    return -1;
                }else if ( count1 == count2){
                    return 0;
                }else{
                    return 1;
                }
            }
        });
    }
}
 }
    //判断马儿可以走2这个位置
    if ((p1.x = curPoint.x + 1) < x && (p1.y = curPoint.y + 2) < y) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走3这个位置
    if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < y) {
        ps.add(new Point(p1));
    }
    //判断马儿可以走4这个位置
    if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < y) {
        ps.add(new Point(p1));
    }
    return ps;
}

//根据当前这个一步的所有的下一步的选择位置,进行非递减排序, 减少回溯的次数
public static void sort(ArrayList<Point> ps){
    ps.sort(new Comparator<Point>() {
        @Override
        public int compare(Point o1, Point o2) {
            // 获取o1的下一步的所有位置个数
            int count1 = next(o1).size();
            // 获取o2的下一步的所有位置个数
            int count2 = next(o2).size();
            if(count1  < count2){
                return -1;
            }else if ( count1 == count2){
                return 0;
            }else{
                return 1;
            }
        }
    });
}

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值