A*搜索算法(含Java源代码)

news2025/1/15 12:48:36

 前言

本来是想写一块的,但是为了这个国庆的专属勋章就分开写了,这个侧重还是对作业题目要求的实现。

课题目的

理解 A Star 算法设计流程。

理解 A Star 算法的启发式函数的作用。

掌握 A Start 解决搜索问题的过程,能够应用 A Star 算法解决迷宫问题

A Star 算法理论

A Star 搜索是一种重要的启发式搜索算法,它始终从打开的表中选择估计成本最低的节点作为新状态。对于任何状态,其估价函数由下式定义:

f(s)=g(s)+h(s)

g(s)是从起点到当前状态 的实际代价,这是一个已知值。以迷宫问题为例,正是从起始状态移动到 的步骤数。 称为启发式函数,它估计从 到目标状态的最优路径的估算代价。设 表示从当前状态 到目标状态的最优路径(通常事先未知)。如果满足:

我们说 是可采纳性(admissible)(即 的下界)。然后我们有一个关于 A Start 的重要定理:具有可采纳性启发式的 A Start 搜索可以保证找到搜索问题的最优路径。请注意,对于本实验中的迷宫问题,基于欧几里得或曼哈顿距离的启发式函数是具有可采纳性

欧几里得距离:

欧氏距离定义: 欧氏距离( Euclidean distance)是一个通常采用的距离定义,它是在m维空间中两个点之间的真实距离。

两点之间线段最短,所以采用欧几里得距离能够保证找到搜索问题的最优路径。

曼哈顿距离:

曼哈顿距离也叫出租车距离,用来标明两个点在标准坐标系上的绝对轴距总和。

因为这个迷宫问题路径的选择只能从上下左右四个方向移动,不能够斜着移动,故而他的最短距离实际上就是当前节点到终点之间x,y差的绝对值的和,也就是曼哈顿距离,所以采用曼哈顿距离能够保证找到搜索问题的最优路径。

实验结果与分析

使用语言:Java

使用文件:smallMaze.txt

使用软件:IDEA

运行结果如下:

1.曼哈顿距离

2.欧几里得距离

命令行运行:

C:\Users\25496\Desktop\artificial intelligence>javac -encoding utf-8 bigMazeSearch.java

C:\Users\25496\Desktop\artificial intelligence>java bigMazeSearch

由于编码问题,需要采用utf-8编码运行。

获取文本数据

因为地图不是自己定义的,是在文本文件里面的,所以需要我们自己进行读取,把数据拿出来放在二维的字符数组里面,同时还是需要标记他的起点和终点。因为都是字符,所以读取一行字符串,把字符放在字符数组里面就行了。

算法实现

A*搜索算法-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_64066303/article/details/133325621?spm=1001.2014.3001.5501A*搜索算法(Java实现)_java a*算法-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Taylar_where/article/details/90298610人工智能大作业——A*算法迷宫寻路问题_a*算法实现迷宫寻路功能-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_46037153/article/details/107136560机器人路径规划算法(十一)A-star算法 - Mronne's Blogicon-default.png?t=N7T8https://mronne.github.io/2020/04/03/%E6%9C%BA%E5%99%A8%E4%BA%BA%E8%B7%AF%E5%BE%84%E8%A7%84%E5%88%92%E7%AE%97%E6%B3%95-%E5%8D%81%E4%B8%80-A-star-%E7%AE%97%E6%B3%95.htmlA*搜索算法(A-Star Search)简介及保姆级代码解读 (qq.com)icon-default.png?t=N7T8https://mp.weixin.qq.com/s?__biz=MzU1NjEwMTY0Mw==&mid=2247554483&idx=1&sn=c3e6a3259f1b469eded07a5498790186&chksm=fbc86cd7ccbfe5c12f4dddf139f73cb5c2879044867bced71edb0243b155f3d68eda50d0db6a&scene=27

//准备两个链表来储存需要选择的节点以及已经走过的节点
public static LinkedList<Grid> openList = new LinkedList<Grid>();
public static LinkedList<Grid> closedList = new LinkedList<Grid>();
public static HashMap<Grid, Grid> resultMap = new HashMap<Grid, Grid>();

