图的表示及遍历

一、图的表示

1.邻接矩阵

缺点是比较浪费空间,但是优点是查询效率高,而且方便矩阵运算。

a.有向图

  • 顶点vi的度是邻接矩阵中的第i行元素之和。
  • 对称矩阵,可压缩存储,有n个结点的无向图需要的存储空间为n(n+1)/2。

b.无向图

  • 矩阵不一定对称,有n个顶点的有向图需要存储空间为n^2。
  • 顶点vi的出度是邻接矩阵中第i行元素之和,顶点vi的出度是邻接矩阵中第i列元素之和。

2.邻接表

比较节省存储空间,但链表不方便查找,所以查询效率没有邻接矩阵存储方式高。

  • 图中顶点用一个一维数组存储。顶点数组中,每个数据元素还需要存储指向第一个邻接点的指针,以便于查找该结点的边信息。
  • 图中每个顶点vi的所有邻接点构成一个线性表。由于邻接点的个数不定,所以用单链表存储。无向图中称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。

a.有向图

b.无向图

二、图的遍历

1.广度优先搜索(BFS)

借助队列来实现,遍历得到的路径是起始顶点到终止顶点的最短路径。

a.基于邻接矩阵的实现

public class Graph_AdjacencyMatrix_Bfs {

    private int mVertexNum; // 顶点数
    private int[][] mAdjacencyMatrix; // 邻接矩阵

    public Graph_AdjacencyMatrix_Bfs(int vertexNum) {
        this.mVertexNum = vertexNum;
        this.mAdjacencyMatrix = new int[vertexNum][vertexNum];
        for (int i = 0; i < vertexNum; ++i) {
            for (int j = 0; j < vertexNum; ++j) {
                mAdjacencyMatrix[i][j] = 0;
            }
        }
    }

    /**
     * 添加图的边
     *
     * @param s 起始顶点
     * @param t 终止顶点
     */
    public void addEdge(int s, int t) {
        mAdjacencyMatrix[s][t] = 1;
        mAdjacencyMatrix[t][s] = 1; // 无向图一条边存两次
    }

    /**
     * 广度优先搜索(最短路径)
     *
     * @param s
     * @param t
     */
    public void bfs(int s, int t) {
        if (s >= mVertexNum || t >= mVertexNum || s == t) {
            return;
        }

        // visited是用来记录已经被访问的顶点,用来避免顶点被重复访问
        boolean[] visited = new boolean[mVertexNum];
        visited[s] = true;

        // prev用来记录搜索路径,prev[i]存储的是:顶点i是从哪个前驱顶点遍历过来的
        int[] prev = new int[mVertexNum];
        for (int i = 0; i < mVertexNum; ++i) {
            prev[i] = -1;
        }

        // queue是一个队列,用来存储已经被访问,但相连的顶点还没有被访问的顶点
        Queue<Integer> queue = new LinkedList<>();
        queue.add(s);

        while (queue.size() != 0) {
            int w = queue.poll();
            for (int i = 0; i < mVertexNum; ++i) {
                int v = mAdjacencyMatrix[w][i];
                if (v == 1 && !visited[i]) {
                    prev[i] = w;
                    if (i == t) {
                        print(prev, s, t);
                        return;
                    }
                    visited[i] = true;
                    queue.add(i);
                }
            }
        }
    }

    /**
     * 递归打印 s->t 的路径
     *
     * @param prev
     * @param s
     * @param t
     */
    private void print(int[] prev, int s, int t) {
        if (prev[t] != -1 && t != s) {
            print(prev, s, prev[t]);
        }
        System.out.print(t + " ");
    }

    public static void main(String[] args) {
        Graph_AdjacencyMatrix_Bfs graph = new Graph_AdjacencyMatrix_Bfs(8);
        graph.addEdge(0, 1);
        graph.addEdge(0, 3);
        graph.addEdge(1, 2);
        graph.addEdge(1, 4);
        graph.addEdge(2, 5);
        graph.addEdge(4, 5);
        graph.addEdge(4, 6);
        graph.addEdge(5, 7);
        graph.addEdge(6, 7);
        graph.bfs(0, 6);
    }
}

b.基于邻接表的实现

public class Graph_AdjacencyList_Bfs {

    private int mVertexNum; // 顶点数
    private List<Integer> mAdjacencyList[]; // 邻接表

    public Graph_AdjacencyList_Bfs(int vertexNum) {
        this.mVertexNum = vertexNum;
        this.mAdjacencyList = new LinkedList[vertexNum];
        for (int i = 0; i < vertexNum; ++i) {
            mAdjacencyList[i] = new LinkedList<>();
        }
    }

    /**
     * 添加图的边
     *
     * @param s 起始顶点
     * @param t 终止顶点
     */
    public void addEdge(int s, int t) {
        mAdjacencyList[s].add(t);
        mAdjacencyList[t].add(s); // 无向图一条边存两次
    }

