数据结构与算法--图的遍历(邻接矩阵和邻接表的深搜,广搜的递归和非递归算法以及判断是否是连通图)

前言

网上已经有很多博客讲了图的遍历的方法,因此本文不再赘述。

一.图的创建

1.节点类的定义

class ListNode {
    public ListNode(int num) {
        this.num = num;
    }

    int num;		//节点的序号
    ListNode next;
}

2.以邻接表储存的图的创建

    private ListNode[] createGraphByLists() {
        //初始化链表所有头节点
        for (int i = 0; i < vertexNum; i++) {
            ListNode headi = new ListNode(i);
            list[i] = headi;
        }
        //在下面增加边
        addEdge(0, 4);
        addEdge(0, 2);
        addEdge(0, 1);
        addEdge(1, 3);
        return list;
    }

    /**
     * 增加边的方法
     */
    private void addEdge(int i, int j) {
        ListNode tem = list[i].next;
        ListNode node = new ListNode(j);
        list[i].next = node;        //头插法
        node.next = tem;
    }

3.以邻接矩阵矩阵储存的图的创建

    private int[][] createGraphByArray() {
        Scanner sc = new Scanner(System.in);
        System.out.println("\n创建以二维数组储存的图:\n" + "是否有向图?(是输入y,否输入n)");
        String t = sc.next();
        boolean isDirected = false;
        if ("y".equals(t)) {
            isDirected = true;
            isDirect = true;
        } else if ("n".equals(t)) {
            isDirect = false;
        } else {
            System.out.println("错误,请重新输入");
        }
        System.out.println("输入节点和边数:");
        int nodeNum = sc.nextInt();
        int edgeNum = sc.nextInt();
        int[][] e = new int[nodeNum + 1][nodeNum + 1];
        if (isDirected) {   //有向图
            for (int i = 0; i < edgeNum; i++) {
                for (int j = 0; j < edgeNum; j++) {
                    if (i == j) {
                        e[i][j] = 666666;       //设666666为正无穷
                    }
                }
            }
            System.out.println("输入各顶点之间的边:");
            //下面读入各顶点之间的边
            for (int i = 0; i < edgeNum; i++) {
                int a = sc.nextInt();
                int b = sc.nextInt();
                e[a][b] = 1;
            }

        } else {        //无向图
            System.out.println("输入各顶点之间的边:");
            for (int i = 0; i < edgeNum; i++) {
                int a = sc.nextInt();
                int b = sc.nextInt();
                e[a][b] = 1;
                e[b][a] = 1;
            }
        }
        return e;
    }

二.图的深搜遍历

1.邻接表的深搜递归遍历

    private void dfs_digui_lists(ListNode[] list, int i) {   //i为出发点
        book[i] = 1;                    //1表示已经访问过
        ListNode p = list[i];           //p指向i节点
        System.out.print(p.num + " ");
        while (p != null) {                  //当前节点有相连的节点
            if (book[p.num] != 1) {          //当前节点未被访问过
                dfs_digui_lists(list, p.num); //访问第一个与i节点相连的节点
            }
            p = p.next;                      //遍历与i节点相连的节点
        }
    }

2.邻接表的深搜非递归遍历

    private void dfs_lists(ListNode[] list) {
        Stack<Integer> stack = new Stack<>();
        int[] visited = new int[list.length];
        stack.add(0);
        while (!stack.isEmpty()) {
            int n = stack.pop();
            if (visited[n] == 0) {
                visited[n] = 1;
                System.out.print(n + " ");
            }
            ListNode p = list[n];
            while (p != null) {
                if (visited[p.num] == 0) {
                    stack.add(p.num);
                }
                p = p.next;
            }
        }
        System.out.println();
    }

3.邻接矩阵的深搜遍历

    private int sum = 0;	//这里声明为全局变量
    private void dfs_array(int cur) {
        sum++;
        System.out.print(cur + " ");
        int n = e.length - 1;   //顶点的总个数
        if (sum == n) {
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (e[cur][i] == 1 && markArr[i] == 0) {
                if (!isDirect) {	//此处的标记位是来判断是否是有向图,不是的话将标记数组的两个元素都记作已访问
                    markArr[cur] = 1;
                }
                markArr[i] = 1;
                dfs_array(i);
            }
        }
    }

三.图的广搜遍历

1.邻接表的广搜遍历

    private void bfs_lists(ListNode[] list) {
        ListNode p;
        Queue<Integer> queue = new LinkedList<>();	//广搜遍历用队列来实现
        Arrays.fill(book, 0);		//这里的book数组是全局变量,在此处全部重置为0
        for (int i = 0; i < list.length; i++) {
            if (book[i] == 0) {
                book[i] = 1;
                System.out.print(list[i].num + " ");
                queue.add(i);
                while (!queue.isEmpty()) {
                    i = queue.poll();
                    p = list[i];
                    while (p != null) {
                        if (book[p.num] == 0) {
                            book[p.num] = 1;
                            System.out.print(p.num + " ");
                            queue.add(p.num);
                        }
                        p = p.next;
                    }
                }
            }
        }
        System.out.println();
    }