把需要探索的节点放在openList链表里面,把已经探索过的节点放在closedList链表里面,resultMap用来储存当前节点和邻居节点的关系(如果邻居节点满足要求(没有越界,没有已经在openList和closeList里面),就用邻居节点作为键,当前节点作为值,加入到resultMap里面),目的是为了之后能够根据键值对的关系,能够从终点到达起点。

从endGrid表示终点,因为对应关系已经有了,先将当前节点作为键找到他对应的值,表示在迷宫中的含义就是找到他的上一步,更好将他的上一步作为键,找到他的上上步,重复这个过程直到达到起点。

注:只能从终点往起点找,不能从起点往终点找。你只能知道你的上一步在哪里,但是你无法知道你的下一步在哪里。

        while (true) {

            Grid grid = resultMap.get(endGrid);

            endGrid = grid;

            if (endGrid == null) {

                break;

            }

            //到达起点

            if (endGrid.x == startGrid.x && endGrid.y == startGrid.y) {

                // mazeArray[endGrid.x][endGrid.y] = '#';

                break;

            }

            mazeArray[endGrid.x][endGrid.y] = '@';

            //最短路径

            shortestPath++;

        }

//把方格抽象成一个类
public static class Grid {
    private int x;//横坐标
    private int y;//纵坐标
    //fn=hn+gn
    private int fn;//估计函数
    private int hn;//估计代价
    private int gn;//实际代价

    private Grid present;//当前节点
    //构造方法
    public Grid(int x, int y) {
        this.x = x;
        this.y = y;
    }
    public int getX() {
        return x;
    }
    public void setX(int x) {
        this.x = x;
    }
   public int getY() {
        return y;
    }
    public void setY(int y) {
        this.y = y;
    }
    //实例化一个方格节点
    public void initGrid(Grid present, Grid end) {
        this.present = present;
        //计算gn
        if (present != null) {
            //实际的代价加一相当于前进了一步
            this.gn = present.gn + 1;
        } else {
            this.gn = 1;
        }
        //计算hn的大小(这里用的估计代价是曼哈顿距离
        //this.hn = Math.abs(this.x - end.x) + Math.abs(this.y - end.y);
        //欧几里得距离作为启发函数
        this.hn= (int) Math.sqrt(Math.pow(this.x-end.x,2)+Math.pow(this.y-end.y,2));
        //计算fn的大小
        this.fn = this.gn + this.hn;
    }
    public String toString() {
        return "(" + this.x + "," + this.y + ")" + "fn:" + this.fn + " gn:" + this.gn + " hn:" + this.hn;
    }
    @Override
    public int hashCode() {
        int tmp = (this.y + (this.x + 1) / 2);
        return x + (tmp * tmp);
    }
    //重写equals方法,不然比较的是地址值
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Grid other = (Grid) obj;
        if (this.x == other.x && this.y == other.y) {
            return true;
        }
        return false;
    }
}

首先把起点添加到openList链表里面,表示待探索他,之后把他从openList链表中删除,添加到closedList链表里面,表示他已经被探索过了,之后就是寻找他的邻居节点是否有满足要求的,有满足要求的就将所有满足要求的添加到openList里面,这里不用管他的顺序,所以最后拿出来探索的节点是通过方法查找的,探索的是Fn值最小节点的邻居。

整个结束就是到达终点,或者就是没有答案,openList里面没有元素了,也会跳出。

//A*算法的实现(需要开始位置和结束位置

private static void AStarSearch(Grid start, Grid end, char[][] mazeArray) {
    //将起点加入链表之后开始寻路
    openList.add(start);
    //链表不为空(没有最后没有到达终点也会停止)
    while (openList.size() > 0) {
        //找到在需要选择的节点中最小的那个节点,之后需要用它进行扩展
        Grid nowGrid = findMinGrid(openList);
        //从中删除最小的那个节点
        openList.remove(nowGrid);
        //将这个节点添加到已经走过的路径中
        closedList.add(nowGrid);
        //寻找他的相邻节点,把合法的节点都添加进来
        LinkedList<Grid> neighbors = findNeighbors(nowGrid, openList, closedList, mazeArray);
        for (Grid grid : neighbors) {
            //判断集合中是否添加了grid节点
            if (!openList.contains(grid)) {
                //进行初始化
                grid.initGrid(nowGrid, end);
                //添加到待搜索集合中
                openList.add(grid);
                if (grid != null && nowGrid != null) {
                    //子节点作为键,父节点作为值
                    resultMap.put(grid, nowGrid);
                }
                //System.out.println(grid + " " + resultMap.get(grid));
            }
        }
        //判断是否可以结束
        for (Grid grid : openList) {
            if ((grid.x == end.x) && (grid.y == end.y)) {
                closedList.add(end);
                return;
            }
        }
    }
}

