C#实现图(Graph)

简介

    图表示点之间的关系,在C#中通过节点对象的集合来表示点(Vertex),用邻接矩阵(adjacency matrix)来表示点之间的关系。下面来看C#实现。

    PS:本片文章是我复习的笔记,代码注释很全。勿吐槽。

 

表示点的对象

    下面实现代码:

    class Vertex
    {
        public string Data;
        public bool IsVisited;
        public Vertex(string Vertexdata)
        {
            Data = Vertexdata;
        }
    }


   

    每个节点包含两个字段,分别为节点数据以及表示是否被访问过的一个布尔类型。

 

表示图的对象

    图中除了需要点的集合和邻接矩阵之外,还需要一些基本的向图中添加或删除元素的方法,以及一个构造方法来对图进行初始化。

 public class Graph
    {
        //图中所能包含的点上限
        private const int Number = 10;
        //顶点数组
        private Vertex[] vertiexes;
        //邻接矩阵
        public int[,] adjmatrix;
        //统计当前图中有几个点
        int numVerts = 0;
        //初始化图
        public Graph()
        {
            //初始化邻接矩阵和顶点数组
            adjmatrix = new Int32[Number, Number];
            vertiexes = new Vertex[Number];
            //将代表邻接矩阵的表全初始化为0
            for (int i = 0; i < Number; i++)
            {
                for (int j = 0; j < Number; j++)
                {
                    adjmatrix[i, j] = 0;
                }
            }
        }

        //向图中添加节点
        public void AddVertex(String v)
        {
            vertiexes[numVerts] = new Vertex(v);
            numVerts++;
        }
        //向图中添加有向边
        public void AddEdge(int vertex1, int vertex2)
        {
            adjmatrix[vertex1, vertex2] = 1;
            //adjmatrix[vertex2, vertex1] = 1;
        }
        //显示点
        public void DisplayVert(int vertexPosition)
        {
            Console.WriteLine(vertiexes[vertexPosition]+" ");
        }
}

 

拓扑排序(TopSort)

    拓扑排序是对一个有向的,并且不是环路的图中所有的顶点线性化。需要如下几个步骤

    1.首先找到没有后继的节点。

    2.将这个节点加入线性栈中

    3.在图中删除这个节点

    4.重复步骤1,2,3

 

   因此,首先需要找到后继节点的方法:

        //寻找图中没有后继节点的点
        //具体表现为邻接矩阵中某一列全为0
        //此时返回行号,如果找不到返回-1
        private int FindNoSuccessor()
        {
            bool isEdge;
            //循环行
            for (int i = 0; i < numVerts; i++)
            {
                isEdge = false;
                //循环列,有一个1就跳出循环
                for (int j = 0; j < numVerts; j++)
                {
                    if (adjmatrix[i, j] == 1)
                    {
                        isEdge = true;
                        break;
                    }
                }
                if (!isEdge)
                {
                    return i;
                }
            }
            return -1;

        }

 

    此外还需要删除图中点的方法,这个方法不仅需要删除图中对应位置的点,还需要删除邻接矩阵对应的行和列,因此设置了两个辅助方法,见代码。

//删除图中的点
        //需要两个操作,分别从数组中删除点
        //从邻接矩阵中消去被删点的行和列
        private void DelVertex(int vert)
        {
            //保证不越界
            if (vert <= numVerts - 1)
            {
                //删除节点
                for (int i = vert; i < numVerts; i++)
                {
                    vertiexes[i] = vertiexes[i + 1];
                }
                //删除邻接矩阵的行
                for (int j = vert; j < numVerts; j++)
                {
                    MoveRow(j, numVerts);
                }
                //删除邻接矩阵中的列,因为已经删了行,所以少一列
                for (int k = vert; k < numVerts - 1;k++ )
                {
                    MoveCol(k, numVerts-1);
                }
                //删除后减少一个
                numVerts--;
            }
        }
        //辅助方法,移动邻接矩阵中的行
        private void MoveRow(int row, int length)
        {
            for (int col = row; col < numVerts; col++)
            {
                adjmatrix[row, col] = adjmatrix[row + 1, col];
            }
        }
        //辅助方法,移动邻接矩阵中的列
        private void MoveCol(int col, int length)
        {
            for (int row = col; row < numVerts; row++)
            {
                adjmatrix[row, col] = adjmatrix[row, col+1];
            }
        }

 

    有了这几个方法,就可以按照步骤开始拓扑排序了:

       //拓扑排序
        //找到没有后继节点的节点,删除,加入一个栈,然后输出
        public void TopSort()
        {
            int origVerts = numVerts;
            //存放返回节点的栈
            System.Collections.Stack result = new Stack();
            while (numVerts > 0)
            {
                //找到第一个没有后继节点的节点
                int currVertex = FindNoSuccessor();
                if (currVertex == -1)
                {
                    Console.WriteLine("图为环路图,不能搞拓扑排序");
                    return;
                }
               //如果找到,将其加入返回结果栈
                result.Push(vertiexes[currVertex].Data);
              //然后删除此节点
                DelVertex(currVertex);
            }
            /*输出排序后的结果*/
            Console.Write("拓扑排序的顺序为:");
            while (result.Count > 0)
            {
                Console.Write(result.Pop()+" ");
            }
            /*输出排序后的结果*/
        }

 

 

    下面,对拓扑排序进行测试,代码如下:

   

 static void Main(string[] args)
        {
            Graph g = new Graph();
            g.AddVertex("A");
            g.AddVertex("B");
            g.AddVertex("C");
            g.AddVertex("D");
            g.AddEdge(0, 1);
            g.AddEdge(1, 2);
            g.AddEdge(2, 3);
            g.AddEdge(3, 4);
            g.TopSort();
            Console.ReadKey();


        }


    测试结果:

    1

 

