.net 数据结构与算法基础:图的操作

来源:互联网 发布:bp算法推导 编辑:程序博客网 时间:2024/05/22 13:30

定义


图是由结点的有穷集合V和边的集合E组成。其中,为了与树形结构加以区别,在图结构中常常将结点称为顶点,边是顶点的有序偶对,若两个顶点之间存在一条边,就表示这两个顶点具有相邻关系。
在有向图中,通常将边称作弧,含箭头的一端称为弧头,另一端称为弧尾,记作<vi,vj>,它表示从顶点vi到顶点vj有一条边。
若有向图中有n个顶点,则最多有n(n-1)条弧,我们又将具有n(n-1)条弧的有向图称作有向完全图。以顶点v为弧尾的弧的数目称作顶点v的出度,以顶点v为弧头的弧的数目称作顶点v的入度。在无向图中,边记作(vi,vj),它蕴涵着存在< vi,vj>和<vj,vi>两条弧。若无向图中有n个顶点,则最多有n(n-1)/2条弧,我们又将具有n(n-1)/2条弧的无向图称作无向完全图。与顶点v相关的边的条数称作顶点v的度。
路径长度是指路径上边或弧的数目。
若第一个顶点和最后一个顶点相同,则这条路径是一条回路。
若路径中顶点没有重复出现,则称这条路径为简单路径。
在无向图中,如果从顶点vi到顶点vj有路径,则称vi和vj连通。如果图中任意两个顶点之间都连通,则称该图为连通图,否则,将其中的极大连通子图称为连通分量。
在有向图中,如果对于每一对顶点vi和vj,从vi到vj和从vj到vi都有路径,则称该图为强连通图;否则,将其中的极大连通子图称为强连通分量。

C#图


节点定义


    public class Vertex
    {
        public bool wasVisted;//确定当前节点是否被访问
        public string label;//节点名
        public Vertex(string label)
        {
            this.label = label;
            this.wasVisted = false;
        }
    }


边定义


   public class Graph
    {
        int numVertex;
        int maxVertex;
        Vertex[] vertexs;//存储节点
        int[,] adjMatrix;//存储边
        public Graph(int maxvertex1)
        {
            this.maxVertex = maxvertex1;
            vertexs=new Vertex[maxVertex];
            adjMatrix=new int[maxVertex,maxVertex];
            numVertex = 0;
            for (int i = 0; i < maxVertex; i++)
            {
                for (int j = 0; j < maxVertex; j++)
                {
                    adjMatrix[i, j] = 0;
                }
            }
        }

        public void AddVertex(string label1)//插入节点
        {
            vertexs[numVertex++] = new Vertex(label1);
        }

        public void AddEdge(int start, int end)//插入边
        {
            adjMatrix[start, end] = 1;
        }

        public void ShowVertex(int v)//显示节点
        {
            Console.Write( vertexs[v].label + " ");
        }
    }


拓扑排序


定义


对一个有向无环图(Directed Acyclic Graph简称DAG)G进行拓扑排序,是将G中所有顶点排成一个线性序列,使得图中任意一对顶点u和v,若<u,v> ∈E(G),则u在线性序列中出现在v之前。通常,这样的线性序列称为满足拓扑次序(Topological Order)的序列,简称拓扑序列。简单的说,由某个集合上的一个偏序得到该集合上的一个全序,这个操作称之为拓扑排序。离散数学中关于偏序和全序的定义:
若集合X上的关系是R是自反的、反对称的和传递的,则称R是集合X上的偏序关系。
设R是集合X上的偏序(Partial Order),如果对每个x,y属于X必有xRy 或 yRx,则称R是集合X上的全序关系。
注意:
①若将图中顶点按拓扑次序排成一行,则图中所有的有向边均是从左指向右的。
②若图中存在有向环,则不可能使顶点满足拓扑次序。
③一个DAG的拓扑序列通常表示某种方案切实可行。

过程


(1)从有向图中选择一个没有前驱(即入度为0)的顶点并且输出它.
(2)从网中删去该顶点,并且删去从该顶点发出的全部有向边.
(3)重复上述两步,直到剩余的网中不再存在没有前趋的顶点为止.

