在求图线任意两点间最短路径时,利用floyd、dijdstra等成熟的算法可以求得,效率还不错。但要求换乘最少、最舒适等路径时,需要求线网图中任意两个点的所有路径,然后根据条件筛选,以上算法无能为力。本人最近做个小项目需要用到这个需求,因此在网上搜索相关资料,找到一个利用栈采用深度优先搜索的算法,利用此算法在下图11条线路190余个站中测试,任意两点间所有路径平均耗时15秒,不能满足需求。

于是,自己琢磨着写了一个算法,现将其记录如下:

第一步:将每条线路视为一个结点,生成所有乘坐线路顺序列表。如上图中楚河汉街——中山公园的路径中,首先采用广度优先搜索生成[[4,2],[4,8,1,2],[4,3,2],......],这样的路径,在生成中剔除明显不合理的路径,如[4,2,7,1,2]这样的路径。

第二步:求第一步生成的线路顺序中相邻两条线的交点(即换乘站),生成以换乘站作为节点的顺序路径。如第一步中[4,3,2]这条线路顺序,4和3的交点为王家湾,3和2的交点为范湖和宏图大道,因此可生成[楚河汉街,王家湾,范湖,中山公园]以及[楚河汉街,王家湾,宏图大道,中山公园]这两起点、终点以及换乘站构成的路径。

第三步:将第二步中每条路径相邻两个站点之间其他站点补齐,即是从起点至终点的完整路径。

因为在第一步剔除了许多不合理路径,因此最后生成所有路径中比以站点深度优先搜索得出来的所有路径少得多,前者为1000余条,后者为15万条。不过没关系,剔除的路径对我们来说没有任何意义。

算法中适用存在环线的线网图,将Line对象中的isCircle设置为true即可。

由于篇幅原因,以下只贴算法中的关键代码,完整的项目以点击下载图中任意两点间所有路径高效算法

首先介绍下算法中存储路径的数据结构:树

Tree:

