图图的存储图的遍历

Posted siyyawu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了图图的存储图的遍历相关的知识,希望对你有一定的参考价值。

图(Graph)是由顶点的有穷非空集合和顶点之间的边组成。G(V,E) V表示顶点的集合,E表示边的集合。

在无向图中,边可以表示为E1={(A,D),(B,C)}

在有向图中,顶点v1和v2的有向边称为。表示为<v1,v2> v1称为弧尾,v2称为弧顶。

在无向图中,如果任意边两个顶点都存在边,则该图为无向完全图,n个顶点的无向完全图有n*(n-1)/2条边。

在有向图中,如果任意边两个顶点都存在互为相反边,则该图为有向完全图,n个顶点的有向完全图有n*(n-1)条边。

稀疏图,稠密图。

带权图称为网。

子图如下。

技术分享图片

无向图的度指的是顶点关联的边的数量,容易得出总边数=度的总数/2

有向图分为出度和入度,以顶点为弧尾的边的数量为出度,以顶点为弧顶的边的数量称为入度。

第一个顶点到最后一个顶点相同的路径称为回路或环 (Cycle)。 序列中顶点不重 复出现的路径称为简单路径. 除了第一个顶点和最后一个顶点之外,其余顶点不重复 出现的回路,称为简单回路或简单环。

技术分享图片

 

任意顶点都是连通的称为连通图。

技术分享图片

无向图中的极大连通子图称为连通分量。

技术分享图片

有向图中,所有顶点都存在路径称为强连通图,左图不是强连通图,右图是强连通图,同时右图是左图的极大强连通子图。

技术分享图片

连通图的生成树是极小的的连通子图

 技术分享图片



 

图通常有五种存储方式,邻接矩阵、邻接表、十字链表、邻接多重表、边集数组。

 邻接矩阵用一个一维数组来表示顶点,用一个二维数组来表示边。

对于无向图来说,矩阵是一个对称矩阵。

技术分享图片

对于有向图,可以把边表中没有弧的地方设为-1。

技术分享图片


 

邻接表是对邻接矩阵的改进,由于稀疏图中边很少,所有二维数组中很多地方都为0。我们可以用链表存储顶点信息,同时保存一个指向边表的结点。

同一个顶点指向的单链表不分先后顺序。

技术分享图片

有向图 的邻接表(结点不分先后)

技术分享图片

带权有向图邻接表的实现:

package GraphAdjList;

import java.util.ArrayList;
import java.util.List;

//带权有向图邻接表实现(出度表)
public class GraphAdjList<T>{

    public List<VertexNode<T>> list;
    
    public GraphAdjList(T[] datas)
    {
        list=new ArrayList<VertexNode<T>>();
        System.out.println("初始化顶点表");
        for(int i=0;i<datas.length;i++)
        {
            VertexNode<T> node=new VertexNode<T>();
            node.data=datas[i];
            list.add(node);
        }
    }
    
    
    //增加顶点
    public void insertVertexNode(T x)
    {
        VertexNode<T> node=new VertexNode();
        node.data=x;
        System.out.println("新增顶点");
        list.add(node);
    }
    
    //遍历顶点表
    public void traversalByVertexNode()
    {
        for(VertexNode<T> v:list)
        {
            System.out.println(v.data);
        }
    }
    
    //获取坐标index的顶点
    public VertexNode<T> getVertexNode(int index)
    {
        return list.get(index);
    }
    /**
     * 新增弧
     * @param x 弧头坐标
     * @param ajavex 弧尾坐标
     * @param weight 权值
     */
    public void addEdge(int x,int ajavex,int weight)
    {
        EdgeNode temp=getVertexNode(x).next;
        while(temp!=null)
        {
            temp=temp.next;
        }
        EdgeNode node=new EdgeNode();
        node.ajavex=ajavex;
        node.weight=weight;
        temp.next=node;
    }
}
//顶点表
class VertexNode<T>
{
    T data;
    EdgeNode next;//指向邻接结点的头指针
}

//边表
class EdgeNode
{
    int ajavex;//邻接点坐标
    int weight;//权值
    EdgeNode next;//邻接点指针
}

测试:

package GraphAdjList;

import java.util.List;

public class App {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String[] str={"V1","V2","V3","V4"};
        GraphAdjList<String> g=new GraphAdjList<String>(str);
        g.traversalByVertexNode();
        g.insertVertexNode("V5");
        g.traversalByVertexNode();
    }

}

结果:

初始化顶点表
V1
V2
V3
V4
新增顶点
V1
V2
V3
V4
V5


十字链表将出度表和入度表结合起来。

顶点表

技术分享图片

边表:

技术分享图片

技术分享图片



 

多重邻接表:无向图的优化

技术分享图片



深度遍历和广度遍历:

深度遍历类似树的先序遍历,始终选取靠左/靠右的顶点,如果某个结点连接的结点都被访问后则原路返回。用递归来实现

广度遍历则类似树的层序遍历,将每次出队的顶点的相互连接的顶点入队,用队列来来实现

技术分享图片

带权无向图邻接矩阵实现,深度遍历、广度遍历:

技术分享图片

 

package AMWGraph;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

//带权无向图邻接矩阵实现,深度遍历、广度遍历
public class AMWGraph {

    public List<Object> vertexList;//结点链表
    public int[][] edges;//邻接矩阵
    public int numOfEdges;//边的数目
    public boolean[] isVisited;
    public int count=0;
    public AMWGraph(int n,String[] v)
    {
        isVisited=new boolean[n];
        vertexList=new ArrayList<>();
        edges=new int[n][n];
        for(int i=0;i<n;i++)
        {
            vertexList.add(v[i]);
            isVisited[i]=false;
        }
        numOfEdges=0;
    }
    
