图论中的算法

news2024/11/30 12:36:19

图论的概念:图论是数学的一个分支,它是以图为研究对象,图论中的图是由若干个给定的点及连接两点的线所构成的图形,这种图形通常用来描述某些实体之间的某种特定的关系,用点代表实体,用连接两点之间的线表示两个实体之间具有某种关系。

图的分类:

  • 无权无向图

 无向就是可以互相通向,没有进行方向的限制,就好比双向指向:

  • 无权有向图

无权就是好比每条路线占的权重一致,没有区别,故我们可以把无权图假设为每个权重都是1的有权图:

  • 有权无向图

  • 有权有向图

BFS和DFS

我们首先来了解什么是BFS?什么又是DFS?

       DFS俗称深度优先算法,有一种不撞南墙不回头的感觉,认准一条路,一致往下走,直到走不通位置,然后再重新返回再重复刚才的操作,直到找到目标节点为止。DFS关键点是递归以及回溯,一般用栈进行操作。在图中我们经常这样:

经典算法题之(知识补充)------ BFS和DFS的感性认识_ProLayman的博客-CSDN博客

       BFS俗称广度优先算法,相对于深度优先算法,如果把深度优先算法当成是一个莽夫的话,广度优先算法像是一个文人墨客,广度优先算法在面临一个路口时,把所有的路口记录下来,然后选择其中一个进入,然后再回退再进入另一个路口,直到找到目标节点为止。BFS关键点是状态的选取和标记,一般用队列去解决问题,在图中我们经常这样:

 图的存储结构

  • 邻接矩阵

概念所谓邻接矩阵存储结构就是每个顶点用一个一维数组存储边的信息,这样所有点合起来就是用矩阵表示图中各顶点之间的邻接关系。所谓矩阵其实就是二维数组。对于有n个顶点的图 G=(V,E) 来说,我们可以用一个 n× n 的矩阵A来表示G中各顶点的相邻关系

 对应的邻接矩阵为:

 与对应点直接相连为1,不直接相连为0

构建邻接矩阵:

package graphtheory;

import java.util.Arrays;

/**
 * 图的表示--使用邻接矩阵
 */
public class Graph01 {
    private char[] V;//顶点上的值

    private Vertex[] vertexs;//顶点数组

    private int N;


    //邻接矩阵
    private int[][] adj;

    //图的构造函数
    public Graph01(char[] arr) {//{'A','E','F','G','H','P'}
        //拿到数组的长度
        int length = arr.length;
        this.N = length;
        V = new char[length];
        //arr元素赋值 到V
        this.V = Arrays.copyOf(arr, length);
        //构建图中的结点
        vertexs = new Vertex[length];
        for (int i = 0; i < length; i++) {
            vertexs[i] = new Vertex(i,this.V[i]);//

        }
        this.adj = new int[length][length];
    }

    //打印邻接矩阵
    public void show() {
        System.out.print("    ");
        for (int i = 0; i < this.N; i++) {
            System.out.format("%4c", this.V[i]);
        }
        System.out.println();
        for (int i = 0; i < this.N; i++) {
            System.out.format("%4c",this.V[i]);
            for (int j = 0; j < this.N; j++) {
                System.out.format("%4s", this.adj[i][j] > 0?(this.adj[i][j]):"-");
            }
            System.out.println();
        }
    }

    /**
     * 创建顶点类
     */
    private class Vertex {
        char v;//值
        int index;//索引

        public Vertex(int index, char c) {
            this.index = index;
            this.v = v;
        }

    }

    public static void main(String[] args) {
        char arr[] = {'A', 'E', 'F', 'G', 'H', 'P'};
        //构建graph01
        Graph01 graph01 = new Graph01(arr);
        //进行连接
        int[][] adjMatrix = graph01.adj;
        adjMatrix[0][1]=1;
        adjMatrix[0][2]=1;
        adjMatrix[0][3]=1;

        adjMatrix[1][0]=1;
        adjMatrix[1][3]=1;
        adjMatrix[1][4]=1;

        adjMatrix[2][0]=1;

        adjMatrix[3][0]=1;
        adjMatrix[3][1]=1;
        adjMatrix[3][4]=1;
        adjMatrix[3][5]=1;

        adjMatrix[4][1]=1;
        adjMatrix[4][3]=1;
        adjMatrix[4][5]=1;

        adjMatrix[5][3]=1;
        adjMatrix[5][4]=1;


        graph01.show();
    }
}

 

  • 邻接表