图的遍历

    很多时候,我们需要知道从图中给定点到另一个点是否能走通,比如几个车站之间是否可以连接。这时我们需要对图进行查找,查找大概可以分为两类,深度优先遍历和广度优先遍历,下面先来看深度优先遍历。

 

深度优先遍历(Depth-First Search)

    深度优先遍历首先从一个点开始,到一条路径结束,然后循环找第二条路径,到结束,依此往复。

    首先我们需要一个辅助方法返回给定的点最近一个连接并且未被访问过的序号。

        //从邻接矩阵查找给定点第一个相邻且未被访问过的点
        //参数v是给定点在邻接矩阵的行
        private int GetAdjUnvisitedVertex(int v)
        {
            for (int j = 0; j < numVerts; j++)
            {
                if (adjmatrix[v,j]==1 && vertiexes[j].IsVisited == false)
                {
                    return j;
                }
            }
            return -1;
        }

 

    下面,就可以进行深度优先遍历了:

//深度优先遍历
        public void DepthFirstSearch()
        {
            //声明一个存储临时结果的栈
            System.Collections.Stack s = new Stack();
            //先访问第一个节点
            vertiexes[0].IsVisited = true;
            DisplayVert(0);
            s.Push(0);
            int v;

            while (s.Count > 0)
            {
                //获得和当前节点连接的未访问过节点的序号
                v = GetAdjUnvisitedVertex((int)s.Peek());
                if (v == -1)
                {
                    s.Pop();
                }
                else
                {
                    //标记为已经被访问过
                    vertiexes[v].IsVisited = true;
                    DisplayVert(v);
                    s.Push(v);
                }
            }
            //重置所有节点为未访问过
            for (int u = 0; u < numVerts; u++)
            {
                vertiexes[u].IsVisited = false;
            }
            

        }

 

广度优先遍历(Breadth-First Search)

    广度优先遍历首先遍历层级。算法如下:

   

//广度优先遍历
        public void BreadthFirstSearch()
        {
            System.Collections.Queue q = new Queue();
            /*首先访问第一个节点*/
            vertiexes[0].IsVisited = true;
            DisplayVert(0);
            q.Enqueue(0);
            /*第一个节点访问结束*/

            int vert1, vert2;
            while (q.Count > 0)
            {
                /*首先访问同层级第一个节点*/
                vert1 = (int)q.Dequeue();
                vert2 = GetAdjUnvisitedVertex(vert1);
                /*结束*/
                
                while (vert2 != -1)
                {
                    /*首先访问第二个节点*/
                    vertiexes[vert2].IsVisited = true;
                    DisplayVert(vert2);
                    q.Enqueue(vert2);
                    //寻找邻接的
                    vert2 = GetAdjUnvisitedVertex(vert1);
                }
            }
            //重置所有节点为未访问过
            for (int u = 0; u < numVerts; u++)
            {
                vertiexes[u].IsVisited = false;
            }
        }


下面我们来测试深度优先和广度优先遍历:

    我们的测试生成一个如下的图:

    1

 

    测试代码:

   

static void Main(string[] args)
        {
            Graph g = new Graph();
            g.AddVertex("A");
            g.AddVertex("B");
            g.AddVertex("C");
            g.AddVertex("D");
            g.AddVertex("E");
            g.AddVertex("F");
            g.AddVertex("G");
            g.AddEdge(0, 1);
            g.AddEdge(0, 2);
            g.AddEdge(1, 3);
            g.AddEdge(2, 4);
            g.AddEdge(3, 5);
            g.AddEdge(4, 6);
            Console.WriteLine("\n深度优先遍历");
            g.DepthFirstSearch();
            Console.WriteLine("\n广度优先遍历");
            g.BreadthFirstSearch();
            Console.ReadKey();
            
            

        }


    运行结果:

    2

