数据结构(五)----图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jBexlhdk-1606460416157)(图.assets/image-20201126135435221.png)]

1.定义

  • 顶点 : 如上图中每个元素都是一个顶点 如 A B C D E F
  • 边 : 顶点到顶点直接建立的连接关系叫做边 如 AB AD AC等等
  • 度 : 每个顶点有多少条边就是该顶点的度 如A的度为3 C的度为3 B的度为2等等

2.图的分类

  • 生活社交网其实就是一个图结构
    • 比如微信 QQ 微博 等等
    • 像微信/QQ中的每个用户就是顶点 而用户之间添加好友后 这个添加关系就是边 用户添加的好友个数就是度
  • 有向图和无向图
    • 在微博中的图和微信 qq有些区别 比如用户A关注B 但是B可以不关注A 这样我们用图表示可以在上面基础上添加一个箭头指向 A关注B 可以A指向B 但是B不指向A C 和 F就是相互关注 这样的图叫做有向图 同理没有指向关系的叫做无向图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m0U0kfRr-1606460416159)(图.assets/image-20201126140258847.png)]

  • 带权图
    • 如下每条边都偶一个权重 可以看做是好友亲密度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GOxv0lEf-1606460416165)(图.assets/image-20201126140609743.png)]

3.图的存储

3.1邻接矩形存储
  • : 底层依赖一个二维数组
    • 无向图 : 如果2个顶点之间有边 就标记为 1
    • 有向图 : A顶点指向B顶点 B没有指向A顶点 则 (A,B)为1 (B,A)为0
    • 带权无向图存储权重

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jVvXH3a5-1606460416167)(图.assets/image-20201126154651791.png)]

  • 无向存储 : (x,y) x为横 y为纵
    • (1,1) (1,4) (4,1)等 没有边 所以为0
    • (1,2) (2,1) (2,3)等 有边 所以为 1
  • 有向存储 : 指向谁 谁那存储为1
    • (1,2) 1指向2 所以为1
    • (2,1) 2没有指向1 所以为0 等等
  • 带权无向 存储的是权重
    • (1,3) 权重为3
    • (2,3)权重为2 等等
  • 该方法
    • 优点是直观 简单
    • 缺点是浪费空间 我们可以发现其无向图是对称的 也就是说 只需要一半就可以完成需求 相当于浪费了一半空间
3.2 邻接链表存储
  • 有点类似于一个散列表 其每个顶点对应一条链表
  • 有向图 链表存储与该顶点相连指向的顶点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cPo5LCKs-1606460416168)(图.assets/image-20201126173916238.png)]

  • 无向图 链表存储的是与该顶点相连的顶点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LM9TuSkT-1606460416170)(图.assets/image-20201126173035121.png)]

  • 带权图 在链表每个节点里在添加一个元素存储器权值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7rsMFDXy-1606460416171)(图.assets/image-20201126174432450.png)]

  • 优点 : 节省空间存储
  • 缺点 : 查询效率低

4.图的应用

  • 构建一个无向图
package com.example.ioimage;

import java.util.LinkedList;

/**
 * 无向图
 */
public class GraphSeaerh {
    //顶点个数
    private int poings;
    //邻接表可以看做是一个散列表  头部存储的是各个顶点  链表其他节点存储与该顶点相连的其他顶点
    private LinkedList<Integer> linkedList[];

    public GraphSeaerh(int poings) {
        this.poings = poings;
        linkedList = new LinkedList[this.poings];
        //初始化每个槽位的链表
        for (int i = 0; i < this.poings; i++) {
            linkedList[i] = new LinkedList<>();//令数组每个槽位都是一个链表
        }
    }