    /**
     * 广度优先搜索(最短路径)
     *
     * @param s
     * @param t
     */
    public void bfs(int s, int t) {
        if (s >= mVertexNum || t >= mVertexNum || s == t) {
            return;
        }

        // visited是用来记录已经被访问的顶点,用来避免顶点被重复访问
        boolean[] visited = new boolean[mVertexNum];
        visited[s] = true;

        // prev用来记录搜索路径,prev[i]存储的是:顶点i是从哪个前驱顶点遍历过来的
        int[] prev = new int[mVertexNum];
        for (int i = 0; i < mVertexNum; ++i) {
            prev[i] = -1;
        }

        // queue是一个队列,用来存储已经被访问,但相连的顶点还没有被访问的顶点
        Queue<Integer> queue = new LinkedList<>();
        queue.add(s);

        while (queue.size() != 0) {
            int w = queue.poll();
            for (int i = 0; i < mAdjacencyList[w].size(); ++i) {
                int q = mAdjacencyList[w].get(i);
                if (!visited[q]) {
                    prev[q] = w;
                    if (q == t) {
                        print(prev, s, t);
                        return;
                    }
                    visited[q] = true;
                    queue.add(q);
                }
            }
        }
    }

    /**
     * 递归打印 s->t 的路径
     *
     * @param prev
     * @param s
     * @param t
     */
    private void print(int[] prev, int s, int t) {
        if (prev[t] != -1 && t != s) {
            print(prev, s, prev[t]);
        }
        System.out.print(t + " ");
    }

    public static void main(String[] args) {
        Graph_AdjacencyList_Bfs graph = new Graph_AdjacencyList_Bfs(8);
        graph.addEdge(0, 1);
        graph.addEdge(0, 3);
        graph.addEdge(1, 2);
        graph.addEdge(1, 4);
        graph.addEdge(2, 5);
        graph.addEdge(4, 5);
        graph.addEdge(4, 6);
        graph.addEdge(5, 7);
        graph.addEdge(6, 7);
        graph.bfs(0, 6);
    }
}

2.深度优先搜索(DFS)

常用递归实现,即借助栈来实现,遍历得到的路径不一定是起始顶点到终止顶点的最短路径。

a.基于邻接矩阵的实现

public class Graph_AdjacencyMatrix_Dfs {

    private boolean isFound; // 是否找到终止顶点

    private int mVertexNum; // 顶点数
    private int[][] mAdjacencyMatrix; // 邻接表

    public Graph_AdjacencyMatrix_Dfs(int vertexNum) {
        this.mVertexNum = vertexNum;
        this.mAdjacencyMatrix = new int[vertexNum][vertexNum];
        for (int i = 0; i < vertexNum; ++i) {
            for (int j = 0; j < vertexNum; ++j) {
                mAdjacencyMatrix[i][j] = 0;
            }
        }
    }

    /**
     * 添加图的边
     *
     * @param s 起始顶点
     * @param t 终止顶点
     */
    public void addEdge(int s, int t) {
        mAdjacencyMatrix[s][t] = 1;
        mAdjacencyMatrix[t][s] = 1; // 无向图一条边存两次
    }

    /**
     * 深度优先搜索
     *
     * @param s
     * @param t
     */
    public void dfs(int s, int t) {
        if (s >= mVertexNum || t >= mVertexNum || s == t) {
            return;
        }

        // visited是用来记录已经被访问的顶点,用来避免顶点被重复访问
        boolean[] visited = new boolean[mVertexNum];

        // prev用来记录搜索路径,prev[i]存储的是:顶点i是从哪个前驱顶点遍历过来的
        int[] prev = new int[mVertexNum];
        for (int i = 0; i < mVertexNum; ++i) {
            prev[i] = -1;
        }

        recurDfs(s, t, visited, prev);
        print(prev, s, t);
    }

    /**
     * 递归方法
     *
     * @param w
     * @param t
     * @param visited
     * @param prev
     */
    private void recurDfs(int w, int t, boolean[] visited, int[] prev) {
        if (isFound) {
            return;
        }

        visited[w] = true;
        if (w == t) {
            isFound = true;
            return;
        }

        for (int i = 0; i < mVertexNum; ++i) {
            int v = mAdjacencyMatrix[w][i];
            if (v == 1 && !visited[i]) {
                prev[i] = w;
                recurDfs(i, t, visited, prev);
            }
        }
    }

    /**
     * 递归打印 s->t 的路径
     *
     * @param prev
     * @param s
     * @param t
     */
    private void print(int[] prev, int s, int t) {
        if (prev[t] != -1 && t != s) {
            print(prev, s, prev[t]);
        }
        System.out.print(t + " ");
    }

    public static void main(String[] args) {
        Graph_AdjacencyMatrix_Dfs graph = new Graph_AdjacencyMatrix_Dfs(8);
        graph.addEdge(0, 1);
        graph.addEdge(0, 3);
        graph.addEdge(1, 2);
        graph.addEdge(1, 4);
        graph.addEdge(2, 5);
        graph.addEdge(4, 5);
        graph.addEdge(4, 6);
        graph.addEdge(5, 7);
        graph.addEdge(6, 7);
        graph.dfs(0, 6);
    }
}