邻接表的概念:邻接表的思想是,对于图中的每一个顶点,用一个数组来记录这个点和哪些点相连。由于相邻的点会动态的添加,所以对于每个点,我们需要用List来记录。

 对应的邻接表为:

 

package graphtheory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 图的表示--使用邻接矩阵
 */
public class Graph02 {
    private char[] V;//顶点上的值

    private Vertex[] vertexs;//顶点数组
    private int N;


    //邻接矩阵
    private List<Integer>[] adj;

    //图的构造函数
    public Graph02(char[] arr) {//{'A','E','F','G','H','P'}
        //拿到数组的长度
        int length = arr.length;
        this.N = length;
        V = new char[length];
        //arr元素赋值 到V
        this.V = Arrays.copyOf(arr, length);
        //构建图中的结点
        vertexs = new Vertex[length];
        for (int i = 0; i < length; i++) {
            vertexs[i] = new Vertex(i, this.V[i]);

        }
        this.adj = new List[length];
        for (int i = 0; i < this.N; i++) {
            this.adj[i]=new ArrayList<>();
        }
    }

    //打印邻接矩阵
    public void show() {
        System.out.println("    ");
        for (int i = 0; i < this.N; i++) {
            System.out.format("%-4c", this.V[i]);
            //拿到邻接表相邻结点的集合
            List<Integer> linkedList = this.adj[i];
            for (int j = 0; j < linkedList.size(); j++) {
                System.out.print(this.V[linkedList.get(j)] + "---->");
            }
            System.out.println();
            System.out.format("%-4d",vertexs[i].index);
            for (int j = 0; j < linkedList.size(); j++) {
                System.out.print(vertexs[linkedList.get(j)].index + "---->");
            }
            System.out.println();

            }
        }



    /**
     * 创建顶点类
     */
    private class Vertex {
        char v;//值

        int index;//索引

        int weight;//权值

        public Vertex(int index, char c) {
            this.index = index;
            this.v = v;
            this.weight = weight;
        }

        public Vertex(int index) {

        }
    }

    public static void main(String[] args) {
        char arr[] = {'A', 'E', 'F', 'G', 'H', 'P'};
        //构建graph01
        Graph02 graph02 = new Graph02(arr);
        //邻接表
        List<Integer>[] adj = graph02.adj;
        adj[0].add(1);
        adj[0].add(2);
        adj[0].add(3);

        adj[1].add(0);
        adj[1].add(3);
        adj[1].add(4);

        adj[2].add(0);

        adj[3].add(0);
        adj[3].add(1);
        adj[3].add(4);
        adj[3].add(5);

        adj[4].add(1);
        adj[4].add(3);
        adj[4].add(5);

        adj[5].add(3);
        adj[5].add(4);


        graph02.show();
    }
}

使用邻接表求出A--P的所有路径:

package graphtheory;


import java.util.*;

// 图的表示-- 使用邻接表
public class Graph03 {
    private char[] V;
    // 顶点数组
    private Vertex[] vertexs;
    private int N;
    // 邻接表
    private List<Integer>[] adj;

    public Graph03(char[] arr) { // {'A','E','F','G','H','P'}
        int length = arr.length;
        this.N = length;
        this.V = Arrays.copyOf(arr, length);
        // 构建图中的结点
        vertexs = new Vertex[length];
        for (int i = 0; i < length; i++) {
            vertexs[i] = new Vertex(0, this.V[i]);
        }
        this.adj = new List[length];
        for (int i = 0; i < this.N; i++) {
            this.adj[i] = new ArrayList<>();
        }
    }

    // 打印邻接矩阵
    public void show() {
        for (int i = 0; i < this.N; i++) {
            System.out.format("%-4c", this.V[i]);
            List<Integer> linkedList = this.adj[i];
            for (int j = 0; j < linkedList.size(); j++) {
                System.out.print(this.V[linkedList.get(j)] + "---->");
            }
            System.out.println();
            System.out.format("%-4c", this.V[i]);
            for (int j = 0; j < linkedList.size(); j++) {
                System.out.print(linkedList.get(j) + "---->");
            }
            System.out.println();
        }
    }