步骤演示




1)图中 算法、操作系统、汇编语言没有后继节点,选择一个删除。
2)若删除 算法或操作系统,则数据结构、汇编语言成为没有后继节点的待删节点。
3)重复以上步骤,直到网中不存在没有前驱节点为止。

程序


    public class Graph
    {
        int numVertex;
        int maxVertex;
        Vertex[] vertexs;
        int[,] adjMatrix;
        public Graph(int maxvertex1)
        {
            this.maxVertex = maxvertex1;
            vertexs=new Vertex[maxVertex];
            adjMatrix=new int[maxVertex,maxVertex];
            numVertex = 0;
            for (int i = 0; i < maxVertex; i++)
            {
                for (int j = 0; j < maxVertex; j++)
                {
                    adjMatrix[i, j] = 0;
                }
            }
        }


        public void AddVertex(string label1)
        {
            vertexs[numVertex++] = new Vertex(label1);
        }


        public void AddEdge(int start, int end)
        {
            adjMatrix[start, end] = 1;
        }


        public void ShowVertex(int v)
        {
            Console.Write( vertexs[v].label + " ");
        }


        public int NoSuccessors()
        {
            bool isEdge  ;
            for (int i = 0; i < maxVertex; i++)
            {
                isEdge = false;
                for (int j = 0; j < maxVertex; j++)
                {
                    if (adjMatrix[i, j] > 0)
                    {
                        isEdge = true;
                        break;
                    }
                }
                if (!isEdge)
                {
                    return i;
                }
            }
            return -1;
        }


        public void Delete(int index)
        {
            if (index != maxVertex - 1)
            {
                for (int i = index; i < maxVertex - 1; i++)
                {
                    vertexs[i] = vertexs[i + 1];
                }
                for (int i = index; i < maxVertex-1; i++)
                {
                    RemoveRow(i);
                }
                for (int i = index; i < maxVertex - 1; i++)
                {
                    RemoveCol(i);
                }
            }
            maxVertex--;
        }


        private void RemoveCol(int index)
        {
            for (int i = 0; i < maxVertex; i++)
            {
                adjMatrix[i,index]=adjMatrix[i,index+1];
            }
        }


        private void RemoveRow(int index)
        {
            for (int i = 0; i < maxVertex; i++)
            {
                adjMatrix[index,i]=adjMatrix[index+1,i];
            }
        }


        public void TopVertList()
        {
            Stack<string> stack = new Stack<string>();
            while (maxVertex > 0)
            {
                int index=NoSuccessors();
                if (index == -1)
                {
                    Console.WriteLine("节点包含环");
                    return;
                }
                stack.Push(vertexs[index].label);
                Delete(index);
            }
            Console.WriteLine("拓扑排序结果:");
            while (stack.Count > 0)
            {
                Console.Write( stack.Pop()+ "  ");
            }
        }
    }

深度优先搜索


定义


深度优先搜索是一种在开发爬虫早期使用较多的方法。它的目的是要达到被搜索结构的叶结点(即那些不包含任何超链HTML文件) 。在一个HTML文件中,当一个超链被选择后,被链接的HTML文件将执行深度优先搜索,即在搜索其余的超链结果之前必须先完整地搜索单独的一条链。深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,说明搜索已经结束。
举例说明之:下图是一个无向图,如果我们从A点发起深度优先搜索(以下的访问次序并不是唯一的,第二个点既可以是B也可以是C,D),则我们可能得到如下的一个访问过程:A->B->E(没有路了!回溯到A)->C->F->H->G->D(没有路,最终回溯到A,A也没有未访问的相邻节点,本次搜索结束)


过程


