Java常用算法

常用算法

二分查找非递归算法

public class BinarySearchNoRecur {
    public static void main(String[] args) {
        //测试
        int[] arr = {1, 3, 8, 10, 11, 67, 100};
        int index = binarySearch(arr, 8);
        System.out.println(((index != -1) ? "index=" + index : "未找到..."));
    }

    /**
     * 二分查找的非递归实现
     *
     * @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 - left) / 2;
            if (arr[mid] == target) {
                return mid;
            } else if (arr[mid] > target) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return -1;
    }
} 

分治算法

Divide-and-Conquer

汉诺塔

public class HanoiTower {
    public static void main(String[] args) {
        hanoiTower(3, 'A', 'B', 'C');
    }

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

动态规划算法

0-1背包问题

image-20220914125310017

image-20220914134543138

image-20220914135008979

public class KnapsackSelf03 {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        System.out.print("请输入物品数量: ");
        int num = scanner.nextInt();
        int[] weight = new int[num + 1];
        int[] value = new int[num + 1];

        System.out.print("请输入背包容量: ");
        int capacity = scanner.nextInt();


        int[][] dp = new int[num + 1][capacity + 1];
        weight[0] = 0;
        for (int i = 1; i < num + 1; i++) {
            System.out.print("请输入第" + i + "件物品重量: ");
            weight[i] = scanner.nextInt();
        }
        value[0] = 0;
        for (int i = 1; i < num + 1; i++) {
            System.out.print("请输入第" + i + "件物品价值: ");
            value[i] = scanner.nextInt();
        }


        for (int i = 1; i < dp.length; i++) { //先遍历物品
            for (int j = 1; j < dp[i].length; j++) { //再遍历背包
                if (weight[i] > j) {
                    dp[i][j] = dp[i - 1][j];
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);
                }
            }
        }

        for (int i = 0; i < num + 1; i++) {
            for (int j = 0; j < capacity + 1; j++) {
                System.out.print(dp[i][j] + "\t\t");
            }
            System.out.println();
        }
        System.out.println("背包内最大的物品价值总和为:" + dp[num][capacity]);// 有num个物品可选,且背包的容量为capacity的情况下,能装入背包的最大价值

        int[] item = new int[num + 1];
        int j = capacity;
        int i = num;
        while (i != 0) {
            if (dp[i][j] != dp[i-1][j]) {
                item[i] = i;
                j -= weight[i];
                i--;
            } else {
                i--;
            }
        }

        for (int w = item.length - 1; w >= 0; w--) {
            if (item[w] != 0) {
                System.out.print( "放入物品编号:" + item[w] + "\t");
            }
        }
    }
}
滚动数组

完全背包问题

public class CompleteKnapsack {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        int[] weight = new int[num + 1];
        int[] value = new int[num + 1];

        weight[0] = 0;
        for (int i = 1; i < num + 1; i++) {
            weight[i] = sc.nextInt();
        }
        value[0] = 0;
        for (int i = 1; i < num + 1; i++) {
            value[i] = sc.nextInt();
        }
        int capacity = sc.nextInt();//背包容量

        //动态规划表
        int[][] dp = new int[num + 1][capacity + 1];

        int[]temp = new int[num + 1];
        Arrays.fill(temp, 0);


        //动态规划表
        for (int i = 0; i < num + 1; i++) {
            for (int j = 0; j < capacity + 1; j++) {
                if (i == 0) {
                    dp[i][j] = 0;
                } else if (j == 0) {
                    dp[i][j] = 0;
                } else {
                    if (weight[i] > j) {
                        dp[i][j] = dp[i - 1][j];
                    } else {
                        for (int k = 0; k * weight[i] <= j; k++) {
                            dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - k * weight[i]] + k * value[i]);
                        }
                    }
                }
                System.out.println("最大价值=" + dp[i][j]);
            }
            System.out.println();
        }


        for (int i = 0; i < num + 1; i++) {
            for (int j = 0; j < capacity + 1; j++) {
                System.out.print(dp[i][j] + "\t");
            }
            System.out.println();
        }
    }
}

暴力匹配算法

image-20220916135923301

public class ViolenceMatch {
    public static void main(String[] args) {
        //测试暴力匹配算法
        String str1 = "詹姆 詹姆詹姆 勒布朗詹姆姆斯詹姆斯";
        String str2 = "詹姆斯";
        int index = violenceMatch(str1, str2);
        if (index != -1) {
            System.out.println("index=" + index);
        } else {
            System.out.println("匹配失败...");
        }
    }

    //暴力匹配算法
    public static int violenceMatch(String str1, String str2) {
        char[] s1 = str1.toCharArray();
        char[] s2 = str2.toCharArray();

        int s1_len = s1.length;
        int s2_len = s2.length;

        int i = 0; //i指针指向s1
        int j = 0; //j指针指向s2
        while (i < s1_len && j < s2_len) { //保证匹配时,不越界
            if (s1[i] == s2[j]) {
                i++;
                j++;
            } else { //匹配失败
                i = i - j + 1; //回溯到上一次起始位置的下一个位置
                j = 0;
            }
        }
        //判断是否匹配成功
        if (j == s2_len) {
            return i - j;
        } else {
            return -1;
        }
    }
}

KMP算法

正月点灯笼

public class KmpAlgorithm02 {
    public static void main(String[] args) {
        String text = "ABABABCABAABABABAB";
        String find = "ABABCABAA";
        kmp_search(text, find);
    }

    public static int[] KmpNext(String find) {
        char[] chars = find.toCharArray();
        int[] next_table = new int[chars.length];
        next_table[0] = 0;
        int len = 0;
        int i = 1;
        while (i < chars.length) {
            if (chars[i] == chars[len]) {
                len++;
                next_table[i] = len;
                i++;
            } else {
                if (len > 0) {
                    len = next_table[len - 1];
                } else {
                    next_table[i] = 0;
                    i++;
                }
            }
        }
        return next_table;
    }

    //例如ababc假设失配位置是c,c左边的最大公共匹配长度为2,移动后利于kmpSearch方法的查找
    public static void move_next_table(int[] next_table) {
        for (int i = next_table.length - 1; i > 0; i--) {
            next_table[i] = next_table[i - 1];
        }
        next_table[0] = -1;
    }


    public static void kmp_search(String textStr, String findStr) {
        char[] text = textStr.toCharArray();
        char[] find = findStr.toCharArray();

        int[] next_table = KmpNext(findStr);
        move_next_table(next_table);

        int i = 0; //text的指针
        int j = 0; //find的指针

        while (i < text.length) {
            if (j == find.length - 1 && text[i] == find[j]) {
                System.out.printf("Found index at %d", i - j);
                j = next_table[j];
            }
            if (text[i] == find[j]) {
                i++;
                j++;
            } else {
                j = next_table[j];
                if (j == -1) {
                    i++;
                    j++;
                }
            }
        }
    }
}

解释为什么后移

例如ababc假设失配位置是c,c左边的最大公共匹配长度为2,移动后利于kmpSearch方法的查找

image-20220918195554497

贪心算法

题目:假设存在如下表的需要付费的广播台,以及广播台信号可以覆盖的地区。 如何选择最少的广播台,让所有的地区都可以接收到信号

广播台覆盖地区
K1“杭州”, “北京”, “上海”, “天津”
K2“广州”, “北京”, “上海”, “天津”
K3“成都”, “武汉”, “杭州”
K4“上海”, “天津”
K5“广州”, “杭州”, “大连”

**注意:**因为tempSet存储的是当前广播所覆盖的地区和所有未覆盖地区的交集,而broadcasts存储的是某个广播对应的所有地区,试想一下,如果首先取得的一个广播所对应的与未覆盖地区的交集最小,并且该广播所覆盖的地区是最大的,那么后面的广播即使出现于所有未覆盖地区的交集比maxKey所覆盖的地区和所有未覆盖地区的交集大,由于maxKey广播所覆盖的地区是最大的,那么它不会满足上面的的贪心策略, 继而不会更新maxKey。

所以贪心策略核心如下:

//tempMaxKeySet是指一个maxKey临时的集合,在遍历过程中,maxKey覆盖的地区和当前还没有覆盖的地区的交集
//while循环里的比较时会用到
tempSet.size() > tempMaxKeySet.size()

源代码如下

import java.util.*;

public class GreedyAlgorithm {
    public static void main(String[] args) {
        //创建广播电台,放入到Map
        //利用hashmap键值对的特性,key表示广播站,value表示覆盖地区
        //hashset用于存储对象,不允许有重复的值
        HashMap<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("大连");

        //加入到map
        broadcasts.put("k1", hashSet1);
        broadcasts.put("k2", hashSet2);
        broadcasts.put("k3", hashSet3);
        broadcasts.put("k4", hashSet4);
        broadcasts.put("k5", hashSet5);

        //测试贪心算法
        List<String> selects = greedy(broadcasts);
        System.out.println(selects);
    }

    //存放所有地区
    public static List<String> greedy(HashMap<String, HashSet<String>> broadcasts) {
        //allAreas存放所有地区
        HashSet<String> allAreas = new HashSet<>();

        for (Map.Entry<String, HashSet<String>> entry : broadcasts.entrySet()) {
            HashSet<String> area = entry.getValue();
            Iterator<String> iterator = area.iterator();
            while (iterator.hasNext()) { //hasNext判断集合是否还有下个元素,如果是最后一个元素则返回false,不移动指针
                allAreas.add(iterator.next()); //把迭代器的指向移到下一个位置,同时返回下一个元素的引用
            }
        }

        //创建ArrayList集合,存放选择的电台集合
        ArrayList<String> selects = new ArrayList<>();

        //定义一个临时的集合,在遍历过程中,存放遍历过程中的电台覆盖的地区和当前还没有覆盖的地区的交集
        HashSet<String> tempSet = new HashSet<>();

         /*
            定义一个maxKey,保存在一次遍历过程中,能够覆盖最大未覆盖的地区对应的电台的key
            如果maxKey不为null,则加入到selects中
         */
        String maxKey;
        //定义一个maxKey临时的集合,在遍历过程中,maxKey覆盖的地区和当前还没有覆盖的地区的交集
        HashSet<String> tempMaxKeySet = new HashSet<>();