b.基于邻接表的实现

public class Graph_AdjacencyList_Dfs {

    private boolean isFound; // 是否找到终止顶点

    private int mVertexNum; // 顶点数
    private List<Integer> mAdjacencyList[]; // 邻接表

    public Graph_AdjacencyList_Dfs(int vertexNum) {
        this.mVertexNum = vertexNum;
        this.mAdjacencyList = new LinkedList[vertexNum];
        for (int i = 0; i < vertexNum; ++i) {
            mAdjacencyList[i] = new LinkedList<>();
        }
    }

    /**
     * 添加图的边
     *
     * @param s 起始顶点
     * @param t 终止顶点
     */
    public void addEdge(int s, int t) {
        mAdjacencyList[s].add(t);
        mAdjacencyList[t].add(s); // 无向图一条边存两次
    }

    /**
     * 深度优先搜索
     *
     * @param s
     * @param t
     */
    public void dfs(int s, int t) {
        if (s >= mVertexNum || t >= mVertexNum || s == t) {
            return;
        }

        // visited是用来记录已经被访问的顶点,用来避免顶点被重复访问
        boolean[] visited = new boolean[mVertexNum];

        // prev用来记录搜索路径,prev[i]存储的是:顶点i是从哪个前驱顶点遍历过来的
        int[] prev = new int[mVertexNum];
        for (int i = 0; i < mVertexNum; ++i) {
            prev[i] = -1;
        }

        recurDfs(s, t, visited, prev);
        print(prev, s, t);
    }

    /**
     * 递归方法
     *
     * @param w
     * @param t
     * @param visited
     * @param prev
     */
    private void recurDfs(int w, int t, boolean[] visited, int[] prev) {
        if (isFound) {
            return;
        }

        visited[w] = true;
        if (w == t) {
            isFound = true;
            return;
        }

        for (int i = 0; i < mAdjacencyList[w].size(); ++i) {
            int q = mAdjacencyList[w].get(i);
            if (!visited[q]) {
                prev[q] = w;
                recurDfs(q, t, visited, prev);
            }
        }
    }

    /**
     * 递归打印 s->t 的路径
     *
     * @param prev
     * @param s
     * @param t
     */
    private void print(int[] prev, int s, int t) {
        if (prev[t] != -1 && t != s) {
            print(prev, s, prev[t]);
        }
        System.out.print(t + " ");
    }

    public static void main(String[] args) {
        Graph_AdjacencyList_Dfs graph = new Graph_AdjacencyList_Dfs(8);
        graph.addEdge(0, 1);
        graph.addEdge(0, 3);
        graph.addEdge(1, 2);
        graph.addEdge(1, 4);
        graph.addEdge(2, 5);
        graph.addEdge(4, 5);
        graph.addEdge(4, 6);
        graph.addEdge(5, 7);
        graph.addEdge(6, 7);
        graph.dfs(0, 6);
    }
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
邻接表是一种表示方法,它用链表来表示每个顶点相邻顶点的集合,具体来说,邻接表中每个顶点都对应一个链表,链表中存储该顶点与其它顶点相邻的边的信息,这种方法可以节省存储空间,适用于稀疏。 以下是邻接表的示例: ``` 1 -- 2 -- 3 | | 4 -- 5 -- 6 ``` 对应的邻接表如下: ``` 1 -> 2 -> 4 2 -> 1 -> 3 -> 5 3 -> 2 -> 6 4 -> 1 -> 5 5 -> 2 -> 4 -> 6 6 -> 3 -> 5 ``` 遍历是指按一定的规则依次访问中的所有顶点,常用的有深度优先遍历(DFS)和广度优先遍历(BFS)。 深度优先遍历的基本思想是从一个顶点开始,尽可能深地探索每个分支,直到到达最深处,然后回溯到前一个节点,再继续探索其他分支。具体实现可以使用递归或栈来实现。 以下是深度优先遍历的示例代码: ```python def dfs(graph, start): visited = set() # 记录已访问的节点 stack = [start] # 用栈来实现深度优先遍历 while stack: node = stack.pop() # 弹出栈顶节点 if node not in visited: visited.add(node) print(node, end=' ') for neighbor in graph[node]: stack.append(neighbor) ``` 对于上述示例,从顶点1开始进行深度优先遍历,输出结果为:1 4 5 2 3 6。 广度优先遍历的基本思想是先访问起始顶点的所有邻接顶点,再逐层向外访问其它顶点,具体实现可以使用队列来实现。 以下是广度优先遍历的示例代码: ```python from collections import deque def bfs(graph, start): visited = set() # 记录已访问的节点 queue = deque([start]) # 用队列来实现广度优先遍历 while queue: node = queue.popleft() # 弹出队列头节点 if node not in visited: visited.add(node) print(node, end=' ') for neighbor in graph[node]: queue.append(neighbor) ``` 对于上述示例,从顶点1开始进行广度优先遍历,输出结果为:1 2 4 3 5 6。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值