博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
浅析"图"的暴力美学
阅读量:6712 次
发布时间:2019-06-25

本文共 4000 字,大约阅读时间需要 13 分钟。

You can't have a better tomorrow if you are thinking about yesterday. 
如果你还在思考昨天,那么你就无法拥有一个更好的明天。

图是一种比较复杂的非线性数据结构。图分很多种,无向图,有向图,带权图,稀疏图等等。本文主要分享了无向图的两种暴力搜索算法BFS(广度优先搜索)和DFS(深度优先搜索)。所有源码均已上传至github:

准备

在这之前,先初始化一个无向图,用LinkedList来当邻接表存储图

private Graph(int count) {        this.count = count;        adj = new LinkedList[count];        for (int i = 0; i < adj.length; i++) {            adj[i] = new LinkedList<>();        }    }复制代码

这里为了插入方便,一条边直接存两次

private void add(int oSide, int rSide) {        adj[oSide].add(rSide);        adj[rSide].add(oSide);    }复制代码

构造一个无向图

private void createGraph() {        //  0 -- 1 -- 2        //  |    |    |        //  3 -- 4 -- 5        //  |    |    |        //  6 -- 7 -- 8        add(0,1);//add(1,0);        add(0,3);//add(3,0);        add(1,2);//add(2,1);        add(1,4);// add(4,1);        add(2,5);//add(5,2);        add(3,4);//add(4,3);        add(3,6);//add(6,3);        add(4,5);//add(5,4);        add(4,7);// add(7,4);        add(5,8);//add(8,5);        add(6,7);//add(7,6);        add(7,8);//add(8,7);    }复制代码

准备完毕

BFS(广度优先搜索)

分析

广度优先搜索,从字面意思理解,它就是一种“地毯式”的搜索策略,先查找离起始顶点最近的,然后是次近的,依次往外搜索,层层递进。

注意

在这里三个重要的核心辅助变量 visited、queue、prev。

  • visited 记录已经被访问的顶点,避免顶点被重复访问
  • queue 用来存储已经被访问、但相连的顶点还没有被访问的顶点的这样的一个队列。
  • prev 记录搜索路径,它是反向存储,便于后续正向打印输出图的路径。

这里看一下它的打印方式就理解prev的存储机制了。

private void print(int[] prev, int oSide, int rSide) {        if (prev[rSide] != -1 && oSide != rSide) {            print(prev, oSide, prev[rSide]);        }        System.out.print(rSide + " ");    }复制代码

解析

首先先将起点oSide加入队列queue,然后在该while循环里,遍历该节点的邻接表,再通过visited进行判断,看是否访问,一直到遍历出的value == rSide 说明已找到,否则让visited 记录该顶点已访问,同时入队,就这样通过不停地入队出队。

ps:遍历出的图的路径也是它的最短路径。

private void bfs(int oSide, int rSide) {        if (oSide == rSide) return;        boolean[] visited = new boolean[count];        visited[oSide] = true;        Queue
queue = new LinkedList<>(); queue.offer(oSide); int[] prev = new int[count]; for (int i = 0; i < count; i++) { prev[i] = -1; } while (!queue.isEmpty()) { int index = queue.poll(); for (int j = 0; j < adj[index].size(); j++) { int value = adj[index].get(j); if (!visited[value]) { prev[value] = index; if (value == rSide) { print(prev, oSide, rSide); } visited[value] = true; queue.offer(value); } } } }复制代码

DFS(深度优先搜索)

分析

深入优先搜索,有点走迷宫的意思,随机选择路径,当发现走不通的时候,退回来重新来过,直到找到终点。通过测试结果也可以看出它把所有的路径都访问了一遍,蛇皮走位。它用到了一种十分著名的思想--回溯思想。就像是'后悔药'。

解析

它和bfs相同的是均用到了prev、visited。不同的是,它声明了一个全局的变量found,起到一个跳出递归的作用。

它的关键在于recursiveDfs这个递归方法。默认起点被访问,先声明一个出口,当满足查找的顶点等于终点的时候跳出。遍历当前顶点的邻接表,逐个判断顶点是否已访问,记录下来,然后递归。

ps:遍历出的图的路径也是它的最长路径。

private boolean found = false;   private void dfs(int oSide, int rSide) {        boolean[] visited = new boolean[count];        int[] prev = new int[count];        for (int i = 0; i < count; i++) {            prev[i] = -1;        }        recursiveDfs(visited, prev, oSide, rSide);        print(prev, oSide, rSide);    }    private void recursiveDfs(boolean[] visited, int[] prev, int oSide, int rSide) {        if (found) return;        visited[oSide] = true;        if (oSide == rSide) {            found = true;            return;        }        for (int i = 0; i < adj[oSide].size(); i++) {            int value = adj[oSide].get(i);            if (!visited[value]) {                prev[value] = oSide;                recursiveDfs(visited, prev, value, rSide);            }        }    }复制代码

测试代码

public static void main(String[] args) {    int count = 9;    Graph graph = new Graph(count);    //  0 -- 1 -- 2    //  |    |    |    //  3 -- 4 -- 5    //  |    |    |    //  6 -- 7 -- 8    graph.createGraph();    System.out.println("BFS(广度优先搜索)");    graph.bfs(0,8);    System.out.println();    System.out.println("DFS(深度优先搜索)");    graph.dfs(0,8);}复制代码

测试结果

end

您的点赞和关注是对我最大的支持,谢谢!

转载地址:http://emolo.baihongyu.com/

你可能感兴趣的文章
ios GCD ---- (1)
查看>>
Pi编译安装PHP/Nginx并安装完整LEMP环境
查看>>
HTTPS 也不安全?被发现新漏洞会暴露你的数据
查看>>
x86 和 ARM 谁能主宰服务器市场?Linux 之父和 Redis 之父有分歧了
查看>>
dva.js学习梳理集
查看>>
ECS运维神器重装上阵,云助手亮相控制台
查看>>
Nacos 发布 0.9.0 版本,为 GA 作准备
查看>>
运维利器 RunDeck v3.0.15 发布, 服务器自动化操作
查看>>
后端架构师技术图谱
查看>>
快速掌握:大型分布式系统中的缓存架构
查看>>
redis系列:分布式锁
查看>>
ES6(Proxy 和 Reflect)
查看>>
spring+springMVC+mybatis的整合 part1
查看>>
[Spark]Spark Streaming 指南四 输入DStreams和Receivers
查看>>
Android Recyclerview 实现画廊功能
查看>>
Integer 与 Long 数字类型的比较:Java与Kotlin的细节不同
查看>>
官宣!vue.ant.design 低调上线
查看>>
云用户生态发展论坛暨第三届中国云计算用户大会北京站盛大召开
查看>>
Emulator 29.0.3 Canary 发布,Android 模拟器
查看>>
总结一波安卓组件化开源方案
查看>>