        while (!allAreas.isEmpty()) { //如果allAreas不为空,表示还没有覆盖到所有地区
            //每次while循环都需要制空maxKey
            maxKey = null;

            //遍历broadcasts,取出对应的key
            for (String key : broadcasts.keySet()) {
                //每进行一次for
                tempSet.clear();
                //当前key所能覆盖的地区
                HashSet<String> area = broadcasts.get(key);
                tempSet.addAll(area);
                //求出tempSet 和 allAreas 集合的交集,交集会赋给tempSet
                tempSet.retainAll(allAreas);

                //tempSet.size() > maxKeyArea.size() 体现出贪心算法的特点,每次选择最优的
                if (tempSet.size() > 0 &&
                        (maxKey == null || tempSet.size() > tempMaxKeySet.size())) {
                    maxKey = key;

                    tempMaxKeySet = broadcasts.get(maxKey);
                    tempMaxKeySet.retainAll(allAreas);
                }
            }
            //maxKey != null, 就应该将maxKey 加入 selects
            if (maxKey != null) {
                selects.add(maxKey);
                //将maxKey指向的广播电台覆盖的地区从allAreas去除
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }
        return selects;
    }
}

普利姆算法

求最小生成树

  1. (无向图)任意两个顶点都有路径相通 ——> 连通图
  2. (有向图)任意两个顶点都相互连接 ——> 强连通图
  3. 连通图中,每个边都有权值 ——> 连通网
  4. 连通图的连通子图,包含图中所有的顶点,但只有构成一棵树的 n - 1 条边 ——> 生成树
    (生成树如果再添加任意一条边一定会生成环)
  5. 在连通图中,所有边的代价最小的那棵树 ——> 最小生成树