    //得到结点数目
    public int getNumOfVertex()
    {
        return vertexList.size();
    }
    
    //得到边的数目
    public int getNumOfEdges()
    {
        return numOfEdges;
    }
    
    
    //得到结点i的数据
    public Object getValueByIndex(int i)
    {
        return vertexList.get(i);
    }
    
    //返回v1,v2的权值
    public int getWeight(int v1,int v2)
    {
        return edges[v1][v2];
    }
    
    //插入结点
    public void insertVertex(Object vertex)
    {
        vertexList.add(vertex);
        System.out.println("增加结点");
        int[][] temp=edges;
        int lengside;
        lengside=getNumOfVertex(); //顶点数量
        edges=new int[lengside][lengside];
        for(int i=0;i<temp.length;i++){
            for(int j=0;j<temp[i].length;j++)
            {
                edges[i][j]=temp[i][j];
            }
        }
        boolean[] isVisitedTemp=isVisited;
        isVisited=new boolean[lengside];
        for(int i=0;i<isVisitedTemp.length;i++)
        {
            isVisited[i]=isVisitedTemp[i];
        }
        isVisited[lengside-1]=false;
     }
    
    //插入边
    public void insertEdge(int v1,int v2,int weight)
    {
        edges[v1][v2]=weight;
        edges[v2][v1]=weight;
        numOfEdges++;
    }
    
    //删除边
    public void deleteEdge(int v1,int v2)
    {
        edges[v1][v2]=0;
        numOfEdges--;
    }
    
    
    //根据一个顶点的下标,返回该顶点的第一个邻接结点的下标
    public int getFirstNeighbor(int index)
    {
        for(int j=0;j<vertexList.size();j++)
        {
            if(edges[index][j]>0)
            {
                return j;
            }
        }
        return -1;
    }
    
    //根据一个邻接结点的下标来取得下一个邻接结点的下标
    public int getNextNeighbor(int v1,int v2)
    {
        for(int j=v2+1;j<vertexList.size();j++)
        {
            if(edges[v1][j]>0)
            {
                return j;
            }
        }
        return -1;
    }
    
    //深度遍历
    public void depthFirstSearch(boolean[] isVisited,int i)
    {
        System.out.println(getValueByIndex(i));
        isVisited[i]=true;
        int w=getFirstNeighbor(i);
        while(w!=-1)
        {
            if(!isVisited[w])
            {
                depthFirstSearch(isVisited,w);
            }
            w=getNextNeighbor(i, w);
        }
    }
    
    //深度优先遍历
    public void depthFirstSearch()
    {
        for(int i=0;i<getNumOfVertex();i++)   //非连通图需要选择多个结点,本结构为连通图
        {
            if(!isVisited[i])
            {
                count++;
                depthFirstSearch(isVisited,i);
            }
        }
    }
    
    //广度优先遍历
    private void broadFirstSearch(boolean[] isVisited,int i)
    {
        int u,w;
        LinkedList<Integer>queue=new LinkedList();
        System.out.println(getValueByIndex(i)+" ");
        isVisited[i]=true;
        
        queue.addLast(i);//坐标进队列
        while(!queue.isEmpty())
        {
            
            u=((Integer)queue.removeFirst());
            w=getFirstNeighbor(i);
            while(w!=-1)
            {
                if(!isVisited[w])
                {
                    System.out.println(getValueByIndex(w)+" ");
                    isVisited[w]=true;
                    queue.addLast(w);
                }
                
                    w=getNextNeighbor(i, w);
            }
        }
    }
    
    //广度遍历
    public void broadFirstSearch()
    {
        for(int i=0;i<getNumOfVertex();i++)
        {
            if(!isVisited[i])
            {
                count++;
                broadFirstSearch(isVisited,i);
            }
        }
    }
    
}

测试:

package AMWGraph;

public class App {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String[] labels={"A","B","C","D","E","F","G","H","I"};
        AMWGraph graph=new AMWGraph(labels.length,labels);
        graph.insertEdge(0, 1, 1);
        graph.insertEdge(0, 5, 1);
        graph.insertEdge(1, 2, 1);
        graph.insertEdge(1, 8, 1);
        graph.insertEdge(1, 6, 1);
        graph.insertEdge(5, 6, 1);
        graph.insertEdge(2, 3, 1);
        graph.insertEdge(3, 4, 1);
        graph.insertEdge(4, 5, 1);
        graph.insertEdge(6, 7, 1);
        graph.insertEdge(3, 7, 1);
        graph.insertEdge(3, 6, 1);
        System.out.println("结点个数:"+graph.getNumOfVertex());
        System.out.println("边个数:"+graph.getNumOfEdges());
//        graph.depthFirstSearch();                
//        System.out.println(graph.count);
        graph.broadFirstSearch();
        System.out.println(graph.count);
    }

}

结果:

DFS
结点个数:9
边个数:12
A
B
C
D
E
F
G
H
I
重新选择结点次数:1

BFS
结点个数:9
边个数:12
A 
B 
F 
C 
D 
E 
G 
H 
I 
重新选择结点次数:5

 

以上是关于图图的存储图的遍历的主要内容,如果未能解决你的问题,请参考以下文章

数据结构-图图的常用算法

DS06--图

js实现图的遍历之广度优先搜索

2018.10.22图图的游戏 / 图图的设计 / 图图的旅行

无向图的深度遍历

《大话数据结构》笔记(7-3)--图:图的遍历