探索相邻的节点(首先判断四个方向的节点是否合法,合法才能添加)

//把所有符合要求的相邻节点都放置到list集合里面里面
private static LinkedList<Grid> findNeighbors(Grid grid, LinkedList<Grid> openList, LinkedList<Grid> closeedList, char[][] mazeArray) {
    LinkedList<Grid> list = new LinkedList<Grid>();
    //判断相邻节点的合法性
    if (legitimacy(grid.x, grid.y - 1, openList, closeedList, mazeArray)) {//下(用数组来看的话他就是往上)
        list.add(new Grid(grid.x, grid.y - 1));
    }
    if (legitimacy(grid.x, grid.y + 1, openList, closeedList, mazeArray)) {//上
        list.add(new Grid(grid.x, grid.y + 1));
    }
    if (legitimacy(grid.x - 1, grid.y, openList, closeedList, mazeArray)) {//左
        list.add(new Grid(grid.x - 1, grid.y));
    }
    if (legitimacy(grid.x + 1, grid.y, openList, closeedList, mazeArray)) {//右
        list.add(new Grid(grid.x + 1, grid.y));
    }
    return list;
}

节点是否合法就是是否越界,以及是否已经探索了,如果探索了就不需要再添加了。

//判断当前节点是否合法
private static boolean legitimacy(int x, int y, LinkedList<Grid> openList, LinkedList<Grid> closedList, char[][] mazeArray) {
    //判断坐标是否越界
    if (x < 0 || x >= mazeArray.length || y < 0 || y >= mazeArray[0].length) {
        return false;
    }
    //判断当前节点是否是障碍
    if (mazeArray[x][y] == '%') {
        return false;
    }
    //判断当前节点是否被添加
    if (contains(openList, x, y)) {
        return false;
    }
    //判断当前节点是否已经走过了
    if (contains(closedList, x, y)) {
        return false;
    }
    //所以条件都满足,他就是合法的
    return true;
}

区分不同的节点就是看他的x,y坐标是否都一致,如果都相同就是同一个节点。

//判断当前的节点是否已经添加
private static boolean contains(LinkedList<Grid> grids, int x, int y) {
    for (Grid grid : grids) {
        //坐标在grids集合中有就是已经被添加了
        if ((grid.x == x) && (grid.y == y)) {
            return true;
        }
    }
    return false;
}

因为没有采用优先队列的数据结构,所以需要自己进行查找,调用这个方法可以返回集合中fn最小的节点。