import java.util.ArrayList;
import java.util.Arrays;

public class PrimAlgorithm {
    public static void main(String[] args) {
        //测试图是否创建成功
        char[] data = new char[] {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int verxs = data.length;
        //邻接矩阵的关系,使用二维数组描述
        int[][] weight ={
                {10000,5,7,10000, 10000,10000,2},
                {5,10000,10000,9,10000,10000,3},
                {7,10000,10000,10000,8,10000,10000},
                {10000,9,10000,10000,10000,4,10000},
                {10000,10000,8,10000,10000,5,4},
                {10000,10000,10000,4,5,10000,6},
                {2,3,10000,10000,4,6,10000}
                };

        //创建MGraph对象
        MGraph mGraph = new MGraph(verxs);
        //创建一个MinTree对象
        MinTree minTree = new MinTree();
        minTree.createGraph(mGraph, verxs, data, weight);
        //打印该图
        minTree.showGraph(mGraph);
        //测试普利姆算法
        minTree.prim(mGraph, 1);
    }
}

//创建最小生成树
class MinTree {
    //创建邻接矩阵
    /**
     * @param graph 图对象
     * @param vertexs 图对应的顶点个数
     * @param data 图的各个顶点的值
     * @param weight 图的邻接矩阵
     */
    public void createGraph(MGraph graph, int vertexs, char[] data, int[][] weight) {
        int i, j;
        for (i = 0; i < vertexs; i++) {
            graph.data[i] = data[i];
            for (j = 0; j < vertexs; j++) {
                graph.weight[i][j] = weight[i][j];
            }
        }
    }

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

