chapter10 十大算法

一、二分查找(非递归)

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

package binarysearchnorec;

/**
 * @author : sky
 * @version : 1.0
 */
public class BinarySearchNS {
    public static void main(String[] args) {
        int[] arr={1,3,8,10,11,67,100};
        int index=binarySearch(arr,8);
        System.out.println(index);
    }

    /**
     * //二分查找的非递归实现
     * @param arr 待查找的数组,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=mid+1;//需要向右边查找
            }
        }
        return -1;
    }
}

二、分治算法

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

package dac;

/**
 * @author : sky
 * @version : 1.0
 */
public class Hanoitower {
    public static void main(String[] args) {
        hanoiTower(4,'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{
            //如果num>=2,我们总是可以看作是两个盘 1.最下面的一个盘 2.上面所有的盘
            //1.先把 最上面的盘A->B,移动过程会使用到c,最终目标是b
            hanoiTower(num-1,a,c,b);
            //2.把最下边的盘A->C
            System.out.println("第"+num+"个盘 "+a+"->"+c);
            //3.把B塔的所有盘从B->C,移动过程使用到A塔
            hanoiTower(num-1,b,a,c);
        }
    }
}

三、动态规划算法

0-1背包问题
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

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

package dp;

/**
 * @author : sky
 * @version : 1.0
 */
public class KnapsackProblem {
    public static void main(String[] args) {
        int[] weight={1,4,3};//物品的重量
        int[] value={1500,3000,2000};//物品的价值
        int capacity=4;//背包的容量
        int num=value.length;//物品的个数
        //创建二维数组,表示价值表
        //v[i][j] 表示在前i个物品中能够装入容量为j的背包中的最大价值
        int[][] v=new int[num+1][capacity+1];
        //记录放入的商品的情况,定义一个二维数组
        int[][] path=new int[num+1][capacity+1];

        //初始化第一行和第一列,这里体现出过程,在本程序中,可以不去处理,因为默认为0
        for (int i = 0; i <v.length; i++) {
            v[i][0]=0;//将第一列设置为0
        }
        for (int j = 0; j < v[0].length; j++) {
            v[0][j]=0;//将第一行设置为0
        }
        //根据得到的公式,动态规划处理
        for (int i = 1; i <v.length ; i++) {//不处理第一行
            for (int j = 1; j <v[0].length ; j++) {//不处理第一列
                if(weight[i-1]>j){//因为程序的i是从1开始的,原来公式中的weight[i]要修改成weight[i-1]
                    v[i][j]=v[i-1][j];
                }else{
                    //说明i从1开始,所以公式需要调整
                    //v[i][j]=Math.max(v[i-1][j],value[i-1]+v[i-1][j-weight[i-1]]);
                    //为了记录商品存放的情况,不能简单的使用公式需要if-else判断
                    if(v[i-1][j] < value[i-1]+v[i-1][j-weight[i-1]]){
                        v[i][j]=value[i-1]+v[i-1][j-weight[i-1]];
                        //把当前的情况记录到path
                        path[i][j]=1;
                    }else{
                        v[i][j]=v[i-1][j];
                    }
                }
            }
        }
        
        //输出v
        for (int i = 0; i <v.length ; i++) {
            for (int j = 0; j <v[i].length ; j++) {
                System.out.print(v[i][j]+"\t");
            }
            System.out.println();
        }
        /*System.out.println("----------------------------");
        //输出最后放入的是哪些商品,遍历path,这样输出会把所有情况都得到
        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();
        }*/
        System.out.println("----------------------------");
        int i=path.length-1;//行的最大下标
        int j=path[0].length-1;//列的最大下标
        while(i>0 && j>0){//从后往前遍历,从path的最后开始找
            if(path[i][j]==1){
                System.out.printf("第%d个商品放入到背包 ",i);
                j-=weight[i-1];//按照背包容量来说,把weight[i-1]放进去了,得往前找
            }
            i--;
        }//第3个商品放入到背包 第1个商品放入到背包 
    }
}

四、KMP算法

在这里插入图片描述
在这里插入图片描述
暴力匹配:

package kmp;

/**
 * @author : sky
 * @version : 1.0
 */
public class ViolenceMatch {
    public static void main(String[] args) {
        //测试暴力匹配
        String str1="硅硅谷 尚硅谷你尚硅 尚硅谷你尚硅谷你尚硅你好";
        String str2="尚硅谷你尚硅你~";
        int index = violenceMatch(str1, str2);
        System.out.println(index);
    }
    //暴力匹配算法实现
    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;
        }
    }
}

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