//返回最小的那个节点
private static Grid findMinGrid(LinkedList<Grid> selectList) {

    Grid tmpgrid = selectList.get(
0);
   
for (Grid grid : selectList) {
       
//更新最小节点
       
if (grid.fn < tmpgrid.fn) {
            tmpgrid = grid;
        }
    }
   
return tmpgrid;

 

计算最短路径:用一个变量去累计,从终点到起点这个过程需要的次数。

扩展节点个数:就是closedList链表里面元素的个数。

算法运行时间:在运行算法运行前放一个时间戳,算法运行后放一个时间戳,两者之间的差值就是算法运行时间花费的毫秒值。

 得到最短路径

这里主要就是写一个前面没有提到的。

最短路径我在网上没有看到Java的实现,写的都是伪代码,思路都很简单,就是从终点往前面找,一直到起点就行了,不能从起点往终点直接实现。

举一个例子,假设起点在左上方,终点在右下方,前面一直在右上方找,突然发现代价大于走下方的代价,或者是他往右上方找完之后没路了,那他直接就跳到走下方去寻找,最后呈现出来的路径进行从一边直接跳到另一边这种,这显示和实际不符合,所以需要从终点往起点来寻找。

我这里是用HashMap来实现的,用当前的节点作为值,相邻满足要求的节点作为键,之后就只需要从终点一直把值的结果作为键,就可以得到前一步的结果了,这里我之前是用TreeMap但是失败了,非常诡异,他键和值都有,但是用那个键去查找他的值结果是null,很离谱。

完整代码 

import java.io.*;
import java.util.HashMap;
import java.util.LinkedList;

public class bigMazeSearch {
    public static void main(String[] args) throws IOException {
        //定义文本文件的路径
        File file = new File("C:\\Users\\25496\\Desktop\\artificial intelligence\\bigMaze.txt");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        //定义字符二维数组的行数和列数
        int rows = 50;
        int cols = 50;
        //创建字符二维数组
        char[][] mazeArray = new char[rows][cols];
        String line;

        //记录起点
        Grid startGrid = new Grid(0, 0);
        //记录终点
        Grid endGrid = new Grid(0, 0);

        //记录最短路径和扩展路径的步数
        int shortestPath=0;
        int expandingCrackingPath=0;

        int row = 0;
        while ((line = bufferedReader.readLine()) != null && row < rows) {//逐行读取内容
            //System.out.println(line);
            for (int col = 0; col < cols && col < line.length(); col++) {
                //把读取的字符储存到字符数组中
                mazeArray[row][col] = line.charAt(col);
                //更新起点
                if (mazeArray[row][col] == 'S') {
                    startGrid.setX(row);
                    startGrid.setY(col);
                } else if (mazeArray[row][col] == 'E') {
                    //更新终点
                    endGrid.setX(row);
                    endGrid.setY(col);
                }
            }
            row++;
        }
        bufferedReader.close();
        fileReader.close();
        //输出二维字符数组的内容
        System.out.println("原始地图是:");
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < cols; j++) {
                System.out.print(mazeArray[i][j] + " ");
            }
            System.out.println();
        }
        //开始时间(获取当前时间戳的毫秒值)
        long startTime=System.currentTimeMillis();
        //调用A*搜索算法
        AStarSearch(startGrid, endGrid, mazeArray);
        //结束时间
        long endTime= System.currentTimeMillis();
        //System.out.println("算法调用");
        //标记路径
        while (true) {
            Grid grid = resultMap.get(endGrid);
            endGrid = grid;
            if (endGrid == null) {
                break;
            }
            //到达起点
            if (endGrid.x == startGrid.x && endGrid.y == startGrid.y) {
                // mazeArray[endGrid.x][endGrid.y] = '#';
                break;
            }
            mazeArray[endGrid.x][endGrid.y] = '@';
            //最短路径
            shortestPath++;
        }
        System.out.println("\n\n路径是:");
        //路径地图是
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < cols; j++) {
            	System.out.print(mazeArray[i][j] + " ");  
            }
            System.out.println();
        }
        expandingCrackingPath=closedList.size();
        long timeElapsed=endTime-startTime;
        System.out.println("最短路径是:"+shortestPath+"步");
        System.out.println("扩展节点数是:"+expandingCrackingPath);
        System.out.println("算法的运行时间是(毫秒)"+timeElapsed);
    }

    //把方格抽象成一个类
    public static class Grid {
        private int x;//横坐标
        private int y;//纵坐标
        //fn=hn+gn
        private int fn;//估计函数
        private int hn;//估计代价
        private int gn;//实际代价

        private Grid present;//当前节点

        //构造方法
        public Grid(int x, int y) {
            this.x = x;
            this.y = y;
        }


        public int getX() {
            return x;
        }

        public void setX(int x) {
            this.x = x;
        }

        public int getY() {
            return y;
        }

        public void setY(int y) {
            this.y = y;
        }

        //实例化一个方格节点
        public void initGrid(Grid present, Grid end) {
            this.present = present;

            //计算gn
            if (present != null) {
                //实际的代价加一相当于前进了一步
                this.gn = present.gn + 1;
            } else {
                this.gn = 1;
            }

            //计算hn的大小(这里用的估计代价是曼哈顿距离
            this.hn = Math.abs(this.x - end.x) + Math.abs(this.y - end.y);

            //计算fn的大小
            this.fn = this.gn + this.hn;
        }

        public String toString() {
            return "(" + this.x + "," + this.y + ")" + "fn:" + this.fn + " gn:" + this.gn + " hn:" + this.hn;
        }

        @Override
        public int hashCode() {
            int tmp = (this.y + (this.x + 1) / 2);
            return x + (tmp * tmp);
        }

        //重写equals方法,不然比较的是地址值
        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            Grid other = (Grid) obj;
            if (this.x == other.x && this.y == other.y) {
                return true;
            }
            return false;
        }
    }

    //准备两个链表来储存需要选择的节点以及已经走过的节点
    public static LinkedList<Grid> openList = new LinkedList<Grid>();
    public static LinkedList<Grid> closedList = new LinkedList<Grid>();
    public static HashMap<Grid, Grid> resultMap = new HashMap<Grid, Grid>();

    //A*算法的实现(需要开始位置和结束位置
    private static void AStarSearch(Grid start, Grid end, char[][] mazeArray) {

        //将起点加入链表之后开始寻路
        openList.add(start);

        //链表不为空(没有最后没有到达终点也会停止)
        while (openList.size() > 0) {
            //找到在需要选择的节点中最小的那个节点,之后需要用它进行扩展
            Grid nowGrid = findMinGrid(openList);

            //从中删除最小的那个节点
            openList.remove(nowGrid);
            //将这个节点添加到已经走过的路径中
            closedList.add(nowGrid);

            //寻找他的相邻节点,把合法的节点都添加进来
            LinkedList<Grid> neighbors = findNeighbors(nowGrid, openList, closedList, mazeArray);

            for (Grid grid : neighbors) {
                //判断集合中是否添加了grid节点
                if (!openList.contains(grid)) {
                    //进行初始化
                    grid.initGrid(nowGrid, end);
                    //添加到待搜索集合中
                    openList.add(grid);
                    if (grid != null && nowGrid != null) {
                        //子节点作为键,父节点作为值
                        resultMap.put(grid, nowGrid);
                    }
                    //System.out.println(grid + " " + resultMap.get(grid));

                }
            }
            //判断是否可以结束
            for (Grid grid : openList) {
                if ((grid.x == end.x) && (grid.y == end.y)) {
                    closedList.add(end);
                    return;
                }
            }
        }
    }

    //把所有符合要求的相邻节点都放置到list集合里面里面
    private static LinkedList<Grid> findNeighbors(Grid grid, LinkedList<Grid> openList, LinkedList<Grid> closeedList, char[][] mazeArray) {
        LinkedList<Grid> list = new LinkedList<Grid>();
        //判断相邻节点的合法性
        if (legitimacy(grid.x, grid.y - 1, openList, closeedList, mazeArray)) {//下(用数组来看的话他就是往上)
            list.add(new Grid(grid.x, grid.y - 1));
        }
        if (legitimacy(grid.x, grid.y + 1, openList, closeedList, mazeArray)) {//上
            list.add(new Grid(grid.x, grid.y + 1));
        }
        if (legitimacy(grid.x - 1, grid.y, openList, closeedList, mazeArray)) {//左
            list.add(new Grid(grid.x - 1, grid.y));
        }
        if (legitimacy(grid.x + 1, grid.y, openList, closeedList, mazeArray)) {//右
            list.add(new Grid(grid.x + 1, grid.y));
        }
        return list;
    }

    //判断当前节点是否合法
    private static boolean legitimacy(int x, int y, LinkedList<Grid> openList, LinkedList<Grid> closedList, char[][] mazeArray) {
        //判断坐标是否越界
        if (x < 0 || x >= mazeArray.length || y < 0 || y >= mazeArray[0].length) {
            return false;
        }
        //判断当前节点是否是障碍
        if (mazeArray[x][y] == '%') {
            return false;
        }
        //判断当前节点是否被添加
        if (contains(openList, x, y)) {
            return false;
        }
        //判断当前节点是否已经走过了
        if (contains(closedList, x, y)) {
            return false;
        }
        //所以条件都满足,他就是合法的
        return true;
    }

    //判断当前的节点是否已经添加
    private static boolean contains(LinkedList<Grid> grids, int x, int y) {
        for (Grid grid : grids) {
            //坐标在grids集合中有就是已经被添加了
            if ((grid.x == x) && (grid.y == y)) {
                return true;
            }
        }
        return false;
    }

    //返回最小的那个节点
    private static Grid findMinGrid(LinkedList<Grid> selectList) {

        Grid tmpgrid = selectList.get(0);
        for (Grid grid : selectList) {
            //更新最小节点
            if (grid.fn < tmpgrid.fn) {
                tmpgrid = grid;
            }
        }
        return tmpgrid;
    }
}

 直接输出太大了,不能截图,这是复制到记事本里面的结果。

 扩展