深度优先遍历图的方法是,从图中某顶点v出发:
(1)访问顶点v;
(2)依次从v的未被访问的邻接点出发,对图进行深度优先遍历;直至图中和v有路径相通的顶点都被访问;
(3)若此时图中尚有顶点未被访问,则从一个未被访问的顶点出发,重新进行深度优先遍历,直到图中所有顶点均被访问过为止。 当然,当人们刚刚掌握深度优先搜索的时候常常用它来走迷宫.事实上我们还有别的方法,那就是广度优先搜索(BFS).状态(state):状态是制问题求解过程中每一步的状况。

程序


        public int GetAdjUnvistedVertex(int index)
        {
            for (int i = 0; i < maxVertex; i++)
            {
                if (vertexs[i].wasVisted == false && adjMatrix[index, i] == 1)
                {
                    return i;
                }
            }
            return -1;
        }


        public void DepthFirstSearch()
        {
            Stack<int> stack = new Stack<int>();
            vertexs[0].wasVisted = true;
            ShowVertex(0);
            stack.Push(0);
            while(stack.Count>0)
            {
                int ver = GetAdjUnvistedVertex(stack.Peek());
                if (ver == -1)
                {
                    stack.Pop();
                }
                else
                {
                    stack.Push(ver);
                    ShowVertex(ver);
                    vertexs[ver].wasVisted = true;
                }
            }
        }


广度优先搜索


定义


宽度优先搜索算法(又称广度优先搜索)是最简便的图的搜索算法之一,这一算法也是很多重要的图的算法的原型。Dijkstra单源最短路径算法和Prim最小生成树算法都采用了和宽度优先搜索类似的思想。其别名又叫BFS,属于一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。换句话说,它并不考虑结果的可能位址,彻底地搜索整张图,直到找到结果为止。

算法思想


1)从图中的某个顶点V出发,访问之;并将其访问标志置为已被访问,即visited[i]=1

2)依次访问顶点V的各个未被访问过的邻接 点,将V的全部邻接点都访问到;

3分别从这些邻接点出发,依次访问它们的未被访问过的邻接点,并使“先被访问的顶 点的邻接点”先于“后被访问的顶点的邻接点”被访问,直到图中所有已被访问过的顶 点的邻接点都被访问到。

(4)依此类推,直到图中所有顶点都被访问完为止 



过程




按照广度优先算法,其遍历顺序为:V1--V2--V3--V4--V5--V6--V7--V8

程序


       public void BreadFirthSearch()
        {
            Queue<int> queue = new Queue<int>();
            ShowVertex(0);
            vertexs[0].wasVisted = true;
            queue.Enqueue(0);
            while (queue.Count > 0)
            {
                int ver1 = queue.Dequeue();
                int ver2 = GetAdjUnvistedVertex(ver1);
                while (ver2 != -1)
                {
                    vertexs[ver2].wasVisted = true;
                    ShowVertex(ver2);
                    queue.Enqueue(ver2);
                    ver2 = GetAdjUnvistedVertex(ver1);
                }
            }
            for (int i = 0; i < maxVertex; i++)
            {
                vertexs[i].wasVisted = false;
            }
        }


最小生成树


定义


在一个具有几个顶点的连通图G中,如果存在子图G'包含G中所有顶点和一部分边,且不形成回路,则称G'为图G的生成树,代价最小生成树则称为最小生成树。
许多应用问题都是一个求无向连通图的最小生成树问题。例如:要在n个城市之间铺设光缆,主要目标是要使这 n 个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同;另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。

程序


       public void MstTree()
        {
            Queue<int> queue = new Queue<int>();
            queue.Enqueue(0);
            vertexs[0].wasVisted=true;
            int ver1,ver2;
            while (queue.Count > 0)
            {
                ver1 = queue.Dequeue();
                ver2 = GetAdjUnvistedVertex(ver1);
                while (ver2 != -1)
                {
                    vertexs[ver2].wasVisted = true;
                    Showvertexs(ver1,ver2);
                    queue.Enqueue(ver2);
                    ver2 = GetAdjUnvistedVertex(ver1);
                }
            }
            for (int i = 0; i < maxVertex; i++)
            {
                vertexs[i].wasVisted = false;
            }
        }

程序运行结果

        
程序下载:http://download.csdn.net/detail/xiang__jiangsu/4820893