数据结构之图

图的定义

定义:图是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合

图的构成
1、顶点:图中数据元素
2、边:顶点之间的逻辑关系
3、顶点集合为有穷非空集合
4、权:与图的边或弧相关的数

图的种类

  1. 无向图:若顶点Vi到Vj之间的边没有方向,则称为这条边为无向边,用无序偶对(Vi,Vj)来表示。如果图中任意两个顶点之间的边都是无向边,则称该图为无向图。
  2. 有向图:若顶点Vi到Vj之间的边有方向,则称为这条边为有向边(弧),用有序偶<Vi,Vj>来表示。如果图中任意两个顶点之间的边都是有向边,则称该图为有向图。(注意弧就是Vi到Vj的的有向边,Vi是弧头,Vj是弧尾,两个的位置不能变)
  3. 简单图:若不存在顶点到其自身的边,且同一条边不重复出现,则称这样的图为简单图
  4. 多重图:图中某两个结点的边数多与一条,又允许顶点通过同一条边和自己关联,则称为多重图
  5. 无向完全图:在无向图中,如果任意两个顶点之间都存在边,则称该图为无向完全图
  6. 有向完全图:在有向图中,如果任意两个顶点之间都存在方向互为相反的两条弧,则称该图为有向完全图
  7. 稀疏图:有很少条边或弧的图称为稀疏图,反之称为稠密图
  8. 网:带权的图

在这里插入图片描述

图的顶点

对于无向图来说:
顶点v的度是指依附于该顶点的边的条数,记为TD(v)。
总顶点的度的和等于边数的2倍

对于有向图来说:
入度是以顶点v为终点的有向边的数目,记为ID(v);
出度时以顶点v为起点的有向边的数目,记为OD(v).
v的度为TD(v)=ID(v)+OD(v);
各顶点出度数=入度数=边数

路径:顶点到顶点之间的一条路径是指顶点序列
简单路径:在路径序列中,顶点不重复出现的路径称为简单路径
回路:第一个顶点和最后一个顶点相同的路径称为回路或环
简单回路:除第一个顶点和最后一个顶点外,其余不重复出现的回路称为简单回路
路径长度:路径上边的数目
顶点到顶点的距离:从顶点u出发到顶点v的最短路径若存在,则次路径的长度称为从u到v的距离,若从u到v不存在路径,则记该距离为无穷

无向图中,若从顶点v到顶点w有路径存在,则称v和w是连通的,如果任意两个顶点都是连通的,则称该图为连通图,否则称为非连通图。
对于n个顶点的无向图G,若图G是连通图,则最少有n-1
条边。

若图G是非连通图,则最多可能有C上标2下标为n-1条边

有向图中,若从顶点v到顶点w和从顶点w到顶点v之间都有路径,则称这两个顶点是强连通的,如果任意一对顶点都是强连通的,则称此图为强连通图。
对于n个结点的有向图G,若G是强连通图,则最少有n条边(形成回路)

图的局部–子图
在这里插入图片描述
如果子图所有结点都有,则称其为G的生成子图

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

生成树
连通图的生成树是包含图中全部顶点的一个极小连通子图
若图中顶点树为n,则它的生成树含有n-1条边。对于生成树而言,若砍去它的一条边,则会变成非连通图,若加上一条边则会形成一个回路。

无向树
n个顶点的树,必有n-1条边
n个顶点的图,若|E|>n-1,则一定有回路
有向树
一个顶点的入度为0,其余顶点的入度均为1的有向图,称为有向树

生成森林
在非连通图中,连通分量的生成树构成了非连通图的生成森林

图的存储

图的存储有四种方法
1、邻接矩阵
2、邻接表
3、十字链表
4、邻接多重表

邻接矩阵
含义:图的邻接矩阵存储方式是用两个数组来表示图。一个一维数组存储图中顶点信息,一个二维数组(称为邻接矩阵)存储图中的边或弧的信息
空间复杂度:O(|V|平方2)——只和顶点数相关,和实际的边数无关
应用:适合用于存储稠密图

在这里插入图片描述
矩阵相乘
A*2*[1][4]=
邻接表
含义:将数组与链表相结合的存储方法称为邻接表
无向图——边结点的数量是2|E|,整体空间复杂度为O(|V|+2|E|)
有向图——边结点的数量是|E|,整体空间复杂度为O(|V|+|E|)
邻接表的实现
1、图中的顶点用一个一维数组存储
2、图中每个顶点v的所有邻接点构成一个线性表
在这里插入图片描述
data是数据域,存储顶点的信息
Firstdae是指针域,指向边表的第一个结点。

在这里插入图片描述
adjvex是邻接点域,存储某顶点的邻接点在顶点表中的下标
next则存储指向边表中下一个结点的指针

十字链表
将邻接表与逆邻接表结合起来

用于存储有向图

在这里插入图片描述
顶点的结点
Firstin:表示入边表头指针
Firstout:表示出边表的头指针

在这里插入图片描述
边表结点
tailvex :指弧起点在顶点表的下标
headvex:指弧终点在顶点表中的下标
headlink:指入边表的指针域,指向终点相同的下一条边
taillink:指边表指针域,指向起点相同的下一条边
如果是网的话,还可以增加一个weight域来存储权值

在这里插入图片描述

邻接多重表
在这里插入图片描述
ivex和jvex是与某条边依附的两个顶点在顶点表中的下标。ilink指向依附顶点ivex的下一条边,jlink指向依附顶点jvex的下一条边。这就是邻接多重表结构

用于存储无向图

在这里插入图片描述