注意:KMP算法是在移动 j 而不是在移动 i

package kmp;

import java.util.Arrays;

/**
 * @author : sky
 * @version : 1.0
 */
public class KMPAlgorithm {
    public static void main(String[] args) {
        String str1="BBC ABCDAB ABCDABCDABDE";
        String str2="ABCDABD";
        //String str2="BBC";
        int[] next=kmpNext(str2);
        System.out.println(Arrays.toString(next));
        int kmp = kmp(str1, str2, next);
        System.out.println(kmp);
    }

    //获取到字串的部分匹配值表
    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)这个条件满足时,部分匹配值就加一
            if(dest.charAt(i)==dest.charAt(j)){
                j++;
            }
            next[i]=j;
        }
        return next;
    }

    /**
     * //KMP搜索算法
     * @param str1 原字符串
     * @param str2 字串,匹配的字符串
     * @param next 部分匹配表
     * @return 如果是-1没有匹配到,否则,返回第一个匹配到的位置
     */
    public static int kmp(String str1,String str2,int[] next){
        //遍历str1,i指向str1,j指向str2
        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;
    }
}

五、贪心算法

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

retainAll方法

先求两个集合的交集,然后再将求出的交集返回给hashSet1

public class Test {
    public static void main(String[] args) {
        HashSet<String> hashSet1=new HashSet<>();
        hashSet1.add("1");
        hashSet1.add("2");
        hashSet1.add("100");
        HashSet<String> hashSet2=new HashSet<>();
        hashSet2.add("1");
        hashSet2.add("2");
        hashSet2.add("200");
        hashSet1.retainAll(hashSet2);
        System.out.println("hashset1="+hashSet1);//hsahset1=[1, 2]
    }
}

贪心算法:

package greed;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;

/**
 * @author : sky
 * @version : 1.0
 */
public class GreedAlgorithm {
    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("大连");
        //加入到map
        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<>();
        HashSet<String> tempMaxSet = new HashSet<>();
        //定义一个maxKey,保存在一次遍历过程中,能够覆盖最多未覆盖地区的电台的key
        //如果maxKey不为空,则会加入到selects中
        String maxKey=null;

        while (allAreas.size()!=0){//如果不为0,则表示还没有覆盖到所有的地区
            //每进行一次while循环,需要把maxKey置空
            maxKey=null;
            //遍历broadcasts,取出对应的key
            for(String key:broadcasts.keySet()){
                //每进行一次for,要把tempSet清空
                tempSet.clear();
                //当前这个key能够覆盖的地区
                HashSet<String> areas = broadcasts.get(key);
                tempSet.addAll(areas);
                //求tempSet和allAreas的交集,交集会赋给tempSet
                tempSet.retainAll(allAreas);
                //这里应当是当前map中的key与allAreas求得交集应当和maxKey指向的map中的value与allAreas求得的交集比较才对。
                if(maxKey!=null){
                    tempMaxSet=broadcasts.get(maxKey);
                    tempMaxSet.retainAll(allAreas);
                }
                //tempSet.size()>broadcasts.get(maxKey).size()体现出贪心算法的特点,每次都选择最优的
                //如果当前集合包含未覆盖地区的数量,比maxKey指向的集合未覆盖的地区还要多,就需要重置maxKey
                if (tempSet.size()>0 && (maxKey==null || tempSet.size()>tempMaxSet.size())){
                    maxKey=key;
                }
            }
            //如果maxKey!=null,就应该将maxKey加入到selects中
            if(maxKey!=null){
                selects.add(maxKey);
                //将maxKey指向的广播电台覆盖的地区,从allAreas去掉
                allAreas.removeAll(broadcasts.get(maxKey));
            }
        }
        System.out.println("得到的选择结果是:"+selects);
    }
}