    public void bfs(int startIndex){
        boolean visited[] = new boolean[this.N];
        List<List<Integer>> result = new ArrayList<>();
        // 使用队列
        Queue<AbstractMap.SimpleEntry<Integer,Integer>> queue = new LinkedList<>();
        // 将开始顶点入队
        queue.add(new AbstractMap.SimpleEntry<>(startIndex,0));
        // 设置startIndex已经被访问
        visited[startIndex]=true;
        while(!queue.isEmpty()){
            AbstractMap.SimpleEntry<Integer,Integer> pair =  queue.poll();
            int key = pair.getKey(); //顶点的索引

            int val = pair.getValue();// 层

            if(result.size() == val){
                ArrayList list = new ArrayList();
                result.add(list);
            }
            List<Integer> levelList = result.get(val);
            levelList.add(key);
           // 找和key顶点直接相连的的索引
           List<Integer> list =  this.adj[key];
           for(int i=0;i<list.size();i++){
               if(!visited[list.get(i)]){
                   queue.add(new AbstractMap.SimpleEntry(list.get(i),val+1));
                   visited[list.get(i)]=true;
               }
           }
        }
       for(int i=0;i<result.size();i++){
           System.out.println("level:"+(i+1));
           for(int j=0;j<result.get(i).size();j++){
               System.out.format("%-4d",result.get(i).get(j));
           }
           System.out.println();
       }
    }



    public void dfs(int startIndex,int endIndex){
        //创建一个数组,用来记录是否已经被访问
        boolean visited[] = new boolean[this.N];
        // 标记开始结点已经被访问过
        LinkedList<Character> path = new LinkedList<>();
        dfs(startIndex,endIndex,visited,path);
    }

    // 递归向下去找
    private void dfs(int startIndex,int endIndex,boolean[] visited, LinkedList<Character> path){
        // 递归终止的条件
        if(startIndex == endIndex){
            path.offerLast(this.V[startIndex]);
            System.out.println(path);
            // 从路径的尾部删掉最后的顶点
            path.pollLast();
            return;
        }
        // 将当前顶点加到路径中去
        path.offer(this.V[startIndex]);
        // 标识startIndex已经被访问了
        visited[startIndex]=true;
        //  递归操作
        // 1、先找和startIndex直接连接的顶点有哪些
        List<Integer> list = this.adj[startIndex];
        // 2、处理每一个直接连接的顶点
        for(int i=0;i<list.size();i++){
            if(!visited[list.get(i)]){
                dfs(list.get(i),endIndex,visited,path);
            }
        }
        visited[startIndex]=false; // 回溯
        path.pollLast();
    }


    private class Vertex {
        char v;
        int index;

        //权值
        int weight;

        public Vertex(int index, char v, int weight) {
            this.index = index;
            this.v = v;
            this.weight = weight;
        }

        public Vertex(int index, char v) {
            this(index, v, 1);
        }
    }

    public static void main(String[] args) {
        char arr[] = {'A', 'E', 'F', 'G', 'H', 'P' };
        Graph03 graph03 = new Graph03(arr);
        // 邻接表
        List<Integer>[] adj = graph03.adj;
        adj[0].add(1);
        adj[0].add(2);
        adj[0].add(3);
        adj[1].add(0);
        adj[1].add(3);
        adj[1].add(4);
        adj[2].add(0);
        adj[3].add(0);
        adj[3].add(1);
        adj[3].add(4);
        adj[3].add(5);
        adj[4].add(1);
        adj[4].add(3);
        adj[4].add(5);
        adj[5].add(3);
        adj[5].add(4);
      
        graph03.bfs(5);
    }

}

迪杰斯特拉算法

概念即先求出长度最短的一条最短路径,再参照它求出长度次短的一条最短路径,依次类推,直到从源点v 到其它各顶点的最短路径全部求出为止。

 假设我们求A点到各点的最短距离

迪杰斯特拉算法过程(原理)

 

package graphtheory;


import java.util.*;

// 图的单源最最短路径-迪杰斯特拉算法(从一个顶点到图中各个顶点的最短距离)
public class Graph04 {
    private char[] V;
    // 顶点数组
    private Vertex[] vertexs;
    private int N;
    // 邻接表
    private List<AbstractMap.SimpleEntry<Integer, Integer>>[] adj; // key:顶点索引,val:权值