2.邻接矩阵的广搜遍历

    private void bfs_array() {
        Arrays.fill(markArr, 0);    //重置标记数组
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        markArr[1] = 1;
        System.out.print(1 + " ");
        while (!queue.isEmpty()) {
            int cur = queue.poll();
            for (int i = 1; i < e.length; i++) {
                if (e[cur][i] == 1 && markArr[i] == 0) {
                    System.out.print(i + " ");
                    if (isDirect) markArr[cur] = 1;
                    queue.add(i);
                    markArr[i] = 1;
                }
            }
        }
        System.out.println();
    }

三.判断图是否连通

方法:从每个节点出发,进行深搜遍历,因为每个连通子图都会进行一次深搜遍历,所以遍历一次给连通分量加1
因为上面已经进行了一次遍历,所以下面再次进行遍历时,如果还有未被访问的节点就直接可以判断不是连通图了,具体体现在dfs_Array中的isConnect标记位上。

	//这里声明的全局变量是为了方便其他方法调用
    private Integer num = 1;        //因为在前面已经进行了一次遍历,所以连通分量大小置为1
    private boolean isConnected = true;
    private boolean isConnected(int[][] e) {
        int[] book = new int[e.length];
        for (int i = 0; i < e.length; i++) {
            sum = 0;		//这里sum为全局变量,在前面的遍历中更改过值,所以每次调用前需要重置它的值
            if (book[i] == 0) {
                book[i] = 1;
                dfs_Array(i);
            }
        }
        System.out.println("\n连通分量个数:" + num);
        return isConnected;
    }


    private void dfs_Array(int cur) {
        sum++;
        int n = e.length - 1;   //顶点的总个数
        if (sum == n) {
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (e[cur][i] == 1 && markArr[i] == 0) {	//cur到i节点有边且未被访问过
                if (!isDirect) {
                    markArr[cur] = 1;
                }
                markArr[i] = 1;
                ++num;				//连通分量加1
                isConnected = false;
                dfs_Array(i);
            }
        }
    }

四.所有源代码

import java.util.*;

public class Graph {
    private final int vertexNum = 5;                    //邻接表节点个数
    private ListNode[] list = new ListNode[vertexNum];  //邻接表储存图
    private int[] book = new int[vertexNum];            //邻接表的访问标记数组
    private static int[][] e = new int[101][101];       //储存图的二维数组
    private int[] markArr = new int[101];               //二维数组的标记数组
    private boolean isDirect;                           //以二维数组储存的图是否是有向图

    public static void main(String[] args) {
        Graph graph = new Graph();
        ListNode[] heads = graph.createGraphByLists();
        System.out.println("邻接表的深搜递归遍历:");
        graph.dfs_digui_lists(heads, 0);
        System.out.println("\n邻接表的深搜非递归遍历:");
        graph.dfs_lists(heads);
        System.out.println("邻接表的广搜遍历:");
        graph.bfs_lists(heads);

        e = graph.createGraphByArray();
        System.out.println("\n二维数组的深搜遍历:");
        graph.dfs_array(1);

        System.out.println("\n二维数组的广搜遍历:");
        graph.bfs_array();

        System.out.println("该图是否连通:" + graph.isConnected(e));

    }


    /**
     * 深搜遍历二维数组
     */
    private int sum = 0;
    private void dfs_array(int cur) {
        sum++;
        System.out.print(cur + " ");
        int n = e.length - 1;   //顶点的总个数
        if (sum == n) {
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (e[cur][i] == 1 && markArr[i] == 0) {
                if (!isDirect) {
                    markArr[cur] = 1;
                }
                markArr[i] = 1;
                dfs_array(i);
            }
        }
    }

    private void dfs_Array(int cur) {
        sum++;
        int n = e.length - 1;   //顶点的总个数
        if (sum == n) {
            return;
        }
        for (int i = 1; i <= n; i++) {
            if (e[cur][i] == 1 && markArr[i] == 0) {
                if (!isDirect) {
                    markArr[cur] = 1;
                }
                markArr[i] = 1;
                ++num;
                isConnected = false;
                dfs_Array(i);
            }
        }
    }

    /**
     * 广搜遍历二维数组
     */
    private void bfs_array() {
        Arrays.fill(markArr, 0);    //重置标记数组
        Queue<Integer> queue = new LinkedList<>();
        queue.add(1);
        markArr[1] = 1;
        System.out.print(1 + " ");
        while (!queue.isEmpty()) {
            int cur = queue.poll();
            for (int i = 1; i < e.length; i++) {
                if (e[cur][i] == 1 && markArr[i] == 0) {
                    System.out.print(i + " ");
                    if (isDirect) markArr[cur] = 1;
                    queue.add(i);
                    markArr[i] = 1;
                }
            }
        }
        System.out.println();
    }