在这里插入图片描述

最小生成树

六、Prim算法

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

package prim;

import java.util.Arrays;

/**
 * @author : sky
 * @version : 1.0
 */
public class Prim {
    public static void main(String[] args) {
        //测试图是否创建成功
        char[] data=new char[]{'A','B','C','D','E','F','G'};
        int vertexes=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(vertexes);
        //创建一个minTree对象
        MinTree minTree=new MinTree();
        minTree.createGraph(mGraph,vertexes,data,weight);
        //输出
        minTree.showGraph(mGraph);

        //测试prim
        System.out.println("-------------------------------------");
        minTree.prim(mGraph,1);
    }
}
//创建图
class MGraph{
    int vertexes;//表示图的节点的个数
    char[] data;//存放节点数据
    int[][] weight;//存放边,邻接矩阵

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

//创建最小生成树
class MinTree{
    /**
     * @param graph 图对象
     * @param vertexes 图对应的顶点个数
     * @param data 图中各个顶点的值
     * @param weight 图的邻接矩阵
     */
    public void createGraph(MGraph graph,int vertexes,char[] data,int[][] weight){
        for (int i = 0; i < vertexes; i++) {//把传进来的值传给图里面的data
            graph.data[i]=data[i];
            for (int j = 0; j < vertexes; 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 graph 图
     * @param v 表示从图的第几个顶点开始生成 如‘a’-》0 ‘b’-》1
     */
    public void prim(MGraph graph,int v){
        //visited标记节点是否被访问过,默认为0,表示没有访问过
        int[] visited = new int[graph.vertexes];
        /*for (int i = 0; i < graph.vertexes; i++) {
            visited[i]=0;
        }*/
        //把当前节点标记为已访问
        visited[v]=1;
        //h1和h2记录两个顶点的下标
        int h1=-1;
        int h2=-1;
        int minWeight=10000;//将他初始成一个较大的数,后面在遍历过程中会被替换
        for (int k = 1; k <graph.vertexes ; k++) {//因为有graph.vertexes个顶点,prim算法结束后,有graph.vertexes-1条边
            //这个是确定每一次生成的子图,和哪个节点的距离最近
            for (int i = 0; i < graph.vertexes; i++) {//i节点表示被访问过的顶点
                for (int j = 0; j < graph.vertexes; 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;
            //minWeight重新设置为最大值10000
            minWeight=10000;
        }
    }

}

七、Kruskal算法

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

package kruskal;

import jdk.internal.org.objectweb.asm.tree.IntInsnNode;

import java.util.Arrays;

/**
 * @author : sky
 * @version : 1.0
 */
public class Kruskal {
    public static void main(String[] args) {
        char[] vertexes={'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},
        };
        Kruskal kruskal = new Kruskal(vertexes, matrix);
        kruskal.print();
        System.out.println("-------------------------");
        kruskal.kruskal();
    }

    private int edgeNum;//边的个数
    private char[] vertexes;
    private int[][] matrix;
    //使用INF表示两个顶点不能连通
    private static final int INF=Integer.MAX_VALUE;

    //构造器
    public Kruskal(char[] vertexes, int[][] matrix) {//用的是复制拷贝的方式,不影响外面传进来的数组
        //初始化顶点数和变得个数
        int vlen=vertexes.length;
        this.vertexes=new char[vlen];
        for (int i = 0; i < vlen; i++) {
            this.vertexes[i]=vertexes[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 kruskal(){
        int index=0;//表示最后结果数组的索引
        int[] ends=new int[vertexes.length];//用于保存已有最小生成树中的每个顶点在最小生成树中的终点
        //创建结果数组,保存最后的最小生成树
        EData[] results=new EData[vertexes.length-1];
        //获取原始图中所有的边的集合,一共有12条边
        EData[] edges=getEdges();
        /*
        System.out.println(edges.length);*/
        //按照边的权值大小进行排序
        sortEdges(edges);
        System.out.println(Arrays.toString(edges));
        //遍历edges数组,将边添加到最小生成树中,并判断准备加入的边是否生成了回路,如果没有就加入到结果数组中
        for (int i = 0; i < edgeNum; i++) {
            //获取到第i条边的第一个顶点,即起点
            int p1=getPosition(edges[i].start);//E p1=4
            //获取到第i条边的第二个顶点,即终点
            int p2=getPosition(edges[i].end);//F p2=5
            //获取p1这个顶点在已有的最小生成树中的终点
            int m=getEnd(ends,p1);//E m=4
            //获取p2这个顶点在已有的最小生成树中的终点
            int n=getEnd(ends,p2);//F m=5
            //判断是否构成回路
            if(m!=n){//不等就没有构成回路
                ends[m]=n;//设置m在已有最小生成树的终点,以<E,F>为例,[0,0,0,0,5,0,0,0,0,0,0,0]
                //ends[n]=n; 没有必要写
                results[index++]=edges[i];
            }
        }
        //统计并打印最小生成树,输出results数组
        System.out.println("最小生成树:");
        System.out.println(Arrays.toString(results));
        //<E,F>,<C,D>,<D,E>,<B,F>,<E,G>,<A,B>,
    }

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

    /**
     * //对边进行排序处理,冒泡排序
     * @param edges 边的集合
     */
    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;
                }
            }
        }
    }

    /**
     * 给一个顶点,返回一个下标
     * @param ch 顶点的值,比如 'A'
     * @return 返回ch顶点对应的下标,找不到返回-1
     */
    private int getPosition(char ch){
        for (int i = 0; i <vertexes.length ; i++) {
            if(vertexes[i]==ch){
                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 < vertexes.length; i++) {
            for (int j = i+1; j <vertexes.length ; j++) {
                if(matrix[i][j]!=INF){
                    edges[index++]=new EData(vertexes[i],vertexes[j],matrix[i][j]);
                }
            }
        }
        return edges;
    }

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

//创建一个类,他的对象实例就表示一条边
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 +
                '}';
    }
}

最短路径

八、Dijkstra算法

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

package dijkstra;

import java.util.Arrays;

/**
 * @author : sky
 * @version : 1.0
 */
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=new Graph(vertex,matrix);
        graph.showGraph();
        //测试dij
        graph.dijkstra(2);
        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 dijkstra(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下标顶点到周围顶点的距离 和 周围顶点的前驱顶点
    private void update(int index){
        int len=0;
        //根据遍历邻接矩阵 matrix[index]这行
        for (int j = 0; j < matrix[index].length; j++) {
            //len含义:出发顶点到index的距离+从index顶点到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{//已经访问的顶点集合
    public int[] already_arr;//记录各个顶点是否访问过,1表示访问过,0表示未访问,会动态更新
    public int[] pre_visited;//每个下标对应的值为前一个顶点下标,会动态更新
    public int[] dis;//记录出发顶点到其他所有顶点的距离,会动态更新

    /**
     * //构造器
     * @param length 表示顶点的个数
     * @param index 表示出发顶点对应的下标,比如G点对应6
     */
    public VisitedVertex(int length,int index){
        this.already_arr=new int[length];
        this.pre_visited=new int[length];
        this.dis=new int[length];
        //初始化dis数组先全部填充为65535
        Arrays.fill(dis,65535);
        this.dis[index]=0;//把自己的改为0,设置出发顶点的访问距离为0
        this.already_arr[index]=1;
    }

    /**
     * 功能:判断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 顶点下标
     * @return 距离
     */
    public int getDis(int index){
        return dis[index];
    }

    /**
     * 功能:继续选择并返回新的访问节点,比如G访问完之后,就是A作为下一个顶点被访问
     * @return 返回下标
     */
    public int updateArr(){
        int min=65535,index=0;
        for (int i = 0; i < already_arr.length; i++) {
            if(already_arr[i]==0 && dis[i]<min){
                min=dis[i];
                index=i;
            }
        }
        //更新index顶点被访问过
        already_arr[index]=1;
        return index;
    }

    //显示最后的结果,即将三个数组的结果输出
    public void show(){
        System.out.println("----------------------");
        System.out.println(Arrays.toString(already_arr));
        System.out.println(Arrays.toString(pre_visited));
        //输出dis
        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.print("N");
            }
            count++;
        }
        System.out.println();
        //[1, 1, 1, 1, 1, 1, 1]
        //[6, 6, 0, 5, 6, 6, 0]
        //A(2) B(3) C(9) D(10) E(4) F(6) G(0)
    }
}

九、Floyd算法

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

三重for循环
在这里插入图片描述

package floyd;

import java.util.Arrays;

/**
 * @author : sky
 * @version : 1.0
 */
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 = 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);//初始化前驱就是自己
        }
    }

