27. 程序员十大常用算法,及其代码实现

2023-05-16

1.二分查找(非递归)

  • 二分查找也称折半查找(Binary Search),它是一种效率较高的查找方法。但是,折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列在这里插入图片描述
  • 代码实现
package com.qin.Algorithm;

//二分查找算法
//要求数组必须有序
public class BinarySearchNoRecursion {

    public static void main(String[] args) {
        int arr[] = {1,3,8,10,11,67,100};
        int index = binarySearch(arr,100);
        if (index==-1){
            System.out.println("没有找到");
        }else {
            System.out.println("被查找的数下标为:"+index);
        }

    }

    //二分查找的非递归实现
    //arr 被找的数组,target返回被查找的下标
    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 = mid + 1; //需要向右查找
            }
        }
        return -1;
    }

}


  • 结果

在这里插入图片描述

2.分治算法

  • 分治算法的基本思想是将一个规模为N的问题分解为K个规模较小的子问题,这些子问题相互独立且与原问题性质相同。求出子问题的解,就可得到原问题的解。即一种分目标完成程序算法,简单问题可用二分法完成

在这里插入图片描述

在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

//分治算法
public class DivideAndConquer {

    public static void main(String[] args) {
        
        //目标把A柱子中的盘子移动到C柱子,并从低到高,从大到小
        hanoiTower(2,'A','B','C');


    }

    //汉诺塔移动的方法
    //使用分治算法
    //num 有几个圆盘 ,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的情况,我们总是可以看做两个盘,最下边的一个盘2和上面的所有盘1
            //1.先把最上面的所有盘A->B,移动过程会使用到c
            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);
        }
    }


}

  • 结果

在这里插入图片描述