    /**
     * 向数组中添加顶点(边)
     * @param i 新添加的顶点
     * @param j 和新顶点有关系的顶点
     */
    public void addEdge(int i,int j){
        //无向图中 新添加一个顶点后 是相互指向  一条边存储两回
        //注意 : 无向图中顶点值 对应数组下标
        linkedList[i].add(j);
        linkedList[j].add(i);
    }
}
4.1广度优先搜索 BFS
  • 是一种地毯式层层推进搜索策略 即先查找离起始顶点最近的 然后查找次近的 依次向外搜索 一般使用队列结构进行辅助查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0SFGXluf-1606460416172)(图.assets/image-20201127094346888.png)]

  • 步骤
    • 把源顶点放到队列里
    • 从来队列里弹出第一个顶点 并检查其是否为目标 如果找到目标 就返回结果 否则把它没有检查过的直接子顶点添加到队列里
    • 当队列为空时 代表整张图都检查过了 存在则返回 不存在就返回null
    • 重复第二步进行查询
  • 代码实现
 /**
     * 广度搜索
     *
     * @param source 源顶点
     * @param target 目标顶点
     */
    public void bfs(int source, int target) {
        if (source == target) {
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个队列
        Queue<Integer> queue = new LinkedList<>();
        queue.add(source);//把源顶点加入队列
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        while (!queue.isEmpty()) {//当队列非空时
            Integer poll = queue.poll();//取出队列元素 该元素被移除队列
            //从邻接表里取出和顶点相连的顶点元素
            for (int i = 0; i < linkedList[poll].size(); i++) {
                //取出和顶点poll相连的顶点p_connection
                Integer p_connection = linkedList[poll].get(i);
                //判断该顶点是否被访问过  true表示被访问过
                //如果没访问过 则取出与该顶点相连接的顶点
                if (!booleans[p_connection]) {
                    //记录p_connection之前的顶点为poll
                    prev[p_connection] = poll;
                    //判断与顶点poll相连的p_connection是否是目标顶点
                    if (p_connection == target) {
                        //打印访问线路
                        print(prev, source, target);
                        return;
                    }
                    //标记p_connection已经被访问过
                    booleans[p_connection] = true;
                    queue.add(p_connection);
                }
            }
        }
    }

    public void print(int[] prev, int source, int target) {
        if (prev[target] != -1 && source != target) {
            print(prev, source, prev[target]);
        }
        System.out.print(target + " --> ");
    }
  • 测试
  public static void main(String[] args) {
        GraphSeaerh graphSeaerh = new GraphSeaerh(8);
        graphSeaerh.addEdge(0,1);
        graphSeaerh.addEdge(0,3);
        graphSeaerh.addEdge(1,2);
        graphSeaerh.addEdge(1,4);
        graphSeaerh.addEdge(3,4);
        graphSeaerh.addEdge(2,5);
        graphSeaerh.addEdge(4,5);
        graphSeaerh.addEdge(4,6);
        graphSeaerh.addEdge(5,7);
        graphSeaerh.addEdge(6,7);
        graphSeaerh.bfs(0,7);
    }
4.2 深度优先搜索 DFS
  • “走迷宫” 假设你站在迷宫的某个岔路口,然后想找到出口。你随意选择一个岔路口来走,走着走着发现走不通的时候,你就回退到上一个岔路口,重新选择一条路继续走,直到最终找到出口

  • 从某个顶点到另外一个顶点 如下图 从s到t 使用深度递归算法 实线表示遍历 虚线表示回退 最终找到路径 (可以发现其并不是最近的路线)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nqct5TSC-1606460416174)(图.assets/image-20201127141053992.png)]

  • 代码实现
 /**
     * 深度搜索
     * @param source
     * @param target
     */
    public void dfs(int source, int target) {
        if (source == target){
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        resourceDFS(source,target,booleans,prev);
        print(prev,source,target);
    }

    //递归查询顶点p到目标顶点间的线路图
    public void resourceDFS(int source, int target, boolean[] booleans, int[] prev) {
        if (found){
            return;
        }
        booleans[source] = true;//标记当前顶点已经被访问过了
        if (source == target) {
            //如果当前顶点和目标顶点相同 则代表已经查找到了 把全局变量修改为true
            this.found = true;
            return;
        }
        //遍历当前顶点相连的所有顶点
        for (int i = 0; i < linkedList[source].size(); i++) {
            //获取与顶点p相连的顶点
            Integer p_connection = linkedList[source].get(i);
            //判断该顶点是否被访问过  true表示被访问过
            //如果没访问过 则取出与该顶点相连接的顶点
            if (!booleans[p_connection]) {
               prev[p_connection] = source;
               resourceDFS(p_connection,target,booleans,prev);
            }
        }
    }
  • 测试

  •  public static void main(String[] args) {
            GraphSeaerh graphSeaerh = new GraphSeaerh(8);
            graphSeaerh.addEdge(0,1);
            graphSeaerh.addEdge(0,3);
            graphSeaerh.addEdge(1,2);
            graphSeaerh.addEdge(1,4);
            graphSeaerh.addEdge(3,4);
            graphSeaerh.addEdge(2,5);
            graphSeaerh.addEdge(4,5);
            graphSeaerh.addEdge(4,6);
            graphSeaerh.addEdge(5,7);
            graphSeaerh.addEdge(6,7);
            graphSeaerh.bfs(0,4);
            System.out.println();
            graphSeaerh.dfs(0,4);
        }
    

5.完整代码

package com.example.ioimage;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 无向图
 */
public class GraphSeaerh {


    public static void main(String[] args) {
        GraphSeaerh graphSeaerh = new GraphSeaerh(8);
        graphSeaerh.addEdge(0,1);
        graphSeaerh.addEdge(0,3);
        graphSeaerh.addEdge(1,2);
        graphSeaerh.addEdge(1,4);
        graphSeaerh.addEdge(3,4);
        graphSeaerh.addEdge(2,5);
        graphSeaerh.addEdge(4,5);
        graphSeaerh.addEdge(4,6);
        graphSeaerh.addEdge(5,7);
        graphSeaerh.addEdge(6,7);
        graphSeaerh.bfs(0,4);
        System.out.println();
        graphSeaerh.dfs(0,4);
    }


    //顶点个数
    private int poings;
    //邻接表可以看做是一个散列表  头部存储的是各个顶点  链表其他节点存储与该顶点相连的其他顶点
    private LinkedList<Integer> linkedList[];

    public GraphSeaerh(int poings) {
        this.poings = poings;
        linkedList = new LinkedList[this.poings];
        //初始化每个槽位的链表
        for (int i = 0; i < this.poings; i++) {
            linkedList[i] = new LinkedList<>();//令数组每个槽位都是一个链表
        }
    }

    /**
     * 向数组中添加顶点(边)
     *
     * @param i 新添加的顶点
     * @param j 和新顶点有关系的顶点
     */
    public void addEdge(int i, int j) {
        //无向图中 新添加一个顶点后 是相互指向  一条边存储两回
        //注意 : 无向图中顶点值 对应数组下标
        linkedList[i].add(j);
        linkedList[j].add(i);
    }

    /**
     * 广度搜索
     *
     * @param source 源顶点
     * @param target 目标顶点
     */
    public void bfs(int source, int target) {
        if (source == target) {
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个队列
        Queue<Integer> queue = new LinkedList<>();
        queue.add(source);//把源顶点加入队列
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        while (!queue.isEmpty()) {//当队列非空时
            Integer poll = queue.poll();//取出队列元素 该元素被移除队列
            //从邻接表里取出和顶点相连的顶点元素
            for (int i = 0; i < linkedList[poll].size(); i++) {
                //取出和顶点poll相连的顶点p_connection
                Integer p_connection = linkedList[poll].get(i);
                //判断该顶点是否被访问过  true表示被访问过
                //如果没访问过 则取出与该顶点相连接的顶点
                if (!booleans[p_connection]) {
                    //记录p_connection之前的顶点为poll
                    prev[p_connection] = poll;
                    //判断与顶点poll相连的p_connection是否是目标顶点
                    if (p_connection == target) {
                        //打印访问线路
                        print(prev, source, target);
                        return;
                    }
                    //标记p_connection已经被访问过
                    booleans[p_connection] = true;
                    queue.add(p_connection);
                }
            }
        }
    }

    private boolean found = false;//记录是否查找到目标顶点

    /**
     * 深度搜索
     * @param source
     * @param target
     */
    public void dfs(int source, int target) {
        if (source == target){
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        resourceDFS(source,target,booleans,prev);
        print(prev,source,target);
    }

    //递归查询顶点p到目标顶点间的线路图
    public void resourceDFS(int source, int target, boolean[] booleans, int[] prev) {
        if (found){
            return;
        }
        booleans[source] = true;//标记当前顶点已经被访问过了
        if (source == target) {
            //如果当前顶点和目标顶点相同 则代表已经查找到了 把全局变量修改为true
            this.found = true;
            return;
        }
        //遍历当前顶点相连的所有顶点
        for (int i = 0; i < linkedList[source].size(); i++) {
            //获取与顶点p相连的顶点
            Integer p_connection = linkedList[source].get(i);
            //判断该顶点是否被访问过  true表示被访问过
            //如果没访问过 则取出与该顶点相连接的顶点
            if (!booleans[p_connection]) {
               prev[p_connection] = source;
               resourceDFS(p_connection,target,booleans,prev);
            }
        }
    }



    public void print(int[] prev, int source, int target) {
        if (prev[target] != -1 && source != target) {
            print(prev, source, prev[target]);
        }
        System.out.print(target + " --> ");
    }
}

1.定义

  • 顶点 : 如上图中每个元素都是一个顶点 如 A B C D E F
  • 边 : 顶点到顶点直接建立的连接关系叫做边 如 AB AD AC等等
  • 度 : 每个顶点有多少条边就是该顶点的度 如A的度为3 C的度为3 B的度为2等等

2.图的分类

  • 生活社交网其实就是一个图结构
    • 比如微信 QQ 微博 等等
    • 像微信/QQ中的每个用户就是顶点 而用户之间添加好友后 这个添加关系就是边 用户添加的好友个数就是度
  • 有向图和无向图
    • 在微博中的图和微信 qq有些区别 比如用户A关注B 但是B可以不关注A 这样我们用图表示可以在上面基础上添加一个箭头指向 A关注B 可以A指向B 但是B不指向A C 和 F就是相互关注 这样的图叫做有向图 同理没有指向关系的叫做无向图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8U0Mq5F-1606460388384)(图.assets/image-20201126140258847.png)]

  • 带权图
    • 如下每条边都偶一个权重 可以看做是好友亲密度

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RY84D1lw-1606460388387)(图.assets/image-20201126140609743.png)]