    public Graph04(char[] arr) { // {'A','E','F','G','H','P'}
        int length = arr.length;
        this.N = length;
        this.V = Arrays.copyOf(arr, length);
        // 构建图中的结点
        vertexs = new Vertex[length];
        for (int i = 0; i < length; i++) {
            vertexs[i] = new Vertex(i, this.V[i]);
        }
        this.adj = new List[length];
        for (int i = 0; i < this.N; i++) {
            this.adj[i] = new ArrayList<>();
        }
    }


    public int[] dijkstra(int sourceIndex) {
        // 1、创建距离表
        int[] dist = new int[this.N];
        Arrays.fill(dist, Integer.MAX_VALUE);
        // 2、创建一个标识顶点是否被访问的数组
        boolean[] visited = new boolean[this.N];
        // 3、初始化距离表
        // 3-1
        dist[sourceIndex] = 0; // 自身到自身的距离
        visited[sourceIndex] = true;
        // 3-2、找和sourceIndex直接相连的顶点
        List<AbstractMap.SimpleEntry<Integer, Integer>> list = this.adj[sourceIndex];
        for (int i = 0; i < list.size(); i++) {
            AbstractMap.SimpleEntry<Integer, Integer> vertex = list.get(i);
            int key = vertex.getKey();//顶点索引
            int val = vertex.getValue();//权值
            // 3-3  更新距离表
            dist[key] = val;
        }

        for (int k = 1; k < this.N; k++) {
            //4、在上述的最短路径dist[]中,从未被访问的顶点中选一条最短的路径长度
            int minDist = Integer.MAX_VALUE;
            int minDistIndex = -1;
            for (int i = 0; i < this.N; i++) {
                if (!visited[i] && dist[i] != Integer.MAX_VALUE && dist[i] < minDist) {
                    minDist = dist[i];
                    minDistIndex = i;
                }
            }

            // 最最短的路径长度所在的顶点与其它顶点都没有相连
            if (minDistIndex == -1) {
                break;
            }

            //5、更新距离表()
            // 5-1 将最最短的路径长度所在的顶点设置成已访问
            visited[minDistIndex] = true;
            // 5-2 找到和最短路径长度所在顶点直接相连的顶点
            List<AbstractMap.SimpleEntry<Integer, Integer>> list2 = this.adj[minDistIndex];
            // 5-3 minDist+权值与距离表中的数据进行比较
            for (int i = 0; i < list2.size(); i++) {
                AbstractMap.SimpleEntry<Integer, Integer> vertex = list2.get(i);
                int key = vertex.getKey();//顶点索引
                int val = vertex.getValue();//权值
                int newVal = minDist + val;
                if (!visited[key] && newVal < dist[key]) {
                    dist[key] = newVal;
                }
            }
        }
        return dist;
    }


    private class Vertex {
        char v;
        int index;

        public Vertex(int index, char v) {
            this.index = index;
            this.v = v;
        }
    }

    public static void main(String[] args) {
        char arr[] = {'A', 'E', 'F', 'G', 'H', 'P'};
        Graph04 graph04 = new Graph04(arr);
        // 邻接表
        List<AbstractMap.SimpleEntry<Integer, Integer>>[] adj = graph04.adj;

        adj[0].add(new AbstractMap.SimpleEntry<>(1, 5));
        adj[0].add(new AbstractMap.SimpleEntry<>(2, 4));
        adj[0].add(new AbstractMap.SimpleEntry<>(5, 2));

        adj[1].add(new AbstractMap.SimpleEntry<>(0, 5));
        adj[1].add(new AbstractMap.SimpleEntry<>(5, 1));
        adj[1].add(new AbstractMap.SimpleEntry<>(4, 3));

        adj[2].add(new AbstractMap.SimpleEntry<>(0, 4));
        adj[3].add(new AbstractMap.SimpleEntry<>(4, 3));
        adj[3].add(new AbstractMap.SimpleEntry<>(5, 4));
        adj[4].add(new AbstractMap.SimpleEntry<>(1, 3));
        adj[4].add(new AbstractMap.SimpleEntry<>(5, 2));
        adj[4].add(new AbstractMap.SimpleEntry<>(3, 3));
        adj[5].add(new AbstractMap.SimpleEntry<>(0, 2));
        adj[5].add(new AbstractMap.SimpleEntry<>(1, 1));
        adj[5].add(new AbstractMap.SimpleEntry<>(4, 2));
        adj[5].add(new AbstractMap.SimpleEntry<>(3, 4));

        int[] dist = graph04.dijkstra(0);
        System.out.println(Arrays.toString(dist));
    }

}

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

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