    /**
     * 创建以二维数组储存的图
     */
    private int[][] createGraphByArray() {
        Scanner sc = new Scanner(System.in);
        System.out.println("\n创建以二维数组储存的图:\n" + "是否有向图?(是输入y,否输入n)");
        String t = sc.next();
        boolean isDirected = false;
        if ("y".equals(t)) {
            isDirected = true;
            isDirect = true;
        } else if ("n".equals(t)) {
            isDirect = false;
        } else {
            System.out.println("错误,请重新输入");
        }
        System.out.println("输入节点和边数:");
        int nodeNum = sc.nextInt();
        int edgeNum = sc.nextInt();
        int[][] e = new int[nodeNum + 1][nodeNum + 1];
        if (isDirected) {   //有向图
            for (int i = 0; i < edgeNum; i++) {
                for (int j = 0; j < edgeNum; j++) {
                    if (i == j) {
                        e[i][j] = 666666;       //设666666为正无穷
                    }
                }
            }
            System.out.println("输入各顶点之间的边:");
            for (int i = 0; i < edgeNum; i++) {
                int a = sc.nextInt();
                int b = sc.nextInt();
                e[a][b] = 1;
            }

        } else {        //无向图
            System.out.println("输入各顶点之间的边:");
            for (int i = 0; i < edgeNum; i++) {
                int a = sc.nextInt();
                int b = sc.nextInt();
                e[a][b] = 1;
                e[b][a] = 1;
            }
        }
        return e;
    }

    /**
     * 邻接表的深搜遍历(递归)
     */
    private void dfs_digui_lists(ListNode[] list, int i) {   //i为出发点
        book[i] = 1;                    //1表示已经访问过
        ListNode p = list[i];           //p指向i节点
        System.out.print(p.num + " ");
        while (p != null) {                  //当前节点有相连的节点
            if (book[p.num] != 1) {          //当前节点未被访问过
                dfs_digui_lists(list, p.num); //访问第一个与i节点相连的节点
            }
            p = p.next;                      //遍历与i节点相连的节点
        }
    }

    /**
     * 邻接表的深搜(非递归)
     */
    private void dfs_lists(ListNode[] list) {
        Stack<Integer> stack = new Stack<>();
        int[] visited = new int[list.length];
        stack.add(0);
        while (!stack.isEmpty()) {
            int n = stack.pop();
            if (visited[n] == 0) {
                visited[n] = 1;
                System.out.print(n + " ");
            }
            ListNode p = list[n];
            while (p != null) {
                if (visited[p.num] == 0) {
                    stack.add(p.num);
                }
                p = p.next;
            }
        }
        System.out.println();
    }


    /**
     * 邻接表的广搜遍历
     */
    private void bfs_lists(ListNode[] list) {
        ListNode p;
        Queue<Integer> queue = new LinkedList<>();
        Arrays.fill(book, 0);
        for (int i = 0; i < list.length; i++) {
            if (book[i] == 0) {
                book[i] = 1;
                System.out.print(list[i].num + " ");
                queue.add(i);
                while (!queue.isEmpty()) {
                    i = queue.poll();
                    p = list[i];
                    while (p != null) {
                        if (book[p.num] == 0) {
                            book[p.num] = 1;
                            System.out.print(p.num + " ");
                            queue.add(p.num);
                        }
                        p = p.next;
                    }
                }
            }
        }
        System.out.println();
    }

    /**
     * 创建以邻接表储存的图
     */
    private ListNode[] createGraphByLists() {
        //初始化链表所有头节点
        for (int i = 0; i < vertexNum; i++) {
            ListNode headi = new ListNode(i);
            list[i] = headi;
        }
        addEdge(0, 4);
        addEdge(0, 2);
        addEdge(0, 1);
        addEdge(1, 3);
        return list;
    }

    /**
     * 增加边的方法
     */
    private void addEdge(int i, int j) {
        ListNode tem = list[i].next;
        ListNode node = new ListNode(j);
        list[i].next = node;        //头插法
        node.next = tem;
    }


    /**
     * 判断一个以邻接矩阵储存的图是否连通,并打印连通分支个数
     */
    private Integer num = 1;        //连通分量大小
    private boolean isConnected = true;
    private boolean isConnected(int[][] e) {
        int[] book = new int[e.length];
        for (int i = 0; i < e.length; i++) {
            sum = 0;
            if (book[i] == 0) {
                book[i] = 1;
                dfs_Array(i);
            }
        }
        System.out.println("\n连通分量个数:" + num);
        return isConnected;
    }

}

/**
 * 节点类
 */
class ListNode {
    public ListNode(int num) {
        this.num = num;
    }

    int num;
    ListNode next;
}

五.后记

代码中可能会有错误,敬请读者指正。

发布了14 篇原创文章 · 获赞 4 · 访问量 687

猜你喜欢

转载自blog.csdn.net/qq_43935080/article/details/103603997