zoukankan      html  css  js  c++  java
  • 数据结构之图(Graph)

    图的概述

    什么是图

    map_overview
    如图就是一张图,其实之前介绍的树、链表都可以看做一个简单的图。

    图描述的是一种多对多的关系,由顶点(vertex)和连接顶点间的边(edge)组成。每个顶点可以有零个或多个前驱、也可以有零个或多个后继。
    注:图可以没有边,但至少有一个顶点。

    因此图可以表示成G=(V,E)。V即顶点的集合、E为边的集合。

    G = (V,E)。 其中,VE={<v,w>|v,w(in)V且P(v,w)}。
    <v,w>是从v到w的一条边,有方向的,与<w,v>不一样的。P(v,w)是定义在<v,w>上的权值信息(即赋予的有意义的数值或信息)。

    图的定义及术语

    几种常见的图
    • 无向图:图中的边没有方向。
    • 有向图:图中的边存在方向。
    • 完全图:图中任意两个顶点都有边连接。
    • 无权图:边上没有赋值,即不带权值。
    • 有权图:边上附有权值。
    顶点与边的数目关系

    假使图中有n个顶点,e条边。
    若有e=n(n-1)/2条无向边,则是无向完全图
    若有e=n(n-1)条有向边,则是有向完全图
    若e<nlogn则称作稀疏图,否则为稠密图

    图的一些概念
    • 邻接点:若顶点v和w之间存在边,则v、w互为邻接点。
    • 度:与顶点v关联的边的数目,即是度。
    • 出度/入度:对于有向图,<v,w>是v指向w的一条边,那么该边是v的出边,w的入边。
      出度/入度即是顶点的出边/入边数目。
      所以顶点的度=出度+入度。
    • 路径长度:从v到w之间存在一条路径,路径上边的数目即是路径的长度。
    • 简单路径:即路径中顶点不重复的路径。
    • 连通图:若G(图)中,任意两个顶点之间都有路径连通,则G称为连通图。
    • 强连通图:对于有向图中,任意两点之间都有一条有向路径。则称这个有向图为强连通图。
    • 连通分量:非连通图,其中的极大连通子图称为连通分量。这里的极大是指子图中包含的顶点个数极大。
    • 生成树:假使一个连通图,n个顶点e条边,若n-1条边与n个顶点构成的极小连通子图,则这个极小连通子图称为该连通图的生成树。如下面的连通图,红色部分即以D为根的生成树。
    • 生成森林:非连通图,每个连通分量的生成树 共同构成该非连通图的生成森林。
      map_tree

    图的存储

    邻接矩阵

    如下图,右边的就是左边图的邻接矩阵表示。即记录每个顶点与其他所有点是否存在边,存在则标1,否则为0。
    map_array
    标志信息也可以根据实际情况表示,如权值等。
    无向图该矩阵是对称的,有向图则不一定。

    邻接表

    如下图所示,每个顶点指向了一个链表,链表里记录了所有存在边的顶点在数组中的位置。
    map_array_link

    图的遍历

    map_search
    图的遍历都以这个有向图为例进行说明的。

    从图中某个顶点V0出发,访问V0,然后依次以v0相连的顶点为出发点向后访问。依次类推,直到所有与V0顶点相连的所有结点都被访问到。 如果图中仍有未访问的顶点,则该图为非连通图,在图中未访问的顶点选择一个顶点为起点,并重复上述过程,直到访问完图中的所有顶点为止。
    下面分别是使用邻接矩阵和邻接表表示的图 实现的深度优先的demo,可以大致看看了解下。
    邻接矩阵表示的图
    注意,邻接矩阵表示 使用的非递归方法实现,通过栈来实现的

    import java.util.ArrayList;
    import java.util.Stack;
    
    //深度优先:使用邻接矩阵存储方式
    public class DFSArrayTest {
        static ArrayList<Node> nodes = new ArrayList<>();
        static class Node {
            char data;//数据
            boolean visited;//是否访问过标志
    
            Node(char data) {
                this.data = data;
            }
        }
    
        //查找某顶点在邻接矩阵中的邻接点
        public ArrayList<Node> findNeighbours(int adjacency_matrix[][], Node x) {
            int nodeIndex = -1;
    
            ArrayList<Node> neighbours = new ArrayList<>();
            for (int i = 0; i < nodes.size(); i++) {
                if (nodes.get(i).equals(x)) {
                    nodeIndex = i;
                    break;
                }
            }
    
            if (nodeIndex != -1) {
                for (int j = 0; j < adjacency_matrix[nodeIndex].length; j++) {
                    if (adjacency_matrix[nodeIndex][j] == 1) {
                        neighbours.add(nodes.get(j));
                    }
                }
            }
            return neighbours;
        }
    
        //使用栈实现深度优先
        public void dfsUseStack(int adjacency_matrix[][], Node node) {
            Stack<Node> stack = new Stack<>();
            stack.add(node);
            
            while (!stack.isEmpty()) {
                Node element = stack.pop();
                if (!element.visited) {
                    System.out.print(element.data + " ");
                    element.visited = true;
                }
    
                ArrayList<Node> neighbours = findNeighbours(adjacency_matrix, element);
                for (int i = 0; i < neighbours.size(); i++) {
                    Node n = neighbours.get(i);
                    if (n != null && !n.visited) {
                        stack.add(n);
                    }
                }
            }
        }
    
        public static void clearVisitedFlags() {
            for (int i = 0; i < nodes.size(); i++) {
                nodes.get(i).visited = false;
            }
        }
    
        public static void main(String arg[]) {
            Node nodeA = new Node('A');
            Node nodeB = new Node('B');
            Node nodeC = new Node('C');
            Node nodeD = new Node('D');
            Node nodeE = new Node('E');
            int adjacency_matrix[][] = { 
                    //A  B  C  D  E
                    { 0, 1, 0, 0, 1 }, // A
                    { 0, 0, 0, 1, 0 }, // B
                    { 1, 0, 0, 0, 0 }, // C
                    { 0, 0, 1, 0, 0 }, // D
                    { 0, 0, 0, 1, 0 }  // E
            };
    		
            nodes.add(nodeA);nodes.add(nodeB);nodes.add(nodeC);nodes.add(nodeD);nodes.add(nodeE);
    		
            DFSArrayTest dfsArrayTest = new DFSArrayTest();
            System.out.println("DFSArray(以C为开始顶点):");
            dfsArrayTest.dfsUseStack(adjacency_matrix, nodeC);
            System.out.println();
            clearVisitedFlags();
            System.out.println("DFSArray(以A为开始顶点):");
            dfsArrayTest.dfsUseStack(adjacency_matrix, nodeA);
        }
    }
    

    结果如下:

    DFSArray(以C为开始顶点):
    C A E D B 
    DFSArray(以A为开始顶点):
    A E D C B 
    

    邻接表 表示的图
    注意,邻接表表示 使用的是递归方法实现

    import java.util.ArrayList;
    import java.util.LinkedList;
    
    //深度优先:使用邻接表存储方式
    class DFSLinkedTest {
    
        static class Node {
            char data;//数据
            boolean visited;//是否访问过的标志
            LinkedList<Node> neighbours;//所有邻接点
    
            Node(char data) {
                this.data = data;
                this.neighbours = new LinkedList<>();
            }
        }
    
        //深度遍历,node为开始顶点
        private void dfsUtil(Node node) {
            node.visited = true;
            System.out.print(node.data + "  ");
            for (int i = 0; i < node.neighbours.size(); i++) {
                Node neighbourNode = node.neighbours.get(i);
                if (neighbourNode != null && !neighbourNode.visited) {
                    dfsUtil(neighbourNode);
                }
            }
        }
    	
        //恢复所有顶点标志到未访问
        private void clearVisitedFlags(ArrayList<Node> nodes) {
            for (int i = 0; i < nodes.size(); i++) {
                nodes.get(i).visited = false;
            }
        }
        
        public static void main(String args[]) {
            DFSLinkedTest dfsLinkedTest = new DFSLinkedTest();
            ArrayList<Node> nodes = new ArrayList<>();
            Node nodeA = new Node('A');
            Node nodeB = new Node('B');
            Node nodeC = new Node('C');
            Node nodeD = new Node('D');
            Node nodeE = new Node('E');
            nodeA.neighbours.add(nodeB);
            nodeA.neighbours.add(nodeE);
            nodeB.neighbours.add(nodeD);
            nodeC.neighbours.add(nodeA);
            nodeD.neighbours.add(nodeC);
            nodeE.neighbours.add(nodeD);
            
            nodes.add(nodeA);nodes.add(nodeB);nodes.add(nodeC);nodes.add(nodeD);nodes.add(nodeE);
            dfsLinkedTest.clearVisitedFlags(nodes);
            System.out.println("DFSLinked(以C为开始顶点):");
            dfsLinkedTest.dfsUtil(nodeC);
            System.out.println();
            dfsLinkedTest.clearVisitedFlags(nodes);
            System.out.println("DFSLinked(以A为开始顶点):");
            dfsLinkedTest.dfsUtil(nodeA);
        }
    }
    

    结果如下:

    DFSLinked(以C为开始顶点):
    C  A  B  D  E  
    DFSLinked(以A为开始顶点):
    A  B  D  C  E  
    

    从图中的某个顶点V0开始,访问V0,然后依次访问V0的所有未访问的邻接点,然后以访问这些顶点的顺序访问它们的邻接点,直到所有顶点在都被访问到。如果图中仍有未访问的顶点,则该图为非连通图,在图中未访问的顶点选择一个顶点为起点,并重复上述过程,直到访问完图中的所有顶点为止。
    同样 下面分别是使用邻接矩阵和邻接表表示的图 实现的广度优先的demo,可以大致看看了解下。
    邻接矩阵表示的图

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Queue;
    
    //广度优先:使用邻接矩阵存储方式
    public class BFSArrayTest {
    
        private Queue<Node> queue;
        static ArrayList<Node> nodes = new ArrayList<Node>();
        
        //顶点定义
        static class Node {
            char data;
            boolean visited;
            
            Node(char data) {
                this.data = data;
            }
        }
    
        public BFSArrayTest() {
            queue = new LinkedList<Node>();
        }
    
        //查找某顶点在邻接矩阵中的邻接点
        public ArrayList<Node> findNeighbours(int adjacency_matrix[][], Node x) {
            int nodeIndex = -1;
            
            ArrayList<Node> neighbours = new ArrayList<Node>();
            for (int i = 0; i < nodes.size(); i++) {
                if (nodes.get(i).equals(x)) {
                    nodeIndex = i;
                    break;
                }
            }
            
            if (nodeIndex != -1) {
                for (int j = 0; j < adjacency_matrix[nodeIndex].length; j++) {
                    if (adjacency_matrix[nodeIndex][j] == 1) {
                        neighbours.add(nodes.get(j));
                    }
                }
            }
            return neighbours;
        }
    
        public void bfsUtil(int adjacency_matrix[][], Node node) {
            queue.add(node);
            node.visited = true;
            while (!queue.isEmpty()) {
                Node element = queue.remove();
                System.out.print(element.data + " ");
                ArrayList<Node> neighbours = findNeighbours(adjacency_matrix, element);
                for (int i = 0; i < neighbours.size(); i++) {
                    Node n = neighbours.get(i);
                    if (n != null && !n.visited) {
                        queue.add(n);
                        n.visited = true;
                    }
                }
            }
        }
    	
        public void clearVisitedFlags() {
            for (int i = 0; i < nodes.size(); i++) {
                nodes.get(i).visited = false;
            }
        }
    
        public static void main(String arg[]) {
            Node nodeA = new Node('A');
            Node nodeB = new Node('B');
            Node nodeC = new Node('C');
            Node nodeD = new Node('D');
            Node nodeE = new Node('E');
            int adjacency_matrix[][] = { 
                    //A  B  C  D  E
                    { 0, 1, 0, 0, 1 }, // A
                    { 0, 0, 0, 1, 0 }, // B
                    { 1, 0, 0, 0, 0 }, // C
                    { 0, 0, 1, 0, 0 }, // D
                    { 0, 0, 0, 1, 0 }  // E
            };
    
            nodes.add(nodeA);nodes.add(nodeB);nodes.add(nodeC);nodes.add(nodeD);nodes.add(nodeE);
    		
            BFSArrayTest bfsArrayTest = new BFSArrayTest();
            System.out.println("BFSArray(以C为开始顶点):");
            bfsArrayTest.bfsUtil(adjacency_matrix, nodeC);
            System.out.println();
            bfsArrayTest.clearVisitedFlags();
            System.out.println("BFSArray(以A为开始顶点):");
            bfsArrayTest.bfsUtil(adjacency_matrix, nodeA);
        }
    }
    

    结果如下:

    BFSArray(以C为开始顶点):
    C A B E D 
    BFSArray(以A为开始顶点):
    A B E D C 
    

    邻接表表示的图

    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.Queue;
    
    //广度优先:使用邻接表存储方式
    public class BFSLinkedTest {
        private Queue<Node> queue;
        static ArrayList<Node> nodes = new ArrayList<Node>();
    
        static class Node {
            char data;
            boolean visited;
            LinkedList<Node> neighbours;
            
            Node(char data) {
                this.data = data;
                this.neighbours = new LinkedList<>();
            }
        }
    
        public BFSLinkedTest() {
            queue = new LinkedList<Node>();
        }
    
        public void bfsUtil(Node node) {
            queue.add(node);
            node.visited = true;
            while (!queue.isEmpty()) {
                Node element = queue.remove();
                System.out.print(element.data + "  ");
                for (int i = 0; i < element.neighbours.size(); i++) {
                    Node n = element.neighbours.get(i);
                    if (n != null && !n.visited) {
                        queue.add(n);
                        n.visited = true;
                    }
                }
            }
        }
    	
        private void clearVisitedFlags(ArrayList<Node> nodes) {
            for (int i = 0; i < nodes.size(); i++) {
                nodes.get(i).visited = false;
            }
        }
    
        public static void main(String arg[]) {
            ArrayList<Node> nodes = new ArrayList<>();
            Node nodeA = new Node('A');
            Node nodeB = new Node('B');
            Node nodeC = new Node('C');
            Node nodeD = new Node('D');
            Node nodeE = new Node('E');
            nodeA.neighbours.add(nodeB);
            nodeA.neighbours.add(nodeE);
            nodeB.neighbours.add(nodeD);
            nodeC.neighbours.add(nodeA);
            nodeD.neighbours.add(nodeC);
            nodeE.neighbours.add(nodeD);
    		
            BFSLinkedTest bfsLinkedTest = new BFSLinkedTest();
            nodes.add(nodeA);nodes.add(nodeB);nodes.add(nodeC);nodes.add(nodeD);nodes.add(nodeE);
            bfsLinkedTest.clearVisitedFlags(nodes);
            System.out.println("BFSLinked(以C为开始顶点):");
            bfsLinkedTest.bfsUtil(nodeC);
            System.out.println();
            bfsLinkedTest.clearVisitedFlags(nodes);
            System.out.println("BFSLinked(以A为开始顶点):");
            bfsLinkedTest.bfsUtil(nodeA);
        }
    }
    

    结果如下:

    BFSLinked(以C为开始顶点):
    C  A  B  E  D  
    BFSLinked(以A为开始顶点):
    A  B  E  D  C  
    
  • 相关阅读:
    Unity Animation扩展方法总结
    Unity 离线建造系统
    Unity 任意区域截屏创建Sprite
    Unity ugui拖动控件(地图模式与物件模式)
    Unity 极简UI框架
    Unity 芯片拼图算法
    Unity Procedural Level Generator 基础总结与功能优化
    MANIFEST.MF是个什么?
    外包程序员怎么办?
    文件上传transferTo一行代码的bug
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/13180568.html
Copyright © 2011-2022 走看看