用idea还可以通过ANSI转义码来实现的输出不同的颜色,eclipse的话还需要自己安装插件才行,用颜色的好处就很对比更加明显了。

 代码的启发式函数采用的是曼哈顿,欧几里得的话还需要自己去实现哦。(这个最短路径是一样的,但是扩展节点的数量不一样)

 总结

本来还想写一起的,但还是分开的,这个思路网上的都是一样的,就是实现不相同。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1056437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

凉鞋的 Godot 笔记 101. Hello Godot!

101. Hello Godot 学习任何一门技术&#xff0c;第一件事就是先完成 Hello World&#xff01;的输出 所以我们也来先完成 Godot 的 Hello World。 我们所使用的 Godot 版本是 4.x 版本。 安装的过程就不给大家展示了&#xff0c;笔者更推荐初学者用 Steam 版本的 Godot&…

Scala第十八章节

Scala第十八章节 scala总目录 文档资料下载 章节目标 掌握Iterable集合相关内容.掌握Seq集合相关内容.掌握Set集合相关内容.掌握Map集合相关内容.掌握统计字符个数案例. 1. Iterable 1.1 概述 Iterable代表一个可以迭代的集合, 它继承了Traversable特质, 同时也是其他集合…

学习开发一个RISC-V上的操作系统(汪辰老师) — 环境配置

