图判断有无环

图论:对于无向图,m个定点,n条边,其中有k颗树。若无环,则肯定n>m,因为 n=k+m。若m>=n,则肯定有环。

广度优先搜索(Breadth-First-Search)和深度优先搜索(Deep-First-Search)是搜索策略中最经常用到的两种方法,特别常用于图的搜索.其中有很多的算法都用到了这两种思想,如:Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。

题目一:无向图,判断有无环。

算法:

(1)删除所有度为0或者1的顶点及相关的边,相关顶点度减一。

(2)将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复(1)。

如果超过m+n步,还存在环,则有环。因为m步+n步足以将所有的顶点和边删除!

题目二:有向图,BFS/DFS以及拓扑排序法。

(1)关于BFS和DFS,可以看这个网址,有详细的使用例子http://blog.youkuaiyun.com/yousheng324/article/details/6767726

BFS的思想:
从一个图的某一个顶点V0出发,首先访问和V0相邻的且未被访问过的顶点V1、V2、……Vn,然后依次访问与V1、V2……Vn相邻且未被访问的顶点。如此继续,找到所要找的顶点或者遍历完整个图。
由此可以看出,用BFS进行搜索所搜索的顶点都是按深度进行扩展的,先找到到V0距离为1的所有顶点,然后找到距离V0为2的顶点……所以BFS所搜索到的都是最短的路径。
由于要将距离V0为d(d>0)的且未被方位的点都记录起来,我们采用队列这种数据结构。队列的特点是先进先出(FIFO),从某个顶点出发,记此顶点已访问标记,然后依次搜索和此顶点相邻的且未被访问的顶点,将其加入队列,并置已访问标记,重复此步骤,直到找到需要搜索的顶点或者所有的顶点都被访问为止。

DFS的思想:
顾名思义,深度优先搜索所遵循的策略就是尽可能“深”的在图中进行搜索,对于图中某一个顶点V,如果它还有相邻的顶点(在有向图中就是还有以V为起点的边)且未被访问,则访问此顶点。如果找不到,则返回到上一个顶点。这一过程一直进行直到所有的顶点都被访问为止。 DFS可以搜索出从某一个顶点到另外的一个顶点的所有路径。 由于要进行返回的操作,我们采用的是递归的方法。

(2)关于拓扑排序:以下内容转自:http://blog.chinaunix.net/uid-20384806-id-1954187.html

对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前。
     通常,这样的线性序列称为满足拓扑次序(TopoiSicai Order)的序列,简称拓扑序列
  注意:
     ①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。
     ②若图中存在有向环,则不可能使顶点满足拓扑次序。
     ③一个DAG的拓扑序列通常表示某种方案切实可行。

下面是Java实现:

package zieckey.datastructure.study.graph;

/**
 * 有方向图
 * 
 * @author zieckey
 */

public class DirectedGraph
{
    private final int    MAX_VERTS    = 20;
    private Vertex[]    vertexList;        // array of vertices

    private int[][]        adjMat;            // adjacency matrix

    private int            nVerts;            // current number of vertices

    private char[]        sortedArray;        // sorted vert labels


    /**
     * 默认构造函数 初始化邻接矩阵
     */

    public DirectedGraph( )
    {
        vertexList = new Vertex[MAX_VERTS];
        sortedArray = new char[MAX_VERTS];
        // 图的邻接矩阵adjacency matrix

        adjMat = new int[MAX_VERTS][MAX_VERTS];
        nVerts = 0;
        for ( int j = 0; j < MAX_VERTS; j++ )
        {
            // set adjacency

            for ( int k = 0; k < MAX_VERTS; k++ )
            {
                adjMat[j][k] = 0;
            }
        }
    }
    