3.图的存储

3.1邻接矩形存储
  • : 底层依赖一个二维数组
    • 无向图 : 如果2个顶点之间有边 就标记为 1
    • 有向图 : A顶点指向B顶点 B没有指向A顶点 则 (A,B)为1 (B,A)为0
    • 带权无向图存储权重

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xKjldldf-1606460388389)(图.assets/image-20201126154651791.png)]

  • 无向存储 : (x,y) x为横 y为纵
    • (1,1) (1,4) (4,1)等 没有边 所以为0
    • (1,2) (2,1) (2,3)等 有边 所以为 1
  • 有向存储 : 指向谁 谁那存储为1
    • (1,2) 1指向2 所以为1
    • (2,1) 2没有指向1 所以为0 等等
  • 带权无向 存储的是权重
    • (1,3) 权重为3
    • (2,3)权重为2 等等
  • 该方法
    • 优点是直观 简单
    • 缺点是浪费空间 我们可以发现其无向图是对称的 也就是说 只需要一半就可以完成需求 相当于浪费了一半空间
3.2 邻接链表存储
  • 有点类似于一个散列表 其每个顶点对应一条链表
  • 有向图 链表存储与该顶点相连指向的顶点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rBiX1yXS-1606460388392)(图.assets/image-20201126173916238.png)]

  • 无向图 链表存储的是与该顶点相连的顶点

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8BKCGZr-1606460388392)(图.assets/image-20201126173035121.png)]

  • 带权图 在链表每个节点里在添加一个元素存储器权值

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OmeTiIrk-1606460388393)(图.assets/image-20201126174432450.png)]

  • 优点 : 节省空间存储
  • 缺点 : 查询效率低