    //显示pre数组和dis数组
    public void show(){
        char[] vertex={'A','B','C','D','E','F','G'};
        System.out.println("距离数组:");
        for (int i = 0; i <dis.length ; i++) {
            //将dis数组输出
            for (int j = 0; j <dis.length ; j++) {
                System.out.print("("+vertex[i]+"到"+vertex[j]+"的最短路径是 "+dis[i][j]+") ");
            }
            System.out.println();
        }
        System.out.println("------------------------------------------------------");
        System.out.println("前驱节点数组:");
        for (int i = 0; i <pre.length ; i++) {
            //先将pre数组输出
            for (int j = 0; j <pre.length ; j++) {
                System.out.print(vertex[pre[i][j]]+"       ");
            }
            System.out.println();
        }
    }

    //弗洛伊德算法
    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]){
                        dis[i][j]=len;//更新距离
                        pre[i][j]=pre[k][j];//更新前驱顶点,终点的前驱
                    }
                }
            }
        }
    }
}

十、马踏棋盘算法、骑士周游算法

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

原始算法:太慢了!

package horse;

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

/**
 * @author : sky
 * @version : 1.0
 */
public class HorseChessboard {
    private static int X;//棋盘的列数
    private static int Y;//棋盘的行数
    //创建一个数组,标记棋盘的各个位置是否被访问过
    private static boolean[] visited;
    //使用一个属性,标记是否棋盘的所有位置都被访问
    private static boolean finished;//如果为true,表示成功