public class SolutionTree {private TreeNode root;//权的根节点public SolutionTree(int Id){root = new TreeNode(Id);}public TreeNode getRoot() {return root;}public void setRoot(TreeNode root) {this.root = root;}}

TreeNode:

public class TreeNode implements Serializable {/*** */private static final long serialVersionUID = 656317088559367582L;private int Id;  //权节点idprotected TreeNode parentNode;//父节点protected List<TreeNode> childList;//子节点列表public TreeNode(int Id) {this.Id=Id;initChildList();}public TreeNode(int Id,TreeNode parentNode) {this.Id=Id;this.parentNode=parentNode;initChildList();}//此处省略get、set以及一些内部方法}

然后是存储站点信息以及线路信息的Class:

Station.class:

public class Station {private int id;//车站idprivate String name;//车站名private Station nextSta;//下个站private Station prevSta;//上个站private int line;//车站所在的线路private List<Integer> transferLines = new ArrayList<Integer>();//当前站可换乘线路列表,不是换乘,列表中只有一个0元素//此外省略了get、set以及一些内部方法}

Line.class:

public class Line {private int id; //线路idprivate List<Station> stationList = new ArrayList<Station>();//本条线的车站列表private boolean isCircle ;//是否为环线//此外省略了get、set以及一些内部方法}

最后就是算法了,算法中需要三个初始数据,graph:站点列表,将每个站点对象中的所有属性按实际情况赋值;matrix:费用矩阵,可连通,设置为连通的费用值,不可连通,设置为无穷大,算法中统一相邻站点费用为2,换乘站连接费用为5,实际使用中可读取费用数据后赋值;lineTable:线路列表,线路id作为key值。以上三个初始数据在实际使用中可通过第二个构造函数传入赋值。代码如下:

public class PathSearch {  private List<Station> graph;  //车站列表,private static final int INF=Integer.MAX_VALUE;private int[][] matrix;//费用矩阵private Map<Integer,Line> lineTable = new HashMap<Integer,Line>();//线路表/*** 从数据库或文件读取站点数据,费用表等数据,初始化数据*/public PathSearch(){try{//要求根据库中的车站按所在线中的顺序依次存储String sql = "Select * From tab_station order by id";List<Map<String,Object>> result = MyDatabase.search(sql);graph = new ArrayList<Station>();List<Station> temp  = new ArrayList<Station>();int line = Integer.parseInt(result.get(0).get("line").toString());for(int i=0;i<result.size();i++) {Map<String,Object> data = result.get(i);Station sta = new Station();sta.setId(i);sta.setLine(Integer.parseInt(data.get("line").toString()));sta.setName(data.get("name").toString());if(!(line == sta.getLine()) || i==result.size()-1){Line tl = new Line();tl.setId(line);tl.setStationList(temp);//若线路为环线,在此设置   tl.setCircle(true);lineTable.put(line, tl);line = sta.getLine();if(i==result.size()-1){temp.add(sta);}temp  = new ArrayList<Station>();}temp.add(sta);List<String> list = Arrays.asList(data.get("transer").toString().replace(" ", "").split(","));for(String tl : list){sta.addTransferLine(Integer.parseInt(tl));}graph.add(sta);}//将前后车站相连,graph中的元素也随着改变for(int key : lineTable.keySet()){Line tl = lineTable.get(key);int lineLength = tl.getStationList().size();if(tl.isCircle())//如果是环线,首尾相连{tl.getStationList().get(0).setPrevSta(tl.getStationList().get(lineLength-1));tl.getStationList().get(lineLength-1).setNextSta(tl.getStationList().get(0));}tl.getStationList().get(0).setNextSta(tl.getStationList().get(1));tl.getStationList().get(lineLength-1).setPrevSta(tl.getStationList().get(lineLength-2));for(int i=1;i<lineLength-1;i++){tl.getStationList().get(i).setPrevSta(tl.getStationList().get(i-1));tl.getStationList().get(i).setNextSta(tl.getStationList().get(i+1));}}matrix = new int[graph.size()][graph.size()];//权重数组for(int i = 0; i<graph.size();i++){Station stai = graph.get(i);for(int j = 0;j<graph.size();j++){Station staj = graph.get(j);if(i==j){matrix[i][j] = 0;}//票价计算,换乘站间设置权重为0,若为时间权限,可在此设置换乘权重else if(stai.getName().equals(staj.getName())){matrix[i][j] = 5;}else{matrix[i][j] = INF;}//相邻车站权重统一设置为2if(stai.getNextSta()!=null){if(stai.getNextSta().getId() == staj.getId()){matrix[i][j] = 2;}}if(stai.getPrevSta()!=null){if(stai.getPrevSta().getId() == staj.getId()){matrix[i][j] = 2;}}if(staj.getNextSta()!=null){if(staj.getNextSta().getId() == stai.getId()){matrix[i][j] = 2;}}if(staj.getPrevSta()!=null){if(staj.getPrevSta().getId() == stai.getId()){matrix[i][j] = 2;}}}}}catch(Exception e){e.printStackTrace();}}/*** 从外部将站点列表,费用矩阵及线路表传入,初始化数据*/public PathSearch(List<Station> graph,int[][] matrix,Map<Integer,Line> lineTable){this.graph = graph;this.matrix = matrix;this.lineTable = lineTable;}//记录每对起始id第一次调用getAllPath方法得到数据,若相同的起始id再次再次调用时,可直接调用XXNoMakeAllPath()方法private List<List<Integer>> allPath;/*** 生成起点到终点的所有路径,该方法返回的所有路径比实际的所有路径要少得多,因为在方法中去除了一些明显不合理的路径,* 例如2号线转3号线,再从3号线转2号线的路径明显不合理* @param startStaId 起点id* @param endStaId  终点id* @return  径路列表*/private List<List<Integer>> getAllPath(int startStaId, int endStaId){Station startSta = graph.get(startStaId);Station endSta = graph.get(endStaId);//以起始线路id作为根节点初始化一棵树SolutionTree tree = new SolutionTree(startSta.getLine());makeTreeNode(tree.getRoot(),endSta.getLine());//获取树的所有叶子节点List<TreeNode> ziYeList = new ArrayList<TreeNode>(); tree.getRoot().getZiYe(ziYeList);Iterator<TreeNode> tempList = ziYeList.iterator();//删除id不是终点站的叶子结点while(tempList.hasNext()){TreeNode node = tempList.next();if(node.getId()!=endSta.getLine()){tempList.remove();}}//每个叶子节点的父辈节点反转后再加上节点本身,即为从起点到终点经过线路顺序List<List<Integer>> tempResult = new ArrayList<List<Integer>>();for(TreeNode node : ziYeList){List<Integer> list = node.getEldersID();Collections.reverse(list);list.add(node.getId());tempResult.add(list);}//通过每条线路顺序,获取换乘站的顺序路径List<List<Integer>> staTempResult = new ArrayList<List<Integer>>();for(List<Integer> temp : tempResult){//以起点站id作根要点id初始化树SolutionTree staTree = new SolutionTree(startStaId);for(int i = 0; i < temp.size()-1; i++){List<TreeNode> fatherList = new ArrayList<TreeNode>();staTree.getRoot().getZiYe(fatherList);Line line = lineTable.get(temp.get(i));Line nextLine = lineTable.get(temp.get(i+1));//两条线的交点即为换乘站,List<Integer> jiaoDianList = getJiaoDianZhan(line,nextLine.getId());//为每个叶子节点增加子节点for(TreeNode father : fatherList){for(int staId : jiaoDianList){//如果交点站为起始车站,则将换乘站所有下条线路的id作为一个节点添加到父节点中if(staId == startStaId){Station nextSta = nextLine.getStation(line.getStation(staId).getName());TreeNode node2 = new TreeNode(nextSta.getId(), father);father.addChildNode(node2);}//否则,则换乘站所有当前线路的id作为一个节点添加到父节点中,同时将换乘站所有下条线路的id作为一个节点添加到刚添加的节点中else{TreeNode node1 = new TreeNode(staId, father);father.addChildNode(node1);Station nextSta = nextLine.getStation(line.getStation(staId).getName());TreeNode node2 = new TreeNode(nextSta.getId(), node1);node1.addChildNode(node2);}}}}List<TreeNode> fatherList = new ArrayList<TreeNode>();staTree.getRoot().getZiYe(fatherList);//将所有叶子节点的父辈节点反转并添加该叶子节点id即为第i条线路顺序所经过的换乘站顺序idfor(TreeNode node : fatherList){List<Integer> list = node.getEldersID();Collections.reverse(list);list.add(node.getId());if(!list.contains(endStaId)){list.add(endStaId);}staTempResult.add(list);//System.out.println(list);}}List<List<Integer>> result = new ArrayList<List<Integer>>();for(List<Integer> temp : staTempResult){List<Integer> path = new ArrayList<Integer>();for(int i=0;i<temp.size()-1;i++){Station sta = graph.get(temp.get(i));Station nextSta = graph.get(temp.get(i+1));//两站之间在同一条线,将两站之间的站点补全if(sta.getLine()==nextSta.getLine()){List<List<Integer>> tempPath = lineTable.get(sta.getLine()).getPathInLine(sta.getId(), nextSta.getId());if(tempPath.size()==1){path.addAll(tempPath.get(0));}else{int minIndex = -1;int minDistance = Integer.MAX_VALUE;for(int index=0;index<tempPath.size();index++){int distance = getDistance(tempPath.get(index));if(minDistance > distance){minIndex = index;minDistance = distance;}}if(minIndex!=-1){path.addAll(tempPath.get(minIndex));}}}else if(i==temp.size()-2){path.add(nextSta.getId());}}result.add(path);}allPath = result;return result;}/*** 获取两条线路的交点* @param line 当前线路id* @param nextLineId  下条线路id* @return  返回两条线路交点集合列表*/private List<Integer> getJiaoDianZhan(Line line, int nextLineId) {// TODO Auto-generated method stubList<Integer> result = new ArrayList<Integer>();//为同一条,返回空列表if(line.getId() == nextLineId)return result;for(Station sta : line.getStationList()){//当前线路的车站的换乘列表中包含下条线路,表明有交集if(sta.getTransferLines().contains(nextLineId)){result.add(sta.getId());}}return result;}/*** 为当前树节点添加子节点* @param father 当前节点* @param endLine 终点所在的线路id,用于判断递归结束条件*/private void makeTreeNode(TreeNode father,int endLine) {// TODO Auto-generated method stubList<Integer> transferList = getXiangGuanXianLu(father,endLine);for(int line : transferList){TreeNode node = new TreeNode(line,father);father.addChildNode(node);//递归直至当前线路等于终点站所在的线路if(line!=endLine){makeTreeNode(node, endLine);}}}/*** 获取与当前线路有交集的线路id* @param father 当前线路的树节点* @param endLine 终点所在的线路id* @return 返回与当前线路有交集的线路id列表*/private List<Integer> getXiangGuanXianLu(TreeNode father,int endLine) {// TODO Auto-generated method stubList<Integer> result = new ArrayList<Integer>();Line line = lineTable.get(father.getId());//获取当前节点的父辈节点id列表List<Integer> temp = father.getEldersID();for(Station sta : line.getStationList()){for(int tl : sta.getTransferLines()){//如果是换乘站,列表中不存在当前相关的线路id,//除当前相关的线路id为终点点所在的线路id外,当前节点的父辈节点中不能包含此id,同时此id不能与当前节点的id相同//满足以上条件,才能作为当前节点的子节点if(tl!=0 && !result.contains(tl) && (tl == endLine || (!temp.contains(tl) && tl != father.getId()))){result.add(tl);}}}return result;}/*** 根据费用矩阵,获取路径的费用总和* @param path 路径列表* @return 返回路径的费用总和*/private int getDistance(List<Integer> path){int sum = 0;for(int i=0;i<path.size()-1;i++){sum += matrix[path.get(i)][path.get(i+1)];}return sum;}/*** 获取费用最小的路径* @param allPath 所有路径列表* @return  返回费用最小的路径*/private  List<List<Integer>> makeShortPath(List<List<Integer>> allPath){if(allPath.size()==0)return allPath;List<Integer> distance = new ArrayList<Integer>();for(List<Integer> path : allPath){distance.add(getDistance(path));}float minDistance = Collections.min(distance);List<List<Integer>> result = new ArrayList<List<Integer>>();for(int i=0;i<allPath.size();i++){if(distance.get(i) == minDistance){result.add(allPath.get(i));}}return result;}/*** 获取换乘最少的路径* @param allPath 所有路径列表* @return  返回换乘最少的路径*/private  List<List<Integer>> makeTransferLessPath(List<List<Integer>> allPath){if(allPath.size()==0)return allPath;List<Integer> transferNumList = new ArrayList<Integer>();for(List<Integer> path : allPath){int sum = 0;for(int i=0;i<path.size()-1;i++){//路径中第i个站与第i+1个站不是同一条线,表明换乘,终点站为换乘站时,不计换乘个数if(graph.get(path.get(i)).getLine() != graph.get(path.get(i+1)).getLine() && i!=path.size()-2){sum ++;}}transferNumList.add(sum);}float minTransferNum = Collections.min(transferNumList);List<List<Integer>> result = new ArrayList<List<Integer>>();for(int i=0;i<allPath.size();i++){if(transferNumList.get(i) == minTransferNum){result.add(allPath.get(i));}}return result;}/*** 获取费用最小的路径,并将id转化成站名* @param startStaId 起始id* @param endStaId 终点id* @return  返回费用最小的字符串路径*/public List<List<String>> getShortPath(int startStaId, int endStaId) {List<List<Integer>> allPath = getAllPath(startStaId, endStaId);List<List<String>> result = new ArrayList<List<String>>();//获取路径最短的路径List<List<Integer>> temp = makeShortPath(allPath);//如果有两条及以上的最短路径,获取换乘最少的路径if(temp.size()>1){temp = makeTransferLessPath(temp);}for(int i=0;i<temp.size();i++){result.add(transformPath(temp.get(i)));}return result;}/*** 获取费用最小的路径,并将id转化成站名,不需要生成AllPath* @param startStaId 起始id* @param endStaId 终点id* @return  返回费用最小的字符串路径*/public List<List<String>> getShortPathNoMakeAllPath(int startStaId, int endStaId) {List<List<String>> result = new ArrayList<List<String>>();if(allPath==null)return result;//获取路径最短的路径List<List<Integer>> temp = makeShortPath(allPath);//如果有两条及以上的最短路径,获取换乘最少的路径if(temp.size()>1){temp = makeTransferLessPath(temp);}for(int i=0;i<temp.size();i++){result.add(transformPath(temp.get(i)));}return result;}/*** 获取换乘最少的路径,并将id转化成站名,不需要生成AllPath* @param startStaId 起始id* @param endStaId 终点id* @return  返回换乘最少的字符串路径*/public List<List<String>> getTransferLessPathNoMakeAllPath(int startStaId, int endStaId) {// TODO Auto-generated method stub//List<List<Integer>> allPath = getAllPath(startStaId, endStaId);List<List<String>> result = new ArrayList<List<String>>();if(allPath==null)return result;//获取换乘个数最少的路径List<List<Integer>> temp = makeTransferLessPath(allPath);//如果有多条换乘个数一样的路径,获取其中路径最短的if(temp.size()>1){temp = makeShortPath(temp);}for(int i=0;i<temp.size();i++){result.add(transformPath(temp.get(i)));}return result;}/*** 获取换乘最少的路径,并将id转化成站名* @param startStaId 起始id* @param endStaId 终点id* @return  返回换乘最少的字符串路径*/public List<List<String>> getTransferLessPath(int startStaId, int endStaId) {// TODO Auto-generated method stubList<List<Integer>> allPath = getAllPath(startStaId, endStaId);List<List<String>> result = new ArrayList<List<String>>();//获取换乘个数最少的路径List<List<Integer>> temp = makeTransferLessPath(allPath);//如果有多条换乘个数一样的路径,获取其中路径最短的if(temp.size()>1){temp = makeShortPath(temp);}for(int i=0;i<temp.size();i++){result.add(transformPath(temp.get(i)));}return result;}/*** 将id路径转化成站名的路径* @param path* @return*/private List<String> transformPath(List<Integer> path){List<String> result = new ArrayList<String>();//获取起点站线路String startLine = getStartLine(path);for(int i=0;i<path.size()-1;i++){Station sta1 = graph.get(path.get(i));Station sta2 = graph.get(path.get(i+1));Station temp = graph.get(path.get(path.size()-1));//换乘站且不是终点站if(sta1.getName().equals(sta2.getName()) && sta2.getId() != temp.getId()){//获取换乘方向String direction = getTransferDirection(sta2,path.get(i+2));result.add(sta1.getName()+"(换乘"+sta2.getLine()+"号线,"+direction+")");i++;}//换乘站,且换乘站为终点站else if(sta1.getName().equals(sta2.getName())){//result.add(sta1.getName());}else{result.add(sta1.getName());}}result.add(graph.get(path.get(path.size()-1)).getName());if(path.size()>1){String direction = getTransferDirection(graph.get(path.get(0)),path.get(1));result.set(0, result.get(0)+"("+startLine+","+direction+")");}else{result.set(0, result.get(0)+"("+startLine+")");}return result;}/*** 获取起点站需要乘坐的路径,起点站为换乘,返回的是换乘站所有的下条线路* @param path 路径* @return 返回起点站需要乘坐的路径*/private String getStartLine(List<Integer> path) {// TODO Auto-generated method stubint n = 0;if(path.size()==1){return "(已在目的地)";}Station sta1 = graph.get(path.get(n));Station sta2 = graph.get(path.get(n+1));while(sta1.getName().equals(sta2.getName())){n++;sta1 = graph.get(path.get(n));sta2 = graph.get(path.get(n+1));}return "乘坐"+sta1.getLine()+"号线";}/*** 获取换乘方向* @param sta 换乘站* @param nextId 换乘站的相邻车站id* @return 换乘方向*/private String getTransferDirection(Station sta,Integer nextId) {// TODO Auto-generated method stubLine line = lineTable.get(sta.getLine());if(sta.getPrevSta()!=null && sta.getPrevSta().getId() == nextId){return "往" + line.getStationList().get(0).getName() + "方向";}else if(sta.getNextSta()!=null && sta.getNextSta().getId() == nextId){return "往" + line.getStationList().get(line.getStationList().size()-1).getName() + "方向";}else{return "";}}public List<Station> getGraph() {return graph;}} 

测试类:

public static void main(String[] args) {// TODO 自动生成的方法存根try{long startTime = new Date().getTime();System.out.println("运行中....");PathSearch ps = new PathSearch();int start = 1;int end = 28;System.out.println("最短路:"+ps.getShortPath(start,end));System.out.println("换乘最少:"+ps.getTransferLessPathNoMakeAllPath(start,end));long endTime = new Date().getTime();System.out.print("耗时:"+(endTime-startTime)/1000.0+"秒");}catch (Exception e) {// TODO: handle exceptione.printStackTrace();}System.exit(0);}}

版权声明:本文作者原创,未经允许不得他用,转载请注明出处。

地铁线路图中任意两点间所有路径高效算法相关推荐

  1. 任意两点间的最短路问题(Floyd-Warshall算法)

    /* 任意两点间的最短路问题(Floyd-Warshall算法)*/import java.util.Scanner;public class Main {//图的顶点数,总边数static int ...

  2. matlab两点之间的所有路径,引用 在图中搜索两点间的所有路径matlab编程

    引用 在图中搜索两点间的所有路径matlab编程 2018-09-18 function possiablePaths = findPath(Graph, partialPath, destinati ...

  3. 图示电路中的等效电阻rab_例求图示电路中ab两点间的等效电阻Rab.ppt

    ,例: 求图示电路中a.b两点间的等效电阻Rab.,,,电阻的星形连接与三角形连接的等效变换,2.6 基尔霍夫定律,,上一页,下一页,返 回,基尔霍夫定律包括电流定律和电压定律. 支路: 一段没有分 ...

  4. 【算法】Floyd-Warshall算法(任意两点间的最短路问题)(判断负圈)

    问题 求解任意两点间最短路问题也叫多源最短路径问题. 可解决途径 一种方法时把图中每个点当做源点重复算n次Dijkstra 算法(Dijkstra是求单源最短路径的算法),时间复杂度O(n^3),据说 ...

  5. 基于百度地图API计算任意两点间的出行距离

    文章目录 前言 使用步骤 1.导入相关包 2.计算小汽车距离 3.计算骑行距离 4.创建主函数 总结 前言 为了方便自己以后查找代码,也不想让自己的桌面变得凌乱不堪,所以将把自己之前的代码保存到这里面 ...

  6. matlab求两点间距离,matlab如何求一个N*2的矩阵的任意两点间的距离?

    matlab如何求一个N*2的矩阵的任意两点间的距离? mip版  关注:64  答案:3  悬赏:0 解决时间 2021-02-23 20:55 已解决 2021-02-23 16:37 假设有个矩 ...

  7. golang计算任意两点间的方位角

    计算任意两点间的方位角 方位角是从某点的指北经线起,依顺时针方向到目标方向线之间的水平夹角(如图所示θ,可以将其看成是指南针所指示的角度),也即是OPN平面与OPQ平面的所构成的二面角大小. 以北极点 ...

  8. 计算球面上任意两点间的球面距离(C++实现)

    文章目录 1 预备知识 2 原理描述 3 代码实现 1 预备知识 在求解此问题之前首先要明确一下几点: (1)两点间的球面距离: 球面上两点间的最短距离,即球心与球面上两点所确定的平面与球面相交,得到 ...

  9. 图论 ---- F. The Shortest Statement (最短路的性质 + 任意两点间最短路 + 图转树)

    题目链接 题目大意: 给你一个nnn个点mmm条边的无向图,就是动态询问任意两点间的最短路 n,m∈[1,1e5],m−n≤20n,m\in[1,1e5],m-n\leq20n,m∈[1,1e5],m ...

最新文章

  1. 使用指针交换i,j,k的值
  2. Javascript迄今为止添加了前导零
  3. 小甲鱼python课后作业十七_小甲鱼Python第十六讲课后习题--017函数
  4. VTK:PolyData之ExtractCellsUsingPoints
  5. sql2012包含数据库,快速生成用户tsql脚本
  6. 在python中下列代码的运行结果是print abc_python与数学
  7. Nodejs 操作 MongoDb 数据库
  8. 容器转换类型,列表,集合,字典推导式
  9. 《iOS 6高级开发手册(第4版)》——2.5节秘诀:Quick Look预览控制器
  10. Castle.Aop.Autofac
  11. 机器学习之工程师入门路线
  12. 信号与系统【奥本海目】第二版笔记
  13. sw槽钢插件_SolidWorks所有实用插件详解一览
  14. 分段插值法 | 分段线性插值 + 分段抛物插值
  15. [放遗忘]PR进行视频剪辑的两种办法
  16. 修改Chrome浏览器默认背景颜色为浅绿色
  17. 用了亿恩的空间,总有上当的感觉
  18. abaqus各种文件说明
  19. Java中long与float
  20. 计算机关闭自带杀毒,电脑系统自带杀毒软件怎么关闭?两种Windows defender彻底关闭方法(图文)...

热门文章

  1. 工具 | 快速视频压缩与格式转换 | 格式工厂
  2. 2011年各大知名软件公司校招聘软件研发类薪资待遇
  3. 企业数字化转型背景下,低代码成为降本增效的“杀手锏”
  4. 华为机试HJ97:记负均正
  5. 豪恩创新:抢票软件引发的APP博弈
  6. 【解决Git ssh 密钥忘记密码】
  7. 文计笔记6 多媒体基础
  8. UAF (Use After Free)漏洞分析及利用
  9. win10下安装双系统磁盘分区
  10. vivo新系统originos和鸿蒙,vivo发布新系统“Origin OS”,这三批机型支持升级,你期待吗?...