4.图的应用

  • 构建一个无向图
package com.example.ioimage;

import java.util.LinkedList;

/**
 * 无向图
 */
public class GraphSeaerh {
    //顶点个数
    private int poings;
    //邻接表可以看做是一个散列表  头部存储的是各个顶点  链表其他节点存储与该顶点相连的其他顶点
    private LinkedList<Integer> linkedList[];

    public GraphSeaerh(int poings) {
        this.poings = poings;
        linkedList = new LinkedList[this.poings];
        //初始化每个槽位的链表
        for (int i = 0; i < this.poings; i++) {
            linkedList[i] = new LinkedList<>();//令数组每个槽位都是一个链表
        }
    }

    /**
     * 向数组中添加顶点(边)
     * @param i 新添加的顶点
     * @param j 和新顶点有关系的顶点
     */
    public void addEdge(int i,int j){
        //无向图中 新添加一个顶点后 是相互指向  一条边存储两回
        //注意 : 无向图中顶点值 对应数组下标
        linkedList[i].add(j);
        linkedList[j].add(i);
    }
}
4.1广度优先搜索 BFS
  • 是一种地毯式层层推进搜索策略 即先查找离起始顶点最近的 然后查找次近的 依次向外搜索 一般使用队列结构进行辅助查询

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QvumeB3n-1606460388395)(图.assets/image-20201127094346888.png)]

  • 步骤
    • 把源顶点放到队列里
    • 从来队列里弹出第一个顶点 并检查其是否为目标 如果找到目标 就返回结果 否则把它没有检查过的直接子顶点添加到队列里
    • 当队列为空时 代表整张图都检查过了 存在则返回 不存在就返回null
    • 重复第二步进行查询
  • 代码实现
 /**
     * 广度搜索
     *
     * @param source 源顶点
     * @param target 目标顶点
     */
    public void bfs(int source, int target) {
        if (source == target) {
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个队列
        Queue<Integer> queue = new LinkedList<>();
        queue.add(source);//把源顶点加入队列
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        while (!queue.isEmpty()) {//当队列非空时
            Integer poll = queue.poll();//取出队列元素 该元素被移除队列
            //从邻接表里取出和顶点相连的顶点元素
            for (int i = 0; i < linkedList[poll].size(); i++) {
                //取出和顶点poll相连的顶点p_connection
                Integer p_connection = linkedList[poll].get(i);
                //判断该顶点是否被访问过  true表示被访问过
                //如果没访问过 则取出与该顶点相连接的顶点
                if (!booleans[p_connection]) {
                    //记录p_connection之前的顶点为poll
                    prev[p_connection] = poll;
                    //判断与顶点poll相连的p_connection是否是目标顶点
                    if (p_connection == target) {
                        //打印访问线路
                        print(prev, source, target);
                        return;
                    }
                    //标记p_connection已经被访问过
                    booleans[p_connection] = true;
                    queue.add(p_connection);
                }
            }
        }
    }

    public void print(int[] prev, int source, int target) {
        if (prev[target] != -1 && source != target) {
            print(prev, source, prev[target]);
        }
        System.out.print(target + " --> ");
    }
  • 测试
  public static void main(String[] args) {
        GraphSeaerh graphSeaerh = new GraphSeaerh(8);
        graphSeaerh.addEdge(0,1);
        graphSeaerh.addEdge(0,3);
        graphSeaerh.addEdge(1,2);
        graphSeaerh.addEdge(1,4);
        graphSeaerh.addEdge(3,4);
        graphSeaerh.addEdge(2,5);
        graphSeaerh.addEdge(4,5);
        graphSeaerh.addEdge(4,6);
        graphSeaerh.addEdge(5,7);
        graphSeaerh.addEdge(6,7);
        graphSeaerh.bfs(0,7);
    }
4.2 深度优先搜索 DFS
  • “走迷宫” 假设你站在迷宫的某个岔路口,然后想找到出口。你随意选择一个岔路口来走,走着走着发现走不通的时候,你就回退到上一个岔路口,重新选择一条路继续走,直到最终找到出口

  • 从某个顶点到另外一个顶点 如下图 从s到t 使用深度递归算法 实线表示遍历 虚线表示回退 最终找到路径 (可以发现其并不是最近的路线)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aewlLI9m-1606460388396)(图.assets/image-20201127141053992.png)]

  • 代码实现
 /**
     * 深度搜索
     * @param source
     * @param target
     */
    public void dfs(int source, int target) {
        if (source == target){
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        resourceDFS(source,target,booleans,prev);
        print(prev,source,target);
    }

    //递归查询顶点p到目标顶点间的线路图
    public void resourceDFS(int source, int target, boolean[] booleans, int[] prev) {
        if (found){
            return;
        }
        booleans[source] = true;//标记当前顶点已经被访问过了
        if (source == target) {
            //如果当前顶点和目标顶点相同 则代表已经查找到了 把全局变量修改为true
            this.found = true;
            return;
        }
        //遍历当前顶点相连的所有顶点
        for (int i = 0; i < linkedList[source].size(); i++) {
            //获取与顶点p相连的顶点
            Integer p_connection = linkedList[source].get(i);
            //判断该顶点是否被访问过  true表示被访问过
            //如果没访问过 则取出与该顶点相连接的顶点
            if (!booleans[p_connection]) {
               prev[p_connection] = source;
               resourceDFS(p_connection,target,booleans,prev);
            }
        }
    }
  • 测试

  •  public static void main(String[] args) {
            GraphSeaerh graphSeaerh = new GraphSeaerh(8);
            graphSeaerh.addEdge(0,1);
            graphSeaerh.addEdge(0,3);
            graphSeaerh.addEdge(1,2);
            graphSeaerh.addEdge(1,4);
            graphSeaerh.addEdge(3,4);
            graphSeaerh.addEdge(2,5);
            graphSeaerh.addEdge(4,5);
            graphSeaerh.addEdge(4,6);
            graphSeaerh.addEdge(5,7);
            graphSeaerh.addEdge(6,7);
            graphSeaerh.bfs(0,4);
            System.out.println();
            graphSeaerh.dfs(0,4);
        }
    

5.完整代码

package com.example.ioimage;

import java.util.LinkedList;
import java.util.Queue;

/**
 * 无向图
 */
public class GraphSeaerh {


    public static void main(String[] args) {
        GraphSeaerh graphSeaerh = new GraphSeaerh(8);
        graphSeaerh.addEdge(0,1);
        graphSeaerh.addEdge(0,3);
        graphSeaerh.addEdge(1,2);
        graphSeaerh.addEdge(1,4);
        graphSeaerh.addEdge(3,4);
        graphSeaerh.addEdge(2,5);
        graphSeaerh.addEdge(4,5);
        graphSeaerh.addEdge(4,6);
        graphSeaerh.addEdge(5,7);
        graphSeaerh.addEdge(6,7);
        graphSeaerh.bfs(0,4);
        System.out.println();
        graphSeaerh.dfs(0,4);
    }


    //顶点个数
    private int poings;
    //邻接表可以看做是一个散列表  头部存储的是各个顶点  链表其他节点存储与该顶点相连的其他顶点
    private LinkedList<Integer> linkedList[];

    public GraphSeaerh(int poings) {
        this.poings = poings;
        linkedList = new LinkedList[this.poings];
        //初始化每个槽位的链表
        for (int i = 0; i < this.poings; i++) {
            linkedList[i] = new LinkedList<>();//令数组每个槽位都是一个链表
        }
    }

    /**
     * 向数组中添加顶点(边)
     *
     * @param i 新添加的顶点
     * @param j 和新顶点有关系的顶点
     */
    public void addEdge(int i, int j) {
        //无向图中 新添加一个顶点后 是相互指向  一条边存储两回
        //注意 : 无向图中顶点值 对应数组下标
        linkedList[i].add(j);
        linkedList[j].add(i);
    }

    /**
     * 广度搜索
     *
     * @param source 源顶点
     * @param target 目标顶点
     */
    public void bfs(int source, int target) {
        if (source == target) {
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个队列
        Queue<Integer> queue = new LinkedList<>();
        queue.add(source);//把源顶点加入队列
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        while (!queue.isEmpty()) {//当队列非空时
            Integer poll = queue.poll();//取出队列元素 该元素被移除队列
            //从邻接表里取出和顶点相连的顶点元素
            for (int i = 0; i < linkedList[poll].size(); i++) {
                //取出和顶点poll相连的顶点p_connection
                Integer p_connection = linkedList[poll].get(i);
                //判断该顶点是否被访问过  true表示被访问过
                //如果没访问过 则取出与该顶点相连接的顶点
                if (!booleans[p_connection]) {
                    //记录p_connection之前的顶点为poll
                    prev[p_connection] = poll;
                    //判断与顶点poll相连的p_connection是否是目标顶点
                    if (p_connection == target) {
                        //打印访问线路
                        print(prev, source, target);
                        return;
                    }
                    //标记p_connection已经被访问过
                    booleans[p_connection] = true;
                    queue.add(p_connection);
                }
            }
        }
    }

    private boolean found = false;//记录是否查找到目标顶点

    /**
     * 深度搜索
     * @param source
     * @param target
     */
    public void dfs(int source, int target) {
        if (source == target){
            return;
        }
        //定义一个boolean数组 记录顶点是否被访问过
        boolean[] booleans = new boolean[this.poings];
        booleans[source] = true;
        //定义一个数组 记录源顶点和目标顶点之间的线路
        int[] prev = new int[this.poings];
        for (int i = 0; i < prev.length; i++) {
            prev[i] = -1;
        }
        resourceDFS(source,target,booleans,prev);
        print(prev,source,target);
    }

    //递归查询顶点p到目标顶点间的线路图
    public void resourceDFS(int source, int target, boolean[] booleans, int[] prev) {
        if (found){
            return;
        }
        booleans[source] = true;//标记当前顶点已经被访问过了
        if (source == target) {
            //如果当前顶点和目标顶点相同 则代表已经查找到了 把全局变量修改为true
            this.found = true;
            return;
        }
        //遍历当前顶点相连的所有顶点
        for (int i = 0; i < linkedList[source].size(); i++) {
            //获取与顶点p相连的顶点
            Integer p_connection = linkedList[source].get(i);
            //判断该顶点是否被访问过  true表示被访问过
            //如果没访问过 则取出与该顶点相连接的顶点
            if (!booleans[p_connection]) {
               prev[p_connection] = source;
               resourceDFS(p_connection,target,booleans,prev);
            }
        }
    }



    public void print(int[] prev, int source, int target) {
        if (prev[target] != -1 && source != target) {
            print(prev, source, prev[target]);
        }
        System.out.print(target + " --> ");
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值