    /**
     * //根据当前位置,计算马儿还能走哪些位置,并放入到一个集合中,最多有8个位置
     * Point java中的一个类,表示点
     * @param curPoint 当前的点
     * @return 返回一个集合
     */
    public static ArrayList<Point> next(Point curPoint){
        //创建一个ArrayList
        ArrayList<Point> ps = new ArrayList<>();
        //创建一个Point
        Point p1 = new Point();
        //x是列,y是行

        //表示马儿可以走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;
    }

    /**
     * 完成骑士周游问题的算法
     * @param chessboard 棋盘
     * @param row 代表马儿当前位置的行,从0开始
     * @param column 代表马儿当前位置的列,从0开始
     * @param step 表示马儿走第几步,初始位置就是第一步
     */
    public static void travelChessboard(int[][] chessboard,int row,int column,int step){
        chessboard[row][column]=step;
        //4*8+4=36,因为visited是一个一维数组
        visited[row*X+column]=true;//标记该位置已经访问
        //获取当前位置可以走的下一个位置的集合
        ArrayList<Point> next = next(new Point(column, row));//x是列,y是行,要对应
        //遍历ArrayList
        while(!next.isEmpty()){
            Point p = next.remove(0);//取出一个可以走的位置
            //判断该点是否已经访问过
            if(!visited[p.y*X+p.x]){//说明还没有访问过
                travelChessboard(chessboard,p.y,p.x,step+1);
            }
        }
        //判断马儿是否完成了任务,使用step的应该走的步数比较,如果没有,则表示没有完成任务,将整个棋盘置0
        if(step<X*Y && !finished){
            //step<X*Y成立的情况有两种:
            //1.棋盘到目前位置仍然没有走完
            //2.棋盘处于一个回溯过程
            chessboard[row][column]=0;
            visited[row*X+column]=false;
        }else{
            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();
        travelChessboard(chessboard,row-1,column-1,1);
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));//22014ms
        //输出棋盘的情况
        for (int[] rows:chessboard){
            for (int columns:rows){
                System.out.print(columns+"\t");
            }
            System.out.println();
        }
    }
}

