第22章 基本图的算法

第22章 基本图的算法

概要

介绍图的表示和图的搜索。图的搜索指的是系统化地跟随图中的边来访问图中的每个节点。图搜索算法可以用来发现图的结构。许多的图算法在一开始都会先通过搜索来获得图的结构,其他一些图算法则是对基本的搜索加以优化。可以说,图的搜素技巧是整个图算法领域的核心。

22.1图的表示

对于图G=(V,E),可以用两种标准表示方法表示。一种表示法将图作为邻接链表的组合,另一种是将图作为邻接矩阵来看待。两种表示方法都可以表示无向图,也可以表示有向图。

此处略过了矩阵的实现代码,先放邻接链表的基本构造代码:

        static void Main(string[] args)
        {
            var graph = new Graph();
            graph.ShowGraph();
        }

        public class Graph
        {
            public Node[] Adj;

            public int Edges;

            public Graph()
            {
                Console.WriteLine("结点个数:");
                var count = Convert.ToInt32(Console.ReadLine());
                Adj = new Node[count];
                for (var i = 0; i < count; i++)
                {
                    Adj[i] = new Node(i + 1);
                }
                Console.WriteLine("边数:");
                Edges = Convert.ToInt32(Console.ReadLine());
                for (var i = 0; i < Edges; i++)
                {
                    int u, v;
                    Console.WriteLine("边" + (i + 1) + "的头结点为:" + (u = Convert.ToInt32(Console.ReadLine())));
                    Console.WriteLine("边" + (i + 1) + "的尾结点为:" + (v = Convert.ToInt32(Console.ReadLine())));
                    Insert(Adj, u - 1, v);
                }
            }

            public void ShowGraph()
            {
                var count = Adj.Count();
                Console.WriteLine("图共有" + count + "个结点");
                Console.WriteLine("共有" + Edges + "条边:");
                for (var i = 0; i < count; i++)
                {
                    var x = Adj[i].next;
                    var u = Adj[i].value;
                    while (x != null)
                    {
                        Console.WriteLine(u + "->" + x.value);
                        x = x.next;
                    }
                }
            }
        }

        public class Node
        {
            public Node next;

            public int value;

            public Node(int value)
            {
                this.value = value;
                next = null;
            }
        }
        public static void Insert(Node[] adj, int index, int v)
        {
            if (adj != null)
            {
                var u = adj[index];
                var vNode = new Node(v);
                vNode.next = u.next;
                u.next = vNode;
            }
        }

如果G是一个有向图,则对于边(u,v)来说,结点v将出现在链表Adj[u]里,因此所有链表长度的和等于|E|;如果G是一个无向图,长度之和为2|E|。但无论是有向还是无向,存储空间均为Θ(V+E)。
对邻接链表稍加修改,即可用来表示权重图。权重通常由一个函数ω:E→R的函数给出,我们可以将边(u,v)的权重存储在链表中。
邻接链表的一个潜在缺陷就是无法快速判断一条边(u,v)是否是图中的一条边,唯一的办法是在邻接链表Adj[u]里面搜索结点v。(当然矩阵表示就没有这个问题,但是却付出了消耗更大空间的代价。)

22.1练习

22.1-1 给定有向图的邻接链表,需要多长时间才能计算出每个结点的出度?
多长时间才能计算出每个结点的入度?
O(E)的时间能计算出出度。
O(V+E)的时间能计算出入度。
22.1-2 给定一棵有7个结点的完全二叉树的邻接链表,请给出等价的邻接矩阵表示。这里假设结点的编号为从1~7。
7个结点的完全二叉树有6条边,循环非叶子结点即可找到所有邻接矩阵的表示结点。
因此在77的矩阵中边为a[1,2]、[1,3]、[2,4]、[2,5]、[3,6]、[3,7]。
22.1-3 有向图GT=(V,E)的转置是图G = (V,ET),这里ET = {(u,v)} ∈V
V:(u,v)∈E}。对于邻接链表和邻接矩阵两种表示,请给出从图G计算出GT的有效算法,并分析算法的运行时间。

邻接链表

            /// <summary>
            /// 翻转图G的所有边
            /// </summary>
            public void Revert()
            {
                var count = Adj.Length;
                var adj_ = new Node[count];
                for (var i = 0; i < count; i++)
                {
                    adj_[i] = new Node(i + 1);
                }
                for (var i = 0; i < count; i++)
                {
                    var x = Adj[i].next;
                    while (x != null)
                    {
                        Console.WriteLine(x.value + "->" + (i + 1));
                        Insert(adj_, x.value - 1, i + 1);
                        x = x.next;
                    }
                }
            }

对遍历V个结点的所有边进行翻转,也就是说每条边都进行了翻转,运行时间为O(V+E)。

邻接矩阵
这里就不放出代码了,主要是循环矩阵,对Adj[u,v]进行翻转得到Adj[v,u] = 1即可。
运行时间为O(V^2)。

22.1-4 给定多图G=(V,E)的邻接链表(多图是允许重复边和自循环边的图),请给出一个时间为O(V+E)的算法,用来计算改图的“等价”无向图G’ = (V,E’)的邻接链表表示。

            public void UnDirected()
            {
                var count = Adj.Length;
                var adj_ = new Node[count];
                // d充当计数器
                var d = new int[count + 1];
                for (var i = 0; i < count; i++)
                {
                    adj_[i] = new Node(i + 1);
                    d[i] = 0;
                }
                Console.WriteLine("等价无向图:");
                for (var i = 0; i < count; i++)
                {
                    var x = Adj[i].next;
                    while (x != null)
                    {
                        if (x.value != (i + 1)
                            && d[x.value - 1] != (i + 1))
                        {
                            Console.WriteLine(x.value + "->" + (i + 1));
                            Insert(adj_, x.value - 1, i + 1);
                            d[x.value - 1] = i + 1;
                        }
                        x = x.next;
                    }
                }
            }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值