前言 &#xff08;1&#xff09;此系列文章是跟着汪辰老师的RISC-V课程所记录的学习笔记。 &#xff08;2&#xff09;该课程相关代码gitee链接&#xff1b; &#xff08;3&#xff09;PLCT实验室实习生长期招聘&#xff1a;招聘信息链接 &#xff08;4&#xff09;在学习汪辰老…

正则表达式验证和跨域postmessage

1.用正则表达式验证用户名 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title>…

msvcp120.dll放在哪个文件夹?msvcp120.dll丢失解决方法详细分析

Msvcp120.dll 丢失可能会导致一些基于 Microsoft Visual C 编写的程序和游戏无法正常运行。Msvcp120.dll 是 Microsoft Visual C Redistributable 的一个组件&#xff0c;它包含了 C 运行时库&#xff0c;这些库在运行程序时会被加载到内存中。如果该文件丢失或损坏&#xff0c…

多目标平衡黏菌算法(MOEOSMA)求解八个现实世界受约束的工程问题

目录 1 受约束的工程问题 1.1 减速器设计问题(Speed reducer design problem) 1.2 弹簧设计问题(Spring design problem) 1.3 静压推力轴承设计问题(Hydrostatic thrust bearing design problem) 1.4 振动平台设计问题(Vibrating platform design problem) 1.5 汽车侧面碰…

18.示例程序(编码器接口测速)

STM32标准库开发-各章节笔记-查阅传送门_Archie_IT的博客-CSDN博客https://blog.csdn.net/m0_61712829/article/details/132434192?spm1001.2014.3001.5501 main.c #include "stm32f10x.h" // Device header #include "Delay.h" #incl…

数据结构:KMP算法的原理图解和代码解析

文章目录 应用场景算法方案算法原理完整代码 本篇总结的是关于串中的KMP算法解析 应用场景 现给定两个串&#xff0c;现在要看较短的一个串是不是较长的串的子串&#xff0c;如果是就输出子串后面的内容&#xff0c;如果不是则输出Not Found 能匹配到&#xff1a; 长串&…

基于SSM的连锁经营商业管理系统设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;采用JSP技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#x…

JavaSE | 初识Java(五) | 方法的使用

方法就是一个代码片段&#xff0c; 类似于 C 语言中的 " 函数 "。 方法可以是我们代码逻辑更清晰&#xff0c;并且可以服用方法使代码更简洁 方法语法格式 // 方法定义 修饰符 返回值类型 方法名称([参数类型 形参 ...]){ 方法体代码; [return 返回值]; } 实例&…