相关文章

2021年国赛高教杯数学建模A题FAST主动反射面的形状调节解题全过程文档及程序

2021年国赛高教杯数学建模 A题 FAST主动反射面的形状调节 原题再现 中国天眼——500 米口径球面射电望远镜&#xff08;Five-hundred-meter Aperture Spherical radio Telescope&#xff0c;简称 FAST&#xff09;&#xff0c;是我国具有自主知识产权的目前世界上单口径最大、…

Java 进阶 -- 集合(三)

4、实现 实现是用于存储集合的数据对象&#xff0c;它实现了接口部分中描述的接口。本课描述了以下类型的实现: 通用实现是最常用的实现&#xff0c;是为日常使用而设计的。它们在标题为“通用实现”的表格中进行了总结。特殊目的实现是为在特殊情况下使用而设计的&#xff0…

IP归属地API分享

各大网站和app都更新了用户IP归属地功能&#xff0c;但是如何获取IP归属地信息科难为了一些技术人员了&#xff0c;下面给大家分享最好用的IP归属地API&#xff0c;能够大大的提升了效率。 IP归属地API&#xff08;IP66_ip归属地在线查询_免费ip查询_ip精准定位平台&#xff09…

关于使用 AzureKinect 不识别的问题解决

AzureKinect如果不识别可能是因为 :(请一个一个排查) 1. 数据线和Kinect电源线没插,或者电源跟数据线插反了 2. SDK 没安装,一共需要安装两个SDK,安装教程 Unity AzureKinect 初识(一) 安装_会思考的猴子的博客-CSDN博客 3.数据线需要插 USB3.0或者3.1,集线器最好是…

软件测试Pytest实现接口自动化应该如何在用例执行后打印日志到日志目录生成日志文件?

Pytest可以使用内置的logging模块来实现接口自动化测试用例执行后打印日志到日志目录以生成日志文件。以下是实现步骤&#xff1a; 如果你想学习自动化测试&#xff0c;我这边给你推荐一套视频&#xff0c;这个视频可以说是B站百万播放全网第一的自动化测试教程&#xff0c;同…

iPhone手机如何将短信同步到安卓手机上

1.下载爱思助手 2.备份全量数据 3.用爱思助手查看备份数据&#xff1b;注&#xff1a;一定要用专业模式查看 4.代开专业模式&#xff0c;就能查看短信模块得数据&#xff0c;并且到导出成 sms.db文件 5. 用在线 db文件转成csv文件格式;地址&#xff1a;DB轉CSV 線上 - db轉成…

3.2 分析特征间的关系

3.2 分析特征间的关系 3.2.1 绘制散点图 scatter()例子1&#xff1a;绘制2000-2017年各季度国民生产总值散点图例子2&#xff1a;使用不同颜色不同形状的点&#xff0c;绘制2000-2017年各产业各季度国民生产总值散点图 3.2.2 绘制折线图 plot()例子1&#xff1a;绘制2000-2017年…

接口自动化测试框架开发(pytest+allure+aiohttp+ 用例自动生成)

近期准备优先做接口测试的覆盖&#xff0c;为此需要开发一个测试框架&#xff0c;经过思考&#xff0c;这次依然想做点儿不一样的东西。 接口测试是比较讲究效率的&#xff0c;测试人员会希望很快能得到结果反馈&#xff0c;然而接口的数量一般都很多&#xff0c;而且会越来越…

Matlab论文插图绘制模板第100期—紧凑排列多子图(Tiledlayout)

不知不觉&#xff0c;《Matlab论文插图绘制模板》系列来到了第100期。 在此之前&#xff0c;其实我也没想到会有这么多种数据可视化表达方式&#xff0c;论文里不是折线图就是柱状图&#xff0c;单调的很。 假如研究生那会要是能遇到现在的自己&#xff08;分享的内容&#x…

因果一致性

上一篇写到了一致性模型&#xff0c;而因果一致性模型比较复杂&#xff0c;故单独写一篇文章来记录 强一致性模型会在网络分区时变的不可用&#xff0c;而最终一致性模型放弃了safety&#xff0c;但同时也对系统可用性和性能产生明显的损害。上层要做些操作。于是有了一个折中…