    /**
     * 对有向图进行拓扑排序
     * 
     * 无后继的顶点优先拓扑排序方法
     * 1、思想方法
     * 该方法的每一步均是输出当前无后继(即出度为0)的顶点。
     * 对于一个DAG,按此方法输出的序列是逆拓扑次序。
     * 因此设置一个栈(或向量)T来保存输出的顶点序列,即可得到拓扑序列。
     * 若T是栈,则每当输出顶点时,只需做人栈操作,排序完成时将栈中顶点依次出栈即可得拓扑序列。
     * 若T是向量,则将输出的顶点从T[n-1]开始依次从后往前存放,即可保证T中存储的顶点是拓扑序列。
     */

    public void nonSuccFirstToplogicalSort()
    {
        int orig_nVerts = nVerts;
        while(nVerts>0)
        {
            int delVertex = getNoSuccessorVertex();
            if ( -== delVertex )
            {
                System.out.println("Error: This graph has cycles! It cannot be toplogical sorted.");
                return;
            }
            
            sortedArray[nVerts-1] = vertexList[delVertex].label;
            deleteVertex( delVertex );
        }
        
        System.out.print("Topologically sorted order: ");
        for(int j=0; j<orig_nVerts; j++)
        {
            System.out.print( sortedArray[j] );
        }
        System.out.println("");
    }
    
    /**
     * 找到没有后继节点的节点
     * @return
     */

    public int getNoSuccessorVertex()
    {
        boolean isEdge; 
        for ( int row = 0; row < nVerts; row++ )
        {
            isEdge = false;
            for ( int column = 0; column < nVerts; column++ )
            {
                if ( adjMat[row][column]>)//大于0,表明有边指向其他点,说明有后继节点

                {    
                    isEdge = true;
                    break;                    //OK,继续下一个节点

                }
            }
            
            if ( false == isEdge )//没有边,表明没有后继节点

            {
                return row;
            }
        }
        return -1;
    }
    
    /**
     * 删除一个节点
     * @param delVertex
     */

    public void deleteVertex( int delVertex )
    {
        if ( delVertex != nVerts-)//如果不是最后一个节点

        {
            //在节点列表中删除这个节点,所有后面的节点前移一格

            for ( int i = delVertex; i < nVerts-1; i++ )
            {
                vertexList[i] = vertexList[i+1];
            }
            
            // 删除邻接矩阵的delVertex行和列,即所有后面的行和列都向前移动一格

            // 所有delVertex行后的行,向上一个格

            for ( int row = delVertex; row < nVerts - 1; row++ )
            { 
                for ( int column = 0; column < nVerts; column++ )
                {
                    adjMat[row][column] = adjMat[row + 1][column];
                }
            }
            
            // 所有delVertex列后的列,向左一个格

            for ( int column = delVertex; column < nVerts; column++ )
            { 
                for ( int row = 0; row < nVerts - 1; row++ )                
                {
                    adjMat[row][column] = adjMat[row][column+1];
                }
            }
        }
        
        nVerts--;//减少一个节点

    }
    
    /**
     * 添加一个节点
     * 
     * @param lab
     */

    public void addVertex( char lab ) // argument is label

    {
        vertexList[nVerts++ ] = new Vertex( lab );
    }

    /**
     * 添加一条边
     * 
     * @param start
     * 起始点
     * @param end
     * 终点
     */

    public void addEdge( int start, int end )
    {
        adjMat[start][end] = 1;
    }

    /**
     * 添加一条边
     * 
     * @param start
     * 起始点
     * @param end
     * 终点
     */

    public void addEdge( char startVertex, char endVertex )
    {
        int start = getIndexByLabel( startVertex );
        int end = getIndexByLabel( endVertex );
        if ( -!= start && -!= end )
        {
            adjMat[start][end] = 1;
        }
    }
    
    /**
     * 显示一个节点
     * 
     * @param v
     */

    public void displayVertex( int v )
    {
        System.out.print( vertexList[v].label );
    }

    public int getIndexByLabel( char lable )
    {
        for ( int i = 0; i < vertexList.length; i++ )
        {
            if ( lable == vertexList[i].label )
            {
                return i;
            }
        }

        // 没有这个节点

        return -1;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值