自媒体文章改写工具-自媒体文章改写软件

自媒体时代已然来临&#xff0c;每个人都有机会成为自己的内容创作者&#xff0c;分享自己的观点和故事。在竞争激烈的自媒体领域&#xff0c;如何让自己的文章脱颖而出&#xff0c;吸引更多读者成为了一个重要的问题。 自媒体文章改写是一项旨在提高文章原创性和吸引力的关键任…

Arcgis打开影像分析窗口没反应

Arcgis打开影像分析窗口没反应 问题描述 做NDVI计算的时候&#xff0c;一直点击窗口-影像分析&#xff0c;发现影像分析的小界面一直不跳出来。 原因 后来发现是被内容列表给遮住了&#xff0c;其实是已经出来了的。。 拖动内容列表就能找到。 解决方案 内容列表和影像分…

热点文章采集-热点资讯采集工具免费

在信息时代&#xff0c;掌握热点资讯、了解热门时事、采集热门文章是许多自媒体从业者和信息追踪者的重要任务。然而&#xff0c;这并不是一项容易的任务。信息的海洋庞大而繁杂&#xff0c;要从中捞取有价值的热点和文章需要耗费大量时间和精力。 热点资讯采集&#xff1a;信息…

[Linux 基础] 一篇带你了解linux权限问题

文章目录 1、Linux下的两种用户2、文件类型和访问权限&#xff08;事物属性&#xff09;2.1 Linux下的文件类型2.2 基本权限2.3 文件权限值的表示方法&#xff08;1&#xff09;字符表示方法&#xff08;2&#xff09;8进制数值表示方法 2.4 文件访问权限的相关设置方法(1) chm…

番外4:VMware安装

step4: 安装过程中&#xff0c;有些选项不需要点&#xff08;安装地址建议选C盘或默认&#xff0c;装载在其他盘后续会报错&#xff09;&#xff0c;如&#xff1a; may error&#xff08;本人猜测安装虚拟机完整版需要C盘的一些桥插件支持&#xff09;: step5: 安装虚拟机成功…

爆文采集器-热点爆文章采集工具

当信息在互联网上迅速传播&#xff0c;新闻迅速变化&#xff0c;自媒体创作者和信息追踪者们都希望能够捕捉到瞬息万变的热点话题&#xff0c;以吸引更多的关注和流量。爆文采集器成为了一项关键的工具&#xff0c;有助于他们在信息的海洋中找到并分享最新、最热门的内容。 热点…

MAC手动修复『已损坏』问题 终端运行命令报错处理

安装一些第三方软件会出现已损坏的报错提醒&#xff0c;需要用命令sudo xattr -rd com.apple.quarantine进行修复&#xff0c;但是终端提示命令错误&#xff0c;怎么版 错误有几种&#xff1a; No module named ‘pkg_resources’ 这是mac电脑上python2&#xff0c;python3并…

eBPF 的发展历程及工作原理

目录 eBPF 是什么 掌握 eBPF 是不是得先成为内核开发者&#xff1f; eBPF 的发展历程是什么样的? eBPF 是怎么工作的? eBPF 是万能的吗? 小结 eBPF 是什么 eBPF 是什么呢&#xff1f; 从它的全称“扩展的伯克利数据包过滤器 (Extended Berkeley Packet Filter)” 来看…

2023年(24届)计算机保研推免经历(保研边缘人)| (吉大AI、华师cs、东南、浙软)

前言 写下这篇博客的原因在于自己保研期间刷了很多很多的经验贴&#xff0c;听很多学长学姐讲述了自己的经历&#xff0c;感觉收获颇丰。所以希望能将自己的经历也分享下去&#xff0c;如果以后的学弟学妹能获得一点点帮助&#xff0c;那就再好不过了。 保研基础知识&#xff0…

借助ChatGPT的神奇力量,解锁AI无限可能!

&#x1f680;欢迎来到本文&#x1f680; &#x1f349;个人简介&#xff1a;陈童学哦&#xff0c;目前学习C/C、算法、Python、Java等方向&#xff0c;一个正在慢慢前行的普通人。 &#x1f3c0;系列专栏&#xff1a;陈童学的日记 &#x1f4a1;其他专栏&#xff1a;CSTL&…