高考作文AI大比拼「GPT-4 vs 文心一言 vs 通义千问」

2023 年 6 月 7 日上午&#xff0c;全国高考语文科目已经考试结束&#xff0c;第一时间拿到了全国甲卷的高考作文题目&#xff1a; 阅读下面的资料&#xff0c;根据需要写作 人们因技术发展得更好地掌控时间&#xff0c;但也有人因此成为了时间的仆人。 这句话引出了你怎样的联…

备战金九银十:1200道Java面试真题合集,助你搞定面试官

不论是校招还是社招都避免不了各种面试。笔试&#xff0c;如何去准备这些东西就显得格外重要。不论是笔试还是面试都是有章可循的&#xff0c;我这个有章可循‘说的意思只是说应对技术面试是可以提前准备。 运筹帷幄之后&#xff0c;决胜千里之外!不打毫无准备的仗,我觉得大家…

爬虫数据采集需要什么样的代理ip以及遇到的反爬措施

随着互联网的快速发展&#xff0c;数据已经成为许多行业中的重要资源。网络爬虫作为一种数据采集工具&#xff0c;在许多领域中得到了广泛应用。但是现在很多网站都有非常多的限制&#xff0c;所以在爬取数据的时候&#xff0c;还需要借助代理ip来助力&#xff0c;才能更好的完…

OKHttp_官方文档[译文]

OKHttp功能类介绍 OKHttp网络请求流程分析 OKHttp连接池 OKHttp分发器 OKHttp拦截器 RetryAndFollowUpInterceptorBridgeInterceptorCacheInterceptorConnectInterceptorCallServerInterceptor 总览 OkHttp HTTP是现代应用程序网络的方式。这就是我们交换数据和媒体的方…

Tcp黏包和半包形象讲解以及结合Netty应用层的解决方案

黏包&#xff1a;顾名思义就是好几次的请求消息粘在了一起 半包&#xff1a;顾名思义就是一个消息分成了好几半发送出去 首先讲解这两种现象出现的原因: 1.大家都知道tcp是一个可靠的网络协议&#xff0c;每发送一段消息&#xff0c;就要进行一次&#xff0c;确认应答(ack处…

5.41 综合案例2.0-modbus协议控制变送器和六路继电器

modbus协议控制变送器和六路继电器 案例说明1.器件光照温湿度变送器六路继电器 2.测试前操作3.连线 代码测试 案例说明 基于modbus协议&#xff0c;本案例实现了下述功能&#xff1a;  &#xff08;1&#xff09;采集和上报温度、湿度、光照数据  &#xff08;2&#xff09;…

安全防御 --- IPSec理论(02)

附&#xff1a; 协议与模式分类 esp 和 ah 的分类&#xff1a; 数据的安全性&#xff1a;ESP有机密性&#xff1b;AH无机密性场景&#xff1a;ESP适合公网场景&#xff1b;AH适合内网 / 私网场景 &#xff08;数据的安全性主要依赖于传输端之间需要做认证&#xff09; 传输…

MyBatisPlus4-DML编程控制(增删改)、id生成策略、逻辑删除、乐观锁和悲观锁

1. id生成策略控制&#xff08;增&#xff09; 名称: TableId 类型: 属性注解 位置: 模型类中用于表示主键的属性定义上方 作用: 设置当前类中主键属性的生成策略 public class User {TableId(type IdType.AUTO)private Long id; }value: 设置数据库主键名称 type: 设置主键属…

vue完美模拟pc版快手实现短视频,含短视频详情播放

目录 一、预览 二、效果图 项目实现的demo效果图&#xff1a; 三、项目细节说明 1.项目结构、设计说明 2.项目可拓展能力题外话&#xff08;看不懂可以忽略&#xff09; 3.项目路由配置 4.框架布局页面源码 5.首页实现 四、总结 一、预览 本作品demo预览地址&#xff1…

测试工程师该何去何从?写给30+岁的测试工程师!

前言&#xff1a; 软件测试是为了发现程序中的错误而执行程序的过程。 通俗的说&#xff0c;软件测试需要在发布软件之前&#xff0c;尽可能的找软件的错误&#xff0c;尽量避免在发布之后给用户带来不好的体验&#xff0c;并要满足用户使用的需求。 首先今年行情肯定比去年好…