第2章 形基础 34 2.1 笔和画刷 34 2.1.1 pen 类 34 2.1.2 brush 类 35 2.2 基本形形状 37 2.2.1 点 37 2.2.2 直线和曲线 37 2.2.3 矩形、椭圆形和圆弧形 40 2.2.4 多边形 42 2.3 颜色 44 2.4 双倍缓存 66 第3章 坐标系统和颜色变换 69 3.1 坐标系统 69 3.2 颜色变换 77 第二部分 二维形的基本算法 第4章 二维矩阵和变换 82 4.1 矩阵基础和变换 82 4.2 齐次坐标 82 4.2.1 齐次坐标中的缩放 83 4.2.2 齐次坐标中的平移 83 4.2.3 齐次坐标中的旋转 84 4.2.4 变换组合 85 4.2.5 c#中矩阵的定义 86 4 .2.6 c#中的矩阵操作 87 4.2.7 c#中基本的矩阵变换 89 4.3 c#形对象的变换 93 基本变换 93 4.4 c#中的多对象变换 101 4.5 文字变换 105 第5章 二维线形形 109 5.1 序列化和反序列化及二维形的基本框架 109 5.1.1 c#序列化和反序列化 110 5.1.2 二维形的基本框架 113 5.2 二维形 248 5.2.1 简单实例 248 5.2.2 例 278 5.2.3 符号 289 5.2.4 对数比例 302 5.2.5 形的修饰 308 5.3 阶梯状 316 5.4 多y轴 318 第6章 特殊二维形 327 6.1 创建柱状 327 6.1.1 水平柱状 327 6.1.2 垂直柱状 343 6.1.3 形充填柱状 344 6.1.4 重叠柱状 346 6.2 饼状 348 6.3 误差 361 6.4 股票 367 6.4.1 最高最低收盘价股票 368 6.4.2 最高最低开盘收盘价股票 369 6.4.3 最高最低价股票 377 6.4.4 k 线(阴阳烛) 380 6.5 面积 389 6.6 综合 390 第三部分 三维形的相关知识及三维形的实现 第7章 三维矩阵和变换 396 7.1 三维数学概念 396 7.1.1 操作三维对象 396 7.1.2 数学结构 397 7.2 三维中的基本矩阵和变换 402 7.2.1 c#中三维点和矩阵的操作 403 7.2.2 三维的基本变换 405 7.3 方位角和仰角 434 7.4 三维形中的特殊坐标系统 439 7.4.1 球坐标系统 440 7.4.2 圆柱坐标系统 443 7.5 特殊坐标中的实际应用 447 7.5.1 球坐标示例 447 7.5.2 双缓存 463 第8章 三维形 473 8.1 三维形基础 473 8.1.1 point3和matrix3类 473 8.1.2 chartstyle类 476 8.1.3 坐标轴 496 8.1.4 网格线 496 8.1.5 标签 497 8.2 三维折线 503 8.3 三维形函数包 508 8.3.1 chartstyle2d类 509 8.3.2 point4类 515 8.3.3 dataseries类 516 8.3.4 chartfunctions类 521 8.3.5 drawchart类 526 8.4 曲面实现 541 8.4.1 网格 541 8.4.2 幕布网格 548 8.4.3 瀑布网格 551 8.4.4 曲面 553 8.5 x-y平面色彩 559 8.6 轮廓 564 8.6.1 轮廓的算法 564 8.6.2 轮廓实现 564 8.7 组合 569 8.7.1 三维体系中的x-y色彩 570 8.7.2 三维体系中的轮廓 571 8.7.3 网格-轮廓组合 575 8.7.4 曲面-轮廓组合 576 8.7.5 填充曲面-轮廓组合 576 8.8 三维柱状 577 实现柱状 577 8.9 切片 591 切片实现 591 第四部分 c#中应用微软office的excel实现各种二维及三维形 第9章 应用程序中的excel表 600 9.1 excel和c#间的互操作 600 9.2 c#应用程序中的excel表示例 602 9.2.1 excel表对象模型 602 9.2.2 创建独立的excel表 604 9.2.3 创建嵌入式excel表 607 9.3 更多的excel表 608 9.3.1 柱状 608 9.3.2 饼状 611 9.3.3 面积 613 9.3.4 圆环 615 9.3.5 雷达 615 9.3.6 股价 617 9.3.7 曲面 619 9.3.8 颜色映射 622 9.4 整合excel表到windows forms应用程序中 627 9.4.1 windows窗体上的独立excel表 627 9.4.2 windows窗体上的嵌入式excel表 631 第五部分 实现文件的相关知识 第10章 文件的读/写 634 10.1 c#文件读/写常用类 634 10.1.1 system.io.file类和system.io.fileinfo类 634 10.1.2 system.io.directory类和system.directoryinfo类 637 10.2 c#基于流的输入/输出 639 流的继承结构 640 10.3 文件读/写操作涉及的类 643 10.4 一些常见的问题及其解决 方案 643
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值