使用贪心算法优化:
在这里插入图片描述

package horse;

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

/**
 * @author : sky
 * @version : 1.0
 */
public class HorseChessboard {
    private static int X;//棋盘的列数
    private static int Y;//棋盘的行数
    //创建一个数组,标记棋盘的各个位置是否被访问过
    private static boolean[] visited;
    //使用一个属性,标记是否棋盘的所有位置都被访问
    private static boolean finished;//如果为true,表示成功

    /**
     * //根据当前位置,计算马儿还能走哪些位置,并放入到一个集合中,最多有8个位置
     * Point java中的一个类,表示点
     * @param curPoint 当前的点
     * @return 返回一个集合
     */
    public static ArrayList<Point> next(Point curPoint){
        //创建一个ArrayList
        ArrayList<Point> ps = new ArrayList<>();
        //创建一个Point
        Point p1 = new Point();
        //x是列,y是行

        //表示马儿可以走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;
                }
            }
        });
    }

    /**
     * 完成骑士周游问题的算法
     * @param chessboard 棋盘
     * @param row 代表马儿当前位置的行,从0开始
     * @param column 代表马儿当前位置的列,从0开始
     * @param step 表示马儿走第几步,初始位置就是第一步
     */
    public static void travelChessboard(int[][] chessboard,int row,int column,int step){
        chessboard[row][column]=step;
        //4*8+4=36,因为visited是一个一维数组
        visited[row*X+column]=true;//标记该位置已经访问
        //获取当前位置可以走的下一个位置的集合
        ArrayList<Point> next = next(new Point(column, row));//x是列,y是行,要对应
        //对next进行排序,排序的规则集合中所有元素下一步位置的个数进行非递减排序
        sort(next);
        //遍历ArrayList
        while(!next.isEmpty()){
            Point p = next.remove(0);//取出一个可以走的位置
            //判断该点是否已经访问过
            if(!visited[p.y*X+p.x]){//说明还没有访问过
                travelChessboard(chessboard,p.y,p.x,step+1);
            }
        }
        //判断马儿是否完成了任务,使用step的应该走的步数比较,如果没有,则表示没有完成任务,将整个棋盘置0
        if(step<X*Y && !finished){
            //step<X*Y成立的情况有两种:
            //1.棋盘到目前位置仍然没有走完
            //2.棋盘处于一个回溯过程
            chessboard[row][column]=0;
            visited[row*X+column]=false;
        }else{
            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();
        travelChessboard(chessboard,row-1,column-1,1);
        long end = System.currentTimeMillis();
        System.out.println("耗时:"+(end-start));//20ms
        //输出棋盘的情况
        for (int[] rows:chessboard){
            for (int columns:rows){
                System.out.print(columns+"\t");
            }
            System.out.println();
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值