3.动态规划算法

  • 动态规划(Dynamic Programming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。20世纪50年代初,美国数学家贝尔曼(R.Bellman)等人在研究多阶段决策过程的优化问题时,提出了著名的最优化原理,从而创立了动态规划。动态规划的应用极其广泛,包括工程技术、经济、工业生产、军事以及自动化控制等领域,并在背包问题、生产经营问题、资金管理问题、资源分配问题、最短路径问题和复杂系统可靠性问题等中取得了显著的效果

在这里插入图片描述

在这里插入图片描述在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

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; //物品的个数

        //填表:为了记录 放入商品的情况我们定义一个二维数组
        int[][] path = new int[n+1][m+1];


        //创建一个二维数组
        //v[i][j]表示在前i个物品中能够装入容量为j的背包中的最大的价值
        int[][] v = new int[n+1][m+1];

        //初始化第一行和第一列,这里在本程序中可以不处处理,因为默认为0
        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; //将第一行设置为0
        }


        //根据前面的到的公式来动态规划
        for (int i = 1; i < v.length; i++) { //不处理第一行,行代表的是物品的种类
            for (int j = 1; j < v[0].length; j++) { //代表不处理第一列,列代表的是重量
                //公式
                //因为我们的程序i是从1开始的,因此原来的公式中w[i]修改成w[i-1]
                if (w[i-1]>j) { //物品的重量大于当前背包的容量
                    //就把当前方案的上一行的方案赋给这一行
                    v[i][j] = v[i-1][j] ;
                }else { //物品的重量小于当前背包的容量时
                    /*
                    因为我们的程序i是从1开始的,因此调整为val[i-1]+v[i-1][j-w[i]]
                    v[i-1][j]指的是上一行同等重量下的的最佳方案的价格
                    val[i-1]指的是这次的物品价值,因为i是从1开始的,而我们的数组下标是从0开始的
                    v[i-1][j-w[i]]指的是上一行重量范围内最佳方案的价格
                    v[i][j] = Math.max(v[i-1][j],val[i-1]+v[i-1][j-w[i-1]]);
                    为了记录商品存放到背包的情况,我们不能简单的使用上面的公式,需要使用if-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];
                    }
                }
            }
        }


        //输出一下v,看看目前情况
        System.out.println("建表情况");
        for (int[] a : v){
            for (int i = 0; i < a.length; i++) {
                System.out.printf("%d\t",a[i]);
            }
            System.out.println();
        }

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

        for (int[] i:path){
            for (int j = 0; j < i.length; j++) {
                System.out.printf("%d\t",i[j]);
            }
            System.out.println();
        }
        //输出我们放入的是那些商品
        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--;
        }
        

    }

}

  • 结果

在这里插入图片描述

4.KMP算法

  • KMP算法是一种改进的字符串匹配算法,由D.E.Knuth,J.H.Morris和V.R.Pratt提出的,因此人们称它为克努特—莫里斯—普拉特操作(简称KMP算法)。KMP算法的核心是利用匹配失败后的信息,尽量减少模式串与主串的匹配次数以达到快速匹配的目的。具体实现就是通过一个next()函数实现,函数本身包含了模式串的局部匹配信息。KMP算法的时间复杂度O(m+n)在这里插入图片描述
  • 代码实现
package com.qin.Algorithm;

//暴力匹配算法
public class ViolenceMatch {

    public static void main(String[] args) {

        String str1 = "秦小东是张伟爸爸的爷爷";
        String str2 = "张伟爸爸的";
        int i = violenceMatch(str1, str2);
        if (i == -1){
            System.out.println("信息不匹配");
        }else {
            System.out.println("开始下标为"+i);
        }

    }

    //暴力匹配算法实现
    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; //i索引指向s1
        int j = 0; //j索引指向s2

        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;
        }else {
            return -1;
        }

    }

}

  • 结果

在这里插入图片描述

在这里插入图片描述

  • 重点:已匹配数-部分匹配表=位移数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 代码实现

package com.qin.Algorithm;

import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;

import java.util.Arrays;

//KMP算法
public class KMP {

    public static void main(String[] args) {

        String str1 = "BBC ABCDAB ABCDABCDABDE";
        String str2 = "ABCDABD";

        int[] next = kmpNext(str2); //[0,1]
        System.out.println("next="+ Arrays.toString(next));
        System.out.println("====================");

        int i = kmpSearch(str1, str2, next);
        if (i==-1){
            System.out.println("没有找到");
        }else {
            System.out.println("开始下标为"+i);
        }

    }

    //获取到一个字符串(子串)的部分匹配值
    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++) {

            //当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;
    }

    //写出我们的kmp搜索算法
    public static int kmpSearch(String s1,String s2,int[] next){
        //遍历str1
        for (int i = 0,j = 0; i < s1.length(); i++) {
            //需要考虑s1.charAt(i)!=s2.charAt(j),核心点
            while (j>0 && s1.charAt(i)!=s2.charAt(j)){
                j = next[j-1];
            }
            if (s1.charAt(i)==s2.charAt(j)){
                j++;
            }
            if(j==s2.length()){ //找到了
                return i-j+1;
            }
        }
        return -1;
    }

}

  • 结果
    在这里插入图片描述

5.贪心算法

  • 贪心算法(又称贪婪算法)是指,在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以考虑,算法得到的是在某种意义上的局部最优解
  • 贪心算法不是对所有问题都能得到整体最优解,关键是贪心策略的选择。也就是说,不从整体最优上加以考虑,做出的只是在某种意义上的局部最优解

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

import javax.print.DocFlavor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

//贪心算法
public class GreedyAlgorithm {

    public static void main(String[] args) {
        //创建广播电台,放入到Map中
        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("大连");

        broadcasts.put("k1",hashSet1);
        broadcasts.put("k2",hashSet2);
        broadcasts.put("k3",hashSet3);
        broadcasts.put("k4",hashSet4);
        broadcasts.put("k5",hashSet5);
        

        //allAreas 存放所有的地区
        HashSet<String> allAreas = new HashSet<>();

        allAreas.add("北京");
        allAreas.add("上海");
        allAreas.add("天津");
        allAreas.add("广州");
        allAreas.add("深圳");
        allAreas.add("成都");
        allAreas.add("杭州");
        allAreas.add("大连");

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

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



        //定义一个maxKey,保存在一次遍历过程中,能够覆盖最大未覆盖的地区对应的电台的key
        //如果maxKey不为空,则会加入到selects里面
        String maxKey = null;
        while (allAreas.size()!=0){ //如果不为0,则表示还没有覆盖到所有地区

            //每进行一次while循环都需要把maxKey制空
            maxKey = null;
            //遍历broadcast,取出对应的key
            for (String key:broadcasts.keySet()){ //拿到hashMap中的k的数组
                //每进行一次for循环就需要把tempSet清空
                tempSet.clear();
                //当前这个key能够覆盖的地区
                HashSet<String> areas = broadcasts.get(key);
                tempSet.addAll(areas);
                //求出tempSet和allAreas的交集,交集会赋给tempSet
                tempSet.retainAll(allAreas);
                //如果当前这个集合包含的未覆盖地区的数量,比maxKey指向的集合覆盖的地区还多
                //就需要重置maxKey
                if (tempSet.size()>0&&
                        //tempSet.size()>broadcasts.get(maxKey).size())体现出贪心算法的核心
                   (maxKey==null||tempSet.size()>broadcasts.get(maxKey).size())){
                    maxKey = key;
                }
            }
            //如果maxKey!=null,就应该讲maxKey加入selects中
            if (maxKey!=null){
                selects.add(maxKey);
                //将maxKey指向的广播电台从broadcasts中取出
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }
        System.out.println("得到的选择结果"+selects);

    }

}

  • 结果

在这里插入图片描述

6.普利姆算法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

import java.util.Arrays;

//普利姆算法
public class Prim {
    public static void main(String[] args) {

        //测试图是否创建成功
        char[] data = new char[]{'A','B','C','D','E','F','G'};
        int num = data.length;
        //邻接矩阵的关系使用二维数组表示
        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 graph = new MGraph(num);
        //创建一个MinTree对象
        MinTree minTree = new MinTree();
        minTree.createGraph(graph,num,data,weight);

        //打印图
        minTree.showGraph(graph);

        //测速普利姆算法
        minTree.prim(graph,1);

    }
}

class MGraph{
    int num; //表示图的节点个数
    char[] data; //存放节点数据
    int[][] weight; //存放边的权值,就是我们的邻接矩阵

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

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

    //显示图的方法
    public void showGraph(MGraph graph){
        for (int[] link:graph.weight) {
            System.out.println(Arrays.toString(link));
        }
    }

    //编写普利姆算法,得到最小生成树
    //graph 这个是图 ,v 这个是从第几个顶点开始生成 'A'-> 0
    public void prim(MGraph graph,int v){
        //visited 标记顶点是否被访问过
        //visited 默认元素都是0 ,代表没有被访问过
        int[] visited = new int[graph.num];
        //把当前节点标记为已经访问过了
        visited[v] = 1;
        //h1,h2记录两个顶点的下标
        int h1 = -1;
        int h2 = -1;
        int minWeight = 10000; //将minWeight初始成一个大数,后面在遍历过程中会被替换
        for (int k = 1; k < graph.num; k++) { //graph.num个顶点,普利姆算法结束后有graph.num-1条边

            //这个是确定每一次生成的子图,和那个节点的距离最近
            for (int i = 0; i < graph.num; i++) { //i节点表示被访问过的节点
                for (int j = 0; j < graph.num; j++) { //j节点表示还没有被访问过的节点
                    if (visited[i]==1&&visited[j]==0&&graph.weight[i][j]<minWeight){
                        //替换minWeight(寻找已经访问过的节点和未访问过的节点中权值最小的边)
                        minWeight = graph.weight[i][j];
                        h1 = i;
                        h2 = j;
                    }
                }
            }
            //推出了这个for循环,找到了一条最小的边
            System.out.println("边<"+graph.data[h1]+","+graph.data[h2]+">权值:"+minWeight);
            //将当前节点标记为已经访问过的
            visited[h2] = 1;
            //重新设置为最大值10000
            minWeight = 10000;
        }
    }

}

  • 结果

在这里插入图片描述

7. 克鲁斯卡尔算法

在这里插入图片描述

在这里插入图片描述

  • 给所有权值排序

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 那么如何判断是否构成回路?

  • 我们顶点的创建是按照ABCDEFG的顺序来创建的,那么在CDEF这几个点中,F是最大的,所以F是CDE的终点

在这里插入图片描述
在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

import java.util.Arrays;

//克鲁斯卡尔算法
public class KruskalCase {

    private int edgeNum;//边的个数
    private char[] vertexs; //顶点的数组
    private int[][] matrix; //邻接矩阵,边
    //使用INF表示两个顶点不能连通
    private 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 (this.matrix[i][j]!=INF){
                    edgeNum++;
                }
            }
        }


    }

    //打印邻接矩阵
    public void print(){
        System.out.println("邻接矩阵为:\n");
        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();
        }
    }

    //对边进行排序,冒泡
    private void sortEdges(EData[] 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){ //交换
                        EData tmp = edges[j];
                        edges[j] = edges[j+1];
                        edges[j+1] = tmp;
                }
            }
        }
        
    }

    //ch顶点的值,比如'A','B'
    //返回ch对应的下标,如果找不到,返回-1
    private int getPosition(char ch){
        for (int i = 0; i < vertexs.length; i++) {
            if (vertexs[i] == ch){ //找到
                return i;
            }
        }
        return -1;
    }

    //获取图中的边,放到EData[]数组中,后面我们需要遍历该数组
    //是通过matrix 邻接矩阵来获取
    //EData[] 形式[['A','B',12],['B','F',7],。。。。]
    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的顶点的终点,用于后面判断两个顶点的终点是否相同
    //ends: 数组就是记录了各个顶点对应的终点是哪个
    //i 表示传入的顶点对应的下标
    //返回的就是下标为i的这个顶点的终点的下标
    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();
        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数组
            }
        }

        //统计并打印最小生成树
        for (int i = 0; i < index; i++) {
            System.out.println(rets[i]);
        }


    }



    public static void main(String[] args) {

        char[] vertexs = {'A','B','C','D','E','F','G'};
        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();
        System.out.println("=====================");
        System.out.println("未排序");
        System.out.println(Arrays.toString(kruskalCase.getEdges()));
        System.out.println("排序后");
        kruskalCase.sortEdges(edges);
        System.out.println(Arrays.toString(edges));

        kruskalCase.kruskal();


    }

}

//创建一个类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;
    }

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


}

在这里插入图片描述

8. 迪杰斯特拉算法

  • 迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

import com.sun.java.swing.plaf.windows.WindowsDesktopIconUI;

import java.util.Arrays;

//迪杰斯特拉算法
public class DijkstraAlgorithm {

    public static void main(String[] args) {

        char[] vertex = {'A','B','C','D','F','E','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 = new Graph(vertex, matrix);
        System.out.println("打印邻接矩阵");
        graph.showGraph();

        System.out.println("============");
        System.out.println("测试迪杰斯特拉算法");
        graph.djs(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 i = 0; i < matrix.length ; i++){
            for (int j = 0; j < matrix[i].length ; j++) {
                System.out.printf("%7d\t",matrix[i][j]);
            }
            System.out.println();
        }
    }

    //迪杰斯特拉算法
    //index 表示出发顶点对应的下标
    public void djs(int index){
        vv = new VisitedVertex(vertex.length, index);
        update(index); //更新index顶点到走周围顶点的距离和前驱顶点
        for (int i = 1; i < vertex.length; i++) {
            index = vv.updateArr(); //选择并访问新的访问顶点
            update(index);
        }
    }

    //更新index下标顶点到周围顶点的距离和周围顶点的前驱结点
    private void update(int index){
        int len = 0;
        //根据遍历我们的邻接矩阵的matrix[index]行
        for (int i = 0; i < matrix[index].length; i++) {
            // len 含义是出发顶点到index顶点的距离 + 从index顶点到i顶点的距离
            len = vv.getDis(index) + matrix[index][i];
            //如果当前i节点没有被访问过和len小于出发顶点到i顶点的距离
            if (!vv.in(i) && len < vv.getDis(i)){
                vv.updatePre(i,index); //更新i节点的前驱结点
                vv.updateDis(i,len); //跟新出发顶点到i顶点的最短距离
            }
        }
    }

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

}


class VisitedVertex{ //已访问顶点集合

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

    //构造器
    //length 表示顶点的个数,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); //设置其他顶点的距离为65535
        this.already_arr[index] = 1; //设置出发顶点被访问过
        this.dis[index] = 0; //设置出发顶点的访问距离为0
    }

    //判断index顶点是否被访问过,如果访问过就返回true
    public boolean in(int index){
        return already_arr[index] == 1;
    }

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

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

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


    //继续选择并返回新的访问节点,比如这里G完后,就是A作为新的访问节点(注意不是出发节点 )
    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;
            }
        }
        //更新index顶点被访问过
        already_arr[index] = 1;
        return index;
    }

    //显示最后的结果
    //即将三个数组显示即可
    public void show(){

        //输出already_arr
        for (int i:already_arr ) {
            System.out.print(i + " ");
        }
        System.out.println();
        //输出前驱顶点
        System.out.println("前驱节点");
        for (int i:pre_visited ) {
            System.out.print(i + " ");
        }
        System.out.println();
        //输出dis
        System.out.println("到出发顶点的最短距离");
        for (int i:dis ) {
            System.out.print(i + " ");
        }
    }

}

  • 结果

在这里插入图片描述

9. 弗洛伊德算法

  • Floyd算法又称为插点法,是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法,与Dijkstra算法类似。该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 代码实现

package com.qin.Algorithm;

import java.util.Arrays;

//弗洛伊德算法
public class FloydAlgorithm {

    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};

        Graph1 graph = new Graph1(vertex.length, matrix, vertex);


        //调用弗洛伊德算法
        graph.floyd();
        graph.show();
    }

}


//创建图
class Graph1 {

    private char[] vertex; //存放顶点的数组
    private int[][] dis; //保存从各个顶点出发到其他顶点的距离,最后的结果也是保留在该数组
    private int[][] pre; //保存到达目标顶点的前驱节点

    //length 大小  matrix 邻接矩阵 vertex 顶点数组
    public Graph1(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);
        }
    }

    //显示pre数组和dis数组
    public void show(){
        char[] vertex = {'A','B','C','D','E','F','G'};
        System.out.println("打印各个顶点到其他顶点的最短距离");
        for (int k = 0; k < dis.length ; k++) {
            for (int i = 0; i < dis.length; i++) {
                System.out.printf(vertex[pre[k][i]]+" ");
            }
            System.out.println();
        }
        System.out.println("打印各个顶点的前驱节点");
        for (int k = 0; k < dis.length; k++) {
            for (int i = 0; i < dis.length ; i++) {
                System.out.print("("+vertex[k]+"到"+vertex[i]+"的最短路径是"+dis[k][i]+")\t");
            }
            System.out.println();
        }
    }

    //弗洛伊德算法
    public void floyd(){
        int len = 0; //保存距离
        //对中间顶点遍历,k就是中间顶点的下标
        for (int k = 0; k < dis.length ; k++) { //['A','B','C','D','E','F','G']
            //从i顶点开始出发['A','B','C','D','E','F','G']
            for (int i = 0; i < dis.length ; i++) {
                //到达j顶点
                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] ; // 更新前驱顶点
                    }
                }
            }
        }
    }

}
  • 结果

在这里插入图片描述

10.马踏棋盘,骑士周游世界算法

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 代码实现
package com.qin.Algorithm;

import javax.print.DocFlavor;
import java.awt.*;
import java.util.ArrayList;

//马踏棋盘算法,骑士周游世界算法
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;//马儿走的初始位置的行,从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();
        }

    }

    //功能,根据当前的位置,计算马儿还能走哪些位置,并放在一个ArrayList中
    public static ArrayList<Point> next(Point curPoint){

        //创建一个ArrayList
        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;
    }

    //完成骑士周游世界问题的算法
    //chessBoard 棋盘 row 马儿当前的行,从0开始
    //column 马儿当前的列,从0开始
    //step 是第几步,初始位置就是1
    public static void traversalChessBoard(int[][] chessBoard,int row, int column,int step){

        chessBoard[row][column] = step;
        // row = 4 X = 8 column = 4 ; 4*8+4 = 36
        visited[row*X+column] = true; //标记该位置已被访问
        //获取当前位置可以走的集合
        ArrayList<Point> ps = next(new Point(column, row));
        //遍历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<X*Y成立的情况有两种
        //第一种棋盘到目前为止没有走完
        //第二种棋盘处于回溯过程
        if (step < X*Y && !finished){
            chessBoard[row][column] = 0;
            visited[row*X+column] = false;
        }else {
            finished = true;
        }



    }



}

  • 结果

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

27. 程序员十大常用算法,及其代码实现 的相关文章

  • STM32中断-外部中断

    STM32中断 外部中断配置 外部中断配置 1 配置向量中断控制器NVIC xff0c 设置中断优先级 a 配置优先级组别 b 配置相关结构体参数 中断源 抢占优先级 子优先级 c 使用函数写入参数 代码参考如下 span class to
  • Ubuntu20 网络助手无法运行

    最近开始正式啃python高级教程 xff0c 遇到第一个问题 xff0c Ubuntu20版本下 xff0c 网络助手安装后 xff0c 点击开启无反应 经过好几天晚上的折腾 xff0c 终于搞定 xff0c 贴下解决过程 Step1 终
  • 通过服务器搭建一个短视频系统(含推荐算法)

    一 前端开发 前端使用的是uni app框架 xff0c 用到的开发软件是HBuiderx xff0c 前端界面如下所示 xff1a 主要包括五大功能 xff0c 一是热门视频展示 xff08 用到了热门视频推荐算法 xff09 个人推荐视
  • 【已解决】error: ‘CV_GRAY2BGR’ was not declared in this scope

    这是运行高翔 slambook2 代码出现的问题 xff0c 有两种方法解决 error CV GRAY2BGR was not declared in this scope home diyu slambook2 ch8 optical
  • 镜像备份工具rsync

    文章目录 1 概述2 rsync的认证协议3 rsync命令详解4 rsync 43 inotify 1 概述 什么是rsync xff1f rsync 即 Remote Sync 是linux系统下的数据镜像备份工具 使用rsync可以远
  • 系统调用的理解

    文章目录 系统调用什么是系统调用系统调用的分类系统调用与库函数的区别 系统调用 什么是系统调用 什么是系统调用 xff1f 答 操作系统的接口函数是连接应用软件与操作系统的中间桥梁 xff0c 系统调用其实就是操作系统提供给应用程序的接口函
  • ROS与C++入门教程(记录步骤)(一)

    ROS与C 43 43 入门教程 xff08 记录步骤 xff09 0 记录学习生活1 构建工作空间1 1 建立工作空间1 2 设置成自动加载环境 2 构建Catkin包2 1 构建2 2 查看程序包依赖关系2 3 解读package xm
  • C语言:全局变量在多个c文件中公用的方法

    用C语言编写程序的时候 xff0c 我们经常会遇到这样一种情况 xff1a 希望在头文件中定义一个全局变量 xff0c 然后包含到两个不同的c文件中 xff0c 希望这个全局变量能在两个文件中共用 举例说明 xff1a 项目文件夹proje
  • 迭代器(iterator)看这篇就够了

    文章目录 前言一 迭代器是什么二 迭代器如何使用2 1 迭代器正常遍历集合2 2 完全版迭代器可以一边遍历一边删除元素2 3 简易版迭代器 总结 前言 迭代器很重要 xff0c 是遍历线性数据结构 xff08 链表 xff09 的重要方法之
  • Jquery 获取元素属性值

    获取属性 获取内置属性获取自定义属性prop value name value attr value name value jquery中内置属性只能用来获取内置 自定义只能用来获取内置 内置属性 span class token func
  • 使用evo测评工具测评性能

    防止健忘 参考EVO工具github链接 xff1a link1 开源室内激光场景数据 xff1a link2 总体来说 xff0c evo是用于处理 评估和比较里程计和SLAM算法的轨迹输出 支持的轨迹文件格式 xff1a Tum文件Ki
  • DNS内网欺骗(仅供参考)

    DNS内网欺骗 仅供参考 下面展示一些 内联代码片 span class token comment 启动apche2 span systemctl start apache2 在 span class token operator spa
  • linux下安装nodejs(附带安装npm)

    一 下载nodejs的二进制文件 附官网链接 xff1a 下载 Node js 右键 xff0c 复制下载链接地址 二 安装解压 mkdir boke cd boke wget https nodejs org dist v16 13 2
  • stm32F103C8T6核心板 使用ST-Link无法烧写程序的解决方案

    stm32F103C8T6核心板 使用ST Link无法烧写程序的解决方案 本人也是小白一名 希望我的回答能对你有所帮助 以下是我遇到的问题 1 首先是插入连接线 电脑显示如图 网上找了很久还没有找到解决方案 不过不影响烧写 其次是FlyM
  • 【无标题】

    stm32最小核心板串口通讯连接方式 首先需要一个含有CH430的usb转ttl模块 3 3v接板子上的3 3v GND接板子上的GND 注意 不要接反了 接反的话usb转ttl模块不会亮 如果接反了并且usb转ttl模块插到电脑上 板子会
  • selenium 滑块问题解决

    滑块问题解决 问题解决分为两步 图片处理 滑块移动处理 图片处理 1 图片获取 这里获取的是背景以及滑块图片 获取图片 通过requests get 将图片下载到本地 with open 39 yuan image html 39 39 r
  • VisionPro 9.0 安装完,没有在Visual Studio 2019工具箱中上显示控件

    VisionPro 9 0 安装完 没有在Visual Studio 2019工具箱中上显示控件 步骤 右键工具箱 然后点击 选择项 然后点击浏览选项 3 目录位置 C Program Files x86 Cognex VisionPro
  • visionPro通过网线连接海康相机踩过的坑

    visionPro通过网线连接海康相机踩过的坑 1 搞了两三天 xff0c 笔者用的是笔记本是小新 xff0c 没有网口 xff0c 通过USB转网口连接摄像头 xff0c 明确的告诉你不行 xff0c USB即使达到所谓的千兆 xff0c
  • 完成select的TCP客户端

    include lt stdio h gt include lt sys types h gt include lt sys socket h gt include lt arpa inet h gt include lt netinet
  • vins概述

    基本框架如上 xff0c VINS的功能模块可包括五个部分 xff1a 数据预处理 初始化 后端非线性优化 闭环检测及闭环优化 代码中主要开启了四个线程 xff0c 分别是 xff1a 前端图像跟踪 后端非线性优化 xff08 其中初始化和

随机推荐