    //编写prim算法,得到最小生成树

    /**
     *
     * @param mGraph 图
     * @param v 表示图的第几个顶点开始生成
     */
    public void prim(MGraph mGraph, int v) {
        //集合存储已访问过的点
        ArrayList<Integer> visited = new ArrayList<>();
        //把当前顶点加入集合
        visited.add(v);
        //h1和h2记录两个顶点的下标
        int h1 = -1;
        int h2 = -1;
        int minWeight = 10000;//将minWeight初始成一个大数,后面在遍历过程中,会被替换


        //因为graph.vertexs顶点,普利姆算法结束后,会有graph.vertexs-1条边,因此从1开始遍历
        for (int k = 1; k < mGraph.verxs; k++) { //直到连同所有顶点为止

            //这是确定每一次生成的子图,哪个顶点和这次遍历的顶点的距离最近
            for (int i = 0; i < visited.size(); i++) { //i表示已被访问过的顶点
                for (int j = 0; j < mGraph.verxs; j++) { //j表示还没有访问过的顶点
                    if (! visited.contains(j)
                            && mGraph.weight[visited.get(i)][j] < minWeight) {
                        //替换minWeight = graph.weight[visited.get(i)][j];
                        minWeight = mGraph.weight[visited.get(i)][j];
                        h1 = visited.get(i);
                        h2 = j;
                    }
                }
            }
            //找到一条边最小
            System.out.println("边<" + mGraph.data[h1] + "," + mGraph.data[h2] + "> 权值:" + minWeight);
            //将当前这个顶点标记为已经访问
            visited.add(h2);
            //minWeight 重新设置为最大值10000
            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-20210128225955226

  1. 某城市新增 7 个站点(A, B, C, D, E, F, G) ,现在需要修路把 7 个站点连通
  2. 各个站点的距离用边线表示(权) ,比如 A – B 距离 12 公里
  3. 问:如何修路保证各个站点都能连通,并且总的修建公路总里程最短?

克鲁斯卡尔算法图解说明

image-20210128230230554 image-20210128230322033

image-20210128230504475image-20210128230558289

image-20210128230838042 image-20210128230946838

第1步:将边<E,F>加入 R 中。

边<E,F>的权值最小,因此将它加入到最小生成树结果 R 中。

第2步:将边<C,D>加入 R 中。

上一步操作之后,边<C,D>的权值最小,因此将它加入到最小生成树结果 R 中。

第3步:将边<D,E>加入 R 中。

上一步操作之后,边<D,E>的权值最小,因此将它加入到最小生成树结果 R 中。

第4步:将边<B,F>加入 R 中。

上一步操作之后,边<C,E>的权值最小,但<C,E>会和已有的边构成回路;因此,跳过边<C,E>。同理,跳过边<C,F>。将边<B,F>加入到最小生成树结果 R 中。

第5步:将边<E,G>加入 R 中。

上一步操作之后,边<E,G>的权值最小,因此将它加入到最小生成树结果 R 中。

第6步:将边<A,B>加入 R 中。
上一步操作之后,边<F,G>的权值最小,但<F,G>会和已有的边构成回路;因此,跳过边<F,G>。同理,跳过边<B,C>。将边<A,B>加入到最小生成树结果 R 中。

此时,最小生成树构造完成!它包括的边依次是:<E,F> <C,D> <D,E> <B,F> <E,G> <A,B>

克鲁斯卡尔算法分析

根据前面介绍的克鲁斯卡尔算法的基本思想和做法,我们能够了解到,克鲁斯卡尔算法重点需要解决的以下两个问题:

问题一: 对图的所有边按照权值大小进行排序。

问题二 :将边添加到最小生成树中时,怎么样判断是否形成了回路。

问题一很好解决,采用排序算法进行排序即可。

问题二,处理方式是:记录顶点在"最小生成树"中的终点,顶点的终点是"在最小生成树中与它连通的最大顶点"。然后每次需要将一条边添加到最小生存树时,判断该边的两个顶点的终点是否重合,重合的话则会构成回路。

并查集

public class DisJoint_Set {
    public static void main(String[] args) {
        int vertices = 6;
        int[] parent = new int[vertices];
        int[] rank = new int[vertices];
        int[][] edges = new int[][]{
                {0, 1}, {1, 2}, {1, 3},
                {3, 4}, {2, 5}, {2, 4}
        };

        //初始化parent[],如果parent[i] = i说明该节点的父节点是本身
        initialise(parent, vertices, rank);

        for (int[] edge : edges) {
            int x = edge[0];
            int y = edge[1];
            if (!union_vertices(x, y, parent, rank)) { //若没有合并成功说明有环
                System.out.println("Cycle detected!");
                break;
            }
            System.out.println("No found cycle");
        }
    }

    public static void initialise(int[] parent, int vertices, int[] rank) {
        for (int i = 0; i < vertices; i++) {
            parent[i] = i;
            rank[i] = 0;
        }
    }

    //寻找该节点的根节点
    //v 表示该节点的索引
    public static int find_root(int v, int[] parent) {
        //假设v就是父节点
        if (parent[v] == v) {
            return v;
        } else {
            parent[v] = find_root(parent[v], parent); //把每个双亲节点设置为根节点,路径压缩
            return parent[v];
        }
    }

    //true 成功合并
    //false 合并失败
    public static boolean union_vertices(int x, int y, int[] parent, int[] rank) {
        int x_root = find_root(x, parent);
        int y_root = find_root(y, parent);
        if (x_root == y_root) {
            return false;
        } else {
            if (rank[x_root] > rank[y_root]) {
                parent[y_root] = x;
            } else if (rank[x_root] < rank[y_root]) {
                parent[x_root] = y;
            } else {
                parent[y_root] = x;
                rank[x_root]++;
            }
            return true;
        }
    }
}

代码实现(Disjoint Set)

public class KruskalSelf02 {
    private int edgeNum;
    private char[] vertex;
    private int[][] matrix;
    private static final int INF = Integer.MAX_VALUE;
    private int[] parent;
    private int[] rank;

    public static void main(String[] args) {
        char[] vertex = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
        int[] parent = new int[vertex.length];
        int[] rank = new int[vertex.length];
        int[][] matrix = new int[][]{
                /*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}
        };
        KruskalSelf02 kruskalSelf02 = new KruskalSelf02(vertex, matrix, parent, rank);
        int index = 0;
        int edgeNum = kruskalSelf02.getEdgeNum();
        Edge_Data[] result = new Edge_Data[edgeNum];
        Edge_Data[] edges = kruskalSelf02.getEdges();
        kruskalSelf02.sortEdge(edges);
        for (int i = 0; i < edgeNum; i++) {
            int v1 = kruskalSelf02.getPosition(edges[i].start);
            int v2 = kruskalSelf02.getPosition(edges[i].end);

            if (union_vertices(v1, v2, parent, rank)) {
                result[index++] = edges[i];
            }
        }
        System.out.println("最小生成树");
        for (int i = 0; i < index; i++) {
            System.out.println(result[i]);
        }
    }

    public KruskalSelf02(char[] vertex, int[][] matrix, int[] parent, int[] rank) {
        this.vertex = new char[vertex.length];
        this.vertex = vertex.clone();

        this.matrix = new int[vertex.length][vertex.length];
        for (int i = 0; i < vertex.length; i++) {
            this.matrix[i] = matrix[i].clone();
        }
        this.parent = new int[vertex.length];
        this.rank = new int[vertex.length];
        for (int i = 0; i < vertex.length; i++) {
            parent[i] = i;
            rank[i] = 0;
        }

    }

    public int getEdgeNum() {
        for (int i = 0; i < vertex.length; i++) {
            for (int j = i + 1; j < vertex.length; j++) {
                if (matrix[i][j] != INF) {
                    edgeNum++;
                }
            }
        }
        return edgeNum;
    }

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

    public void sortEdge(Edge_Data[] edges) {
        for (int i = 0; i < edges.length - 1; i++) {
            for (int j = 0; j < edges.length - 1 - i; j++) {
                if (edges[j].weight > edges[j + 1].weight) {
                    Edge_Data temp = edges[j];
                    edges[j] = edges[j + 1];
                    edges[j + 1] = temp;
                }
            }
        }
    }

    public int getPosition(char ch) {
        for (int i = 0; i < vertex.length; i++) {
            if (vertex[i] == ch) {
                return i;
            }
        }
        return -1;
    }

    public Edge_Data[] getEdges() {
        int index = 0;
        Edge_Data[] edges = new Edge_Data[edgeNum];
        for (int i = 0; i < vertex.length; i++) {
            for (int j = i + 1; j < vertex.length; j++) {
                if (matrix[i][j] != INF) {
                    edges[index++] = new Edge_Data(vertex[i], vertex[j], matrix[i][j]);
                }
            }
        }
        return edges;
    }

    public static int find_root(int v, int[] parent) {
        if (parent[v] == v) {
            return v;
        } else {
            parent[v] = find_root(parent[v], parent);
            return parent[v];
        }
    }

    public static boolean union_vertices(int x, int y, int[] parent, int[] rank) {
        int x_root = find_root(x, parent);
        int y_root = find_root(y, parent);
        if (x_root == y_root) {
            return false;
        } else {
            if (rank[x_root] > rank[y_root]) {
                parent[y_root] = x_root;
            } else if (rank[x_root] < rank[y_root]) {
                parent[x_root] = y_root;
            } else {
                parent[y_root] = x_root;
                rank[x_root]++;
            }
            return true;
        }
    }

}

class Edge_Data {
    char start;
    char end;
    int weight;

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

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

迪杰斯特拉算法

迪杰斯特拉(Dijkstra)算法是典型****最短路径算法****,用于计算一个结点到其他结点的[最短路径。它的主要特点是以 起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。

import java.util.Arrays;

public class DijkstraDemo {
    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;
        /*                    A  B  C  D  E  F  G*/
        matrix[0] = new int[]{N, 5, 7, N, N, N, 2};/*A*/
        matrix[1] = new int[]{5, N, N, 9, N, N, 3};/*B*/
        matrix[2] = new int[]{7, N, N, N, 8, N, N};/*C*/
        matrix[3] = new int[]{N, 9, N, N, N, 4, N};/*D*/
        matrix[4] = new int[]{N, N, 8, N, N, 5, 4};/*E*/
        matrix[5] = new int[]{N, N, N, 4, 5, N, 6};/*F*/
        matrix[6] = new int[]{2, 3, N, N, 4, 6, N};/*G*/
        //创建Graph对象
        Graph graph = new Graph(vertex, matrix);
        //测试邻接矩阵是否ok
        graph.showGraph();
        //测试迪杰斯特拉算法
        graph.dsj(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));
        }
    }

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

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

    //显示结果
    public void showDijkstra() {
        vv.show();
    }
}

class VisitedVertex {
    //记录各个顶点是否访问过 1表示访问过,0表示未访问,会动态更新
    public int[] already_arr;
    //每个下标对应的值为前一个顶点的下标,会动态更新
    public int[] pre_visited;
    //记录出发顶点到其他所有顶点的距离,比如G为出发顶点,就记录G到其他顶点的距离,会动态更新,求得最短距离放入dis
    public int[] dis;

    /**
     * 构造器
     *
     * @param vNum   表示顶点的个数
     * @param vIndex 出发顶点的下标,比如 G点,下标为 6
     */
    public VisitedVertex(int vNum, int vIndex) {
        this.already_arr = new int[vNum];
        this.pre_visited = new int[vNum];
        this.dis = new int[vNum];
        //初始化dis数组
        Arrays.fill(dis, 65535);
        this.dis[vIndex] = 0;// 设置出发顶点的访问距离为0
    }

    /**
     * 判断index下标对应的顶点是否访问过
     *
     * @param index
     * @return 如果访问过,就返回 true,否则返回 false
     */
    public boolean isVisited(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
     */
    public int getDis(int index) {
        return dis[index];
    }


    /**
     * 继续选择并返回新的访问顶点, 比如这里G访问完后,广度搜索选择距离最短的后继顶点作为新的访问顶点
     *
     * @return
     */
    public int updateArr() {
        int min = 65535;
        int index = 0;
        for (int i = 0; i < already_arr.length; i++) {
            if (already_arr[i] == 0 && 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 + "\t\t");
        }
        System.out.println();
        //输出pre_visited
        for (int i : pre_visited) {
            System.out.print(i + "\t\t");
        }
        System.out.println();
        //输出dis
        for (int i : dis) {
            System.out.print(i + "\t\t");
        }
    }
}

弗洛伊德算法

  1. 和Dijkstra算法一 样,弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短路径的算法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特弗洛伊德命名
  2. 弗洛伊德算法(Floyd)计算图中各个顶点之间的最短路径
  3. 迪杰斯特拉算法用于计算图中某一一个顶点到其他项点的最短路径。
  4. 弗洛伊德算法VS迪杰斯特拉算法:迪杰斯特拉算法通过选定的被访问顶点,求出从出发访问顶点到其他顶点的最短路径;弗洛伊德算法中每-一个顶点都是出发访问点,所以需要将每一个顶点看做被访问顶点,求出从每一个顶点到其他项点的最短路径。

代码实现

public class FloydSelf01 {
    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 = 100000;
        /*                    A  B  C  D  E  F  G*/
        matrix[0] = new int[]{N, 5, 7, N, N, N, 2};/*A*/
        matrix[1] = new int[]{5, 0, N, 9, N, N, 3};/*B*/
        matrix[2] = new int[]{7, N, 0, N, 8, N, N};/*C*/
        matrix[3] = new int[]{N, 9, N, 0, N, 4, N};/*D*/
        matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};/*E*/
        matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};/*F*/
        matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};/*G*/

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

        floyd(matrix, path, N);

    }

    public static void floyd(int[][] matrix, int[][] path, int N) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                path[i][j] = -1;
            }
        }

        for (int m = 0; m < matrix.length; m++) {
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix.length; j++) {
                    if (matrix[i][m] + matrix[m][j] < matrix[i][j]) {
                        matrix[i][j] = matrix[i][m] + matrix[m][j];
                        path[i][j] = m; //记录经过哪个点到达
                    }
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = i + 1; j < matrix.length; j++) {
                if (matrix[i][j] == N) {
                    System.out.println(i + "到" + j + "不可到达");
                } else {
                    System.out.print(i + "到" + j + "的最短路径长度为:" + matrix[i][j]);
                    System.out.print("  其最短路径:" + i + "->");
                    findPath(i, j, path);
                    System.out.println(j);
                }
            }
        }
    }

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

骑士周游问题

image-20220926145555997

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

public class HorseChessBoard {
    private static int X; //棋盘行数
    private static int Y; //棋盘列数

    //创建一个数组,标记棋盘各个位置是否被访问过
    private static boolean[] visited;
    //使用一个属性,表示是否棋盘的所有位置都被访问过
    private static boolean finished; //如果为true,表示成功

    public static void main(String[] args) {
        //测试骑士周游算法是否正确
        X = 8;
        Y = 8;
        int row = 1; //初始位置的行
        int column = 1; //初始位置的列
        //创建棋盘
        int[][] chessboard = new int[X][Y];
        visited = new boolean[X * Y]; //初始值都是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(row, column));
        //对ps进行排序,排序规则就是对ps的所有Point对象的下一步的位置的数目,进行非递减排序
        sort(ps);
        //遍历ps
        while (!ps.isEmpty()) {
            Point p = ps.remove(0); //取出下一个可以走的位置
            //判断该点是否已访问
            if (!visited[p.x * X + p.y]) { //说明还没有访问过
                traversalChessBoard(chessboard, p.x, p.y, 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;
        }
    }

    //根据当前位置(Point对象)计算马还能走到哪些位置(Point),并放入到一个集合中ArrayList
    //最多有8个位置
    public static ArrayList<Point> next(Point curPoint) {
        //创建ArrayList
        ArrayList<Point> ps = new ArrayList<>();
        //创建一个Point
        Point p1 = new Point();
        //判断马是否可以走5的位置
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走6的位置
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走7的位置
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走0的位置
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走1的位置
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走2的位置
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走3的位置
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y  -  1) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走4的位置
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {
            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;
                }
            }
        });
    }
}

out.print(i + “\t\t”);
}
}
}


## 弗洛伊德算法

1. 和Dijkstra算法一 样,弗洛伊德(Floyd)算法也是一种用于寻找给定的加权图中顶点间最短路径的算法。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特弗洛伊德命名
2. 弗洛伊德算法(Floyd)计算图中各个顶点之间的最短路径
3. 迪杰斯特拉算法用于计算图中某一一个顶点到其他项点的最短路径。
4. 弗洛伊德算法VS迪杰斯特拉算法:迪杰斯特拉算法通过选定的被访问顶点,求出从出发访问顶点到其他顶点的最短路径;弗洛伊德算法中每-一个顶点都是出发访问点,所以需要将每一个顶点看做被访问顶点,求出从每一个顶点到其他项点的最短路径。

**代码实现**

```java
public class FloydSelf01 {
    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 = 100000;
        /*                    A  B  C  D  E  F  G*/
        matrix[0] = new int[]{N, 5, 7, N, N, N, 2};/*A*/
        matrix[1] = new int[]{5, 0, N, 9, N, N, 3};/*B*/
        matrix[2] = new int[]{7, N, 0, N, 8, N, N};/*C*/
        matrix[3] = new int[]{N, 9, N, 0, N, 4, N};/*D*/
        matrix[4] = new int[]{N, N, 8, N, 0, 5, 4};/*E*/
        matrix[5] = new int[]{N, N, N, 4, 5, 0, 6};/*F*/
        matrix[6] = new int[]{2, 3, N, N, 4, 6, 0};/*G*/

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

        floyd(matrix, path, N);

    }

    public static void floyd(int[][] matrix, int[][] path, int N) {
        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix.length; j++) {
                path[i][j] = -1;
            }
        }

        for (int m = 0; m < matrix.length; m++) {
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix.length; j++) {
                    if (matrix[i][m] + matrix[m][j] < matrix[i][j]) {
                        matrix[i][j] = matrix[i][m] + matrix[m][j];
                        path[i][j] = m; //记录经过哪个点到达
                    }
                }
            }
        }

        for (int i = 0; i < matrix.length; i++) {
            for (int j = i + 1; j < matrix.length; j++) {
                if (matrix[i][j] == N) {
                    System.out.println(i + "到" + j + "不可到达");
                } else {
                    System.out.print(i + "到" + j + "的最短路径长度为:" + matrix[i][j]);
                    System.out.print("  其最短路径:" + i + "->");
                    findPath(i, j, path);
                    System.out.println(j);
                }
            }
        }
    }

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

骑士周游问题

[外链图片转存中…(img-2Pf0VsI0-1664357828390)]

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

public class HorseChessBoard {
    private static int X; //棋盘行数
    private static int Y; //棋盘列数

    //创建一个数组,标记棋盘各个位置是否被访问过
    private static boolean[] visited;
    //使用一个属性,表示是否棋盘的所有位置都被访问过
    private static boolean finished; //如果为true,表示成功

    public static void main(String[] args) {
        //测试骑士周游算法是否正确
        X = 8;
        Y = 8;
        int row = 1; //初始位置的行
        int column = 1; //初始位置的列
        //创建棋盘
        int[][] chessboard = new int[X][Y];
        visited = new boolean[X * Y]; //初始值都是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(row, column));
        //对ps进行排序,排序规则就是对ps的所有Point对象的下一步的位置的数目,进行非递减排序
        sort(ps);
        //遍历ps
        while (!ps.isEmpty()) {
            Point p = ps.remove(0); //取出下一个可以走的位置
            //判断该点是否已访问
            if (!visited[p.x * X + p.y]) { //说明还没有访问过
                traversalChessBoard(chessboard, p.x, p.y, 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;
        }
    }

    //根据当前位置(Point对象)计算马还能走到哪些位置(Point),并放入到一个集合中ArrayList
    //最多有8个位置
    public static ArrayList<Point> next(Point curPoint) {
        //创建ArrayList
        ArrayList<Point> ps = new ArrayList<>();
        //创建一个Point
        Point p1 = new Point();
        //判断马是否可以走5的位置
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y - 2) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走6的位置
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y - 1) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走7的位置
        if ((p1.x = curPoint.x - 2) >= 0 && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走0的位置
        if ((p1.x = curPoint.x - 1) >= 0 && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走1的位置
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y + 2) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走2的位置
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y + 1) < Y) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走3的位置
        if ((p1.x = curPoint.x + 2) < X && (p1.y = curPoint.y  -  1) >= 0) {
            ps.add(new Point(p1));
        }
        //判断马是否可以走4的位置
        if ((p1.x = curPoint.x + 1) < X && (p1.y = curPoint.y - 2) >= 0) {
            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;
                }
            }
        });
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值