实现图的实例代码(邻接表法)

 /// <summary>
    /// 表头结点
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Vertex<T>
    {
        public T data;//数据

        public Node<T> firstEdge;//邻接点表头指针

        public Boolean visited;//访问标识

        public Vertex(T value)
        {
            data = value;
        }


    }

    //表结点
    public class Node<T>
    {
        public Vertex<T> adjex;//邻接点表头指针
        public Node<T> next;//下一个邻接点
        public Node(Vertex<T> value)
        {
            adjex = value;
        }



    }


    public class MyAdjacencyList<T>
    {

       List<Vertex<T>> items;//图的所有顶点集合

        public MyAdjacencyList(int capacity)
        {
            items = new List<Vertex<T>>();
        }
    
        //查找是否含有相同项
        public bool Containt(T item)
        {
            foreach (Vertex<T> v in items)//foreach与for的区别,foreach是安全性线程,而for是非安全性。同时foreach的效率更高
            {
                if (v.data.Equals(items))
                {
                    return true;
                }
             
            }   return false;
        }
        //查找指定项
        private Vertex<T> Find(T t)
        {
            foreach (Vertex<T> item in items)
            {
                if (item.data.Equals(t))
                {  
                    return item;
                }
              
            }
            return null;
        }


        //添加结点
        public void AddVertex(T t)
        {
            if (Containt(t))
            {
                throw new ArithmeticException("插入了重复结点!");

            }
            items.Add(new Vertex<T>(t));
        }
        //添加边(无向边)
        public void AddEdge(T from, T to)
        {
            Vertex<T> fromvar = Find(from);//寻找头结点
            if (fromvar == null)
            {
                throw new ArgumentException("头结点不存在");
            }
            Vertex<T> tovar = Find(to);
            if (tovar == null)
            {
                throw new ArgumentException("头结点不存在");
            }

            AddDirectEdge(fromvar, tovar);
            AddDirectEdge( tovar,fromvar);

        }

        //添加有向边
        public void AddDirectEdge(Vertex<T> fromvar, Vertex<T> tovar)
        {
            if (fromvar.firstEdge == null)//无邻接结点
            {
                fromvar.firstEdge = new Node<T>(tovar);
            }
            else
            {
                Node<T> tmp, node = fromvar.firstEdge;
                do
                {
                    //检查是否添加了重复边
                    if (node.adjex.data.Equals(tovar.data))
                    {
                        throw new ArgumentException("添加了重复边");
                    }
                    tmp = node;
                    node = node.next;
                }
                while
                (node != null);
                tmp.next = new Node<T>(tovar);//添加到链尾
               
            }
        }


        public override string ToString()
        {
            //打印结点信息
            string s=string.Empty;

            foreach (Vertex<T> v in items)
            {
                s += v.data.ToString() + ":";
                if (v.firstEdge != null)
                {
                    Node<T> tmp = v.firstEdge;
                    while (tmp != null)
                    {
                        s += tmp.adjex.data.ToString();
                        tmp = tmp.next;
                    }
                }
                s += "\r\n";
            }
            return s;
        }


    }
    class Program
    {
        static void Main(string[] args)//邻接表
        {
            MyAdjacencyList<string> a = new MyAdjacencyList<string>(4);
            a.AddVertex("A");
            a.AddVertex("B");
            a.AddVertex("C");
            a.AddVertex("D");

            //添加边
            a.AddEdge("A", "B");
            a.AddEdge("B", "D");
            a.AddEdge("A", "D");
            a.AddEdge("A", "C");
            Console.WriteLine(a.ToString());
            Console.ReadLine();


        }
    }

图的遍历
1、深度优先遍历


      public void DFSTravn()
        {
            IninVisited();
            DFS(items[0]);
        }
        private void DFS(Vertex<T> v)
        {
            v.visited = true;//先访问此结点
            Console.WriteLine(v.data+" ");

            Node<T> node = v.firstEdge;
            while (node != null)
            {
                if (!node.adjex.visited)//未被访问
                {
                    DFS(node.adjex);//递归
                }
                node = node.next;
            }
        }

2、广度优先遍历

  //广度优先遍历
        public void BFSTravn()
        {
            IninVisited();
            BFS(items[0]);
        }
        //采用队列的方法的广度遍历
        private void BFS(Vertex<T> v)
        {
            Queue<Vertex<T>> queue = new Queue<Vertex<T>>();
            v.visited = true;
            Console.WriteLine(v.data.ToString());
            queue.Enqueue(v);
            while (queue.Count > 0)//队列中存在元素继续遍历访问
            {
                Vertex<T> w = queue.Dequeue();
                Node<T> node = w.firstEdge;
                while (node != null)//访问此表头结点的所有表结点
                {
                    if (!node.adjex.visited)
                    {
                         Console.WriteLine(node.adjex.data+" ");
                    node.adjex.visited = true;
                    queue.Enqueue(node.adjex);
                    }
                     node = node.next;
                }
              
            }
        }

生成树
对于无向图,含有连通图全部顶点的一个极小连通图
本质:从连通图任一结点出发进行遍历操作所经过的边,再加上所有顶点构成的子图

最小生成树
如果连通图是一个网络,称该网络的所有生成树中权值总和最小的生成树
1、必须只使用该图中的边来构造最小生成树
2、必须使用且使用n-1边来连接途中的n个顶点
3、构造的最小生成树中不存在会路

计算最小生成树的方法
1、Prim算法(普利姆)
从某一个顶点开始构建生成树,每次将代价最小的新顶点纳入生成树,直到所有顶点都纳入为止。
时间复杂度:O(|v|2)适合用于稠密图

2、Kruskal算法(克鲁斯卡尔)
每次选择一条权值最小的边,使这条边的两头连通(原本已经连通的就不选),直到所有结点都连通
时间复杂度:O(|E|log2|E|)适合用于稀疏图

  • 5
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值