欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#圖表算法之有向圖

 更新時(shí)間:2022年04月24日 16:49:12   作者:Ruby_Lu  
這篇文章介紹了C#圖表算法之有向圖,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

在有向圖中,邊是單向的:每條邊連接的兩個(gè)頂點(diǎn)都是一個(gè)有序?qū)?,它們的鄰接性是單向的。許多應(yīng)用都是天然的有向圖,如下圖。為實(shí)現(xiàn)添加這種單向性的限制很容易也很自然,看起來沒什么壞處。但實(shí)際上這種組合性的結(jié)構(gòu)對(duì)算法有深刻的影響,使得有向圖和無向圖的處理大有不同。

1.術(shù)語

雖然我們?yōu)橛邢驁D的定義和無向圖幾乎相同(將使用的部分算法和代碼也是),但為了說明邊的方向性而產(chǎn)生的細(xì)小文字差異所代表的結(jié)構(gòu)特性是重點(diǎn)。

定義:一幅有方向性的圖(或有向圖)是由一組頂點(diǎn)和一組有方向的邊組成的,每條有方向的邊都連著有序的一對(duì)頂點(diǎn)。

我們稱一條有向邊由第一個(gè)頂點(diǎn)指出并指向第二個(gè)頂點(diǎn)。在一幅有向圖中,一個(gè)頂點(diǎn)的出度為由該頂點(diǎn)指出的邊的總數(shù);一個(gè)頂點(diǎn)的入度為指向該頂點(diǎn)的邊的總數(shù)。一條有向邊的第一個(gè)頂點(diǎn)稱為它的頭,第二個(gè)頂點(diǎn)則稱為它的尾。用 v->w 表示有向圖中一條由v 指向 w 的邊。一幅有向圖的兩個(gè)頂點(diǎn)的關(guān)系可能有四種:沒有邊相連;v->w; w-> v;v->w 和 w->v。

在一幅有向圖中,有向路徑由一系列頂點(diǎn)組成,對(duì)于其中的每個(gè)頂點(diǎn)都存在一條有向邊從它指向序列中的下一個(gè)頂點(diǎn)。有向環(huán)為一條至少含有一條邊且起點(diǎn)和終點(diǎn)相同的有向路徑。路徑或環(huán)的長度即為其中所包含的邊數(shù)。

當(dāng)存在從 v 到 w 的有向路徑時(shí),稱頂點(diǎn) w 能夠由頂點(diǎn) v 達(dá)到。我們需要理解有向圖中的可達(dá)性和無向圖中的連通性的區(qū)別。

2.有向圖的數(shù)據(jù)類型

有向圖API

有向圖表示

我們使用鄰接表來表示有向圖,其中邊 v -> w 表示頂點(diǎn) v 所對(duì)應(yīng)的鄰接鏈表中包含一個(gè) w 頂點(diǎn)。這種表示方法和無向圖幾乎相同而且更明晰,因?yàn)槊織l邊都只會(huì)出現(xiàn)一次。

有向圖取反

Digraph 的 API 中還添加了一個(gè) Reverse 方法。它返回該有向圖的一個(gè)副本,但將其中所有邊的方向反轉(zhuǎn)。在處理有向圖時(shí)這個(gè)方法有時(shí)很有用,因?yàn)檫@樣用例就可以找出“指向”每個(gè)頂點(diǎn)的所有邊,而 Adj 方法給出的是由每個(gè)頂點(diǎn)指出的邊所連接的所有頂點(diǎn)。

頂點(diǎn)的符號(hào)名

在有向圖中,使用符號(hào)名作為頂點(diǎn)也很簡單,參考SymbolGraph。

namespace Digraphs
{
    public class Digraph
    {
        private int v;
        private int e;
        private List<int>[] adj;

        public Digraph(int V)
        {
            this.v = V;
            this.e = 0;
            adj = new List<int>[v];
            for (var i = 0; i < v; i++)
            {
                adj[i] = new List<int>();
            }
        }

        public int V()
        {
            return v;
        }

        public int E()
        {
            return e;
        }

        public List<int> Adj(int v)
        {
            return adj[v];
        }

        public void AddEdge(int v, int w)
        {
            adj[v].Add(w);
            e++;
        }

        public Digraph Reverse()
        {
            Digraph R = new Digraph(v);
            for (var i = 0; i < v; i++)
                foreach (var w in Adj(i))
                    R.AddEdge(w,i);

            return R;
        }
    }
}

3.有向圖的可達(dá)性

在無向圖中介紹的深度優(yōu)先搜索DepthFirstSearch ,解決了單點(diǎn)連通性的問題,使得用例可以判定其他頂點(diǎn)和給定的起點(diǎn)是否連通。使用完全相同的代碼,將其中的 Graph 替換成 Digraph 也可以解決有向圖中的單點(diǎn)可達(dá)性問題(給定一幅有向圖和一個(gè)起點(diǎn) s ,是否存在一條從 s 到達(dá)給定頂點(diǎn) v 的有向路徑?)。

在添加了一個(gè)接受多個(gè)頂點(diǎn)的構(gòu)造函數(shù)之后,這份 API 使得用例能夠解決一個(gè)更加一般的問題 -- 多點(diǎn)可達(dá)性 (給定一幅有向圖和頂點(diǎn)的集合,是否存在一條從集合中的任意頂點(diǎn)到達(dá)給定頂點(diǎn) v 的有向路徑?)

下面的DirectedDFS 算法使用了解決圖處理的標(biāo)準(zhǔn)范例和標(biāo)準(zhǔn)的深度優(yōu)先搜索來解決。對(duì)每個(gè)起點(diǎn)調(diào)用遞歸方法 Dfs ,以標(biāo)記遇到的任意頂點(diǎn)。

namespace Digraphs
{
    public class DirectedDFS
    {
        private bool[] marked;

        public DirectedDFS(Digraph G, int s)
        {
            marked = new bool[G.V()];
            Dfs(G,s);
        }

        public DirectedDFS(Digraph G, IEnumerable<int> sources)
        {
            marked = new bool[G.V()];
            foreach (var s in sources)
            {
                if (!marked[s])
                    Dfs(G,s);
            }
        }

        private void Dfs(Digraph G, int V)
        {
            marked[V] = true;
            foreach (var w in G.Adj(V))
            {
                if (!marked[w])
                    Dfs(G,w);
            }
        }

        public bool Marked(int v)
        {
            return marked[v];
        }
    }
}

在有向圖中,深度優(yōu)先搜索標(biāo)記由一個(gè)集合的頂點(diǎn)可達(dá)的所有頂點(diǎn)所需的時(shí)間與被標(biāo)記的所有頂點(diǎn)的出度之和成正比。

有向圖的尋路

在無向圖中的尋找路徑的算法,只需將 Graph 替換為 Digraph 就能夠解決下面問題:

  • 1.單點(diǎn)有向路徑:給定一幅有向圖和一個(gè)起點(diǎn) s ,從 s 到給定目的頂點(diǎn)是否存在一條有向路徑?如果有,找出這條路徑。
  • 2.單點(diǎn)最短有向路徑:給定一幅有向圖和一個(gè)起點(diǎn) s ,從 s 到給定目的頂點(diǎn) v 是否存在一條有向路徑?如果有,找出其中最短的那條(所含邊數(shù)最少)。

4.環(huán)和有向無環(huán)圖

在和有向圖相關(guān)的實(shí)際應(yīng)用中,有向環(huán)特別的重要。沒有計(jì)算機(jī)的幫助,在一幅普通的有向圖中找出有向環(huán)可能會(huì)很困難。從原則上來說,一幅有向圖可能含有大量的環(huán);在實(shí)際應(yīng)用中,我們一般只重點(diǎn)關(guān)注其中一小部分,或者只想知道它們是否存在。

調(diào)度問題

一種應(yīng)用廣泛的模型是給定一組任務(wù)并安排它們的執(zhí)行順序,限制條件是這些任務(wù)的執(zhí)行方法和開始時(shí)間。限制條件還可能包括任務(wù)的耗時(shí)以及消耗的資源。最重要的一種限制條件叫做優(yōu)先級(jí)限制,它指明了哪些任務(wù)必須在哪些任務(wù)之前完成。不同類型的限制條件會(huì)產(chǎn)生不同類型不同難度的調(diào)度問題。

下面以一個(gè)正在安排課程的大學(xué)生為例,有些課程是其他課程的先導(dǎo)課程:

如果假設(shè)該學(xué)生一次只能修一門課程,就會(huì)遇到優(yōu)先級(jí)下的調(diào)度問題:給定一組需要完成的任務(wù),以及一組關(guān)于任務(wù)完成的先后次序的優(yōu)先級(jí)限制。在滿足限制條件的前提下應(yīng)該如何安排并完成所有任務(wù)?

對(duì)于任意一個(gè)這樣的問題,我們先畫出一幅有向圖,其中頂點(diǎn)對(duì)應(yīng)任務(wù),有向邊對(duì)應(yīng)優(yōu)先級(jí)順序。為了簡化問題,我們以整數(shù)為頂點(diǎn):

在有向圖中,優(yōu)先級(jí)限制下的調(diào)度問題等價(jià)于一個(gè)基本問題--拓?fù)渑判?/strong>:給定一幅圖,將所有頂點(diǎn)排序,使得所有的有向邊均從排在前面的元素指向排在后面的元素(或者說明無法做到這一點(diǎn))。

如圖,所有的邊都是向下的,所以清晰地表示了這幅有向圖模型所代表的有優(yōu)先級(jí)限制的調(diào)度問題的一個(gè)解決方法:按照這個(gè)順序,該同學(xué)可以滿足先導(dǎo)課程限制的條件下修完所有課程。

有向圖中的環(huán)

如果任務(wù) x 必須在任務(wù) y 之前完成,而任務(wù) y 必須在任務(wù) z 之前完成,但任務(wù) z 又必須在任務(wù) x 之前完成,那肯定是有人搞錯(cuò)了,因?yàn)檫@三個(gè)限制條件是不可能被同時(shí)滿足的。一般來說,如果一個(gè)優(yōu)先級(jí)限制的問題中存在有向環(huán),那么這個(gè)問題肯定是無解的。要檢查這種錯(cuò)誤,需要解決 有向環(huán)檢測:給定的有向圖中包含有向環(huán)嗎?如果有,按照路徑的方向從某個(gè)頂點(diǎn)并返回自己來找到環(huán)上的所有頂點(diǎn)。

一幅有向圖中含有環(huán)的數(shù)量可能是圖的大小的指數(shù)級(jí)別,因此我們只需找到一個(gè)環(huán)即可,而不是所有環(huán)。在任務(wù)調(diào)度和其他許多實(shí)際問題中不允許出現(xiàn)有向環(huán),因此有向無環(huán)圖就變得很特殊。

基于深度優(yōu)先搜索可以解決有向環(huán)檢測的問題,因?yàn)橛上到y(tǒng)維護(hù)的遞歸調(diào)用的棧表示的正是“當(dāng)前”正在遍歷的有向路徑。一旦我們找到了一條有向邊 v -> w 且 w 已經(jīng)存在于棧中,就找到了一個(gè)環(huán),因?yàn)闂1硎镜氖且粭l由 w 到 v 的有向路徑,而 v -> w 正好補(bǔ)全了這個(gè)環(huán)。如果沒有找到這樣的邊,就意味著這副有向圖是無環(huán)的。DirectedCycle 基于這個(gè)思想實(shí)現(xiàn)的:

namespace Digraphs
{
    public class DirectedCycle
    {
        private bool[] marked;
        private int[] edgeTo;
        private Stack<int> cycle;//有向環(huán)中的所有頂點(diǎn)(如果存在)
        private bool[] onStack;//遞歸調(diào)用的棧上的所有頂點(diǎn)

        public DirectedCycle(Digraph G)
        {
            onStack = new bool[G.V()];
            edgeTo = new int[G.V()];
            marked = new bool[G.V()];

            for (int v = 0; v < G.V(); v++)
            {
                if (!marked[v])
                    Dfs(G,v);
            }
        }

        private void Dfs(Digraph G, int v)
        {
            onStack[v] = true;
            marked[v] = true;
            foreach (var w in G.Adj(v))
            {
                if (hasCycle())
                    return;
                else if (!marked[w])
                {
                    edgeTo[w] = v;
                    Dfs(G, w);
                }
                else if (onStack[w])
                {
                    cycle = new Stack<int>();
                    for (int x = v; x != w; x = edgeTo[x])
                        cycle.Push(x);
                    cycle.Push(w);
                    cycle.Push(v);
                }
            }
            onStack[v] = false;
        }

        private bool hasCycle()
        {
            return cycle != null;
        }

        public IEnumerable<int> Cycle()
        {
            return cycle;
        }
    }
}

該類為標(biāo)準(zhǔn)的的遞歸 Dfs 方法添加了一個(gè)布爾類型的數(shù)組 onStack 來保存遞歸調(diào)用期間棧上的所有頂點(diǎn)。當(dāng)它找到一條邊 v -> w 且 w 在棧中時(shí),它就找到了一個(gè)有向環(huán)。環(huán)上的所有頂點(diǎn)可以通過 edgeTo 中的鏈接得到。

在執(zhí)行 Dfs 時(shí),查找的是一條由起點(diǎn)到 v 的有向路徑。要保存這條路徑,DirectedCycle 維護(hù)了一個(gè)由頂點(diǎn)索引的數(shù)組onStack,以標(biāo)記遞歸調(diào)用的棧上的所有頂點(diǎn)(在調(diào)用 Dfs 時(shí)將 onStack[ v ] 設(shè)為 true,在調(diào)用結(jié)束時(shí)將其設(shè)為 false)。DirectedCycle 同時(shí)也使用了一個(gè)edgeTo 數(shù)組,在找到有向環(huán)時(shí)返回環(huán)中的所有頂點(diǎn)。

頂點(diǎn)的深度優(yōu)先次序與拓?fù)渑判?/h3>

優(yōu)先級(jí)限制下的調(diào)度問題等價(jià)于計(jì)算有向無環(huán)圖中的所有頂點(diǎn)的拓?fù)渑判颍?/p>

下面算法的基本思想是深度優(yōu)先搜索正好只會(huì)訪問每個(gè)頂點(diǎn)一次。如果將 Dfs 的參數(shù)頂點(diǎn)保存在一個(gè)數(shù)據(jù)結(jié)構(gòu)中,遍歷這個(gè)數(shù)據(jù)結(jié)構(gòu)實(shí)際上就能訪問圖中的所有頂點(diǎn),遍歷的順序取決于這個(gè)數(shù)據(jù)結(jié)構(gòu)的性質(zhì)以及是在遞歸調(diào)用之前還是之后進(jìn)行保存。在典型的應(yīng)用中,頂點(diǎn)一下三種排列順序:

  • 前序:在遞歸調(diào)用之前將頂點(diǎn)加入隊(duì)列;
  • 后序:在遞歸調(diào)用之后將頂點(diǎn)加入隊(duì)列;
  • 逆后序:在遞歸調(diào)用之后將頂點(diǎn)壓入棧。

該類允許用例用各種順序遍歷深度優(yōu)先搜索經(jīng)過得頂點(diǎn)。這在高級(jí)得有向圖處理算法非常有用,因?yàn)樗阉鞯眠f歸性使得我們能夠證明這段計(jì)算得許多性質(zhì)。

namespace Digraphs
{
    public class DepthFirstOrder
    {
        private bool[] marked;
        private Queue<int> pre;//所有頂點(diǎn)的前序排列
        private Queue<int> post;//所有頂點(diǎn)的后序排列
        private Stack<int> reversePost;//所有頂點(diǎn)的逆后序排列

        public DepthFirstOrder(Digraph G)
        {
            marked = new bool[G.V()];
            pre = new Queue<int>();
            post = new Queue<int>();
            reversePost = new Stack<int>();

            for (var v = 0; v < G.V(); v++)
            {
                if (!marked[v])
                    Dfs(G,v);
            }
        }

        private void Dfs(Digraph G, int v)
        {
            pre.Enqueue(v);

            marked[v] = true;
            foreach (var w in G.Adj(v))
            {
                if (!marked[w])
                    Dfs(G,w);
            }

            post.Enqueue(v);
            reversePost.Push(v);
        }

        public IEnumerable<int> Pre()
        {
            return pre;
        }

        public IEnumerable<int> Post()
        {
            return post;
        }

        public IEnumerable<int> ReversePost()
        {
            return reversePost;
        }
    }
}

一幅有向無環(huán)圖得拓?fù)渑判蚣礊樗许旤c(diǎn)的逆后序排列。

拓?fù)渑判?/h3>
namespace Digraphs
{
    public class Topological
    {
        private IEnumerable<int> order;
        public Topological(Digraph G)
        {
            DirectedCycle cycleFinder = new DirectedCycle(G);
            if (cycleFinder.HasCycle())
            {
                DepthFirstOrder dfs = new DepthFirstOrder(G);
                order = dfs.ReversePost();
            }
        }

        public IEnumerable<int> Order()
        {
            return order;
        }

        public bool IsDAG()
        {
            return order != null;
        }
    }
}

這段使用DirectedCycle 檢測是否有環(huán),使用DepthFirstOrder 返回有向圖的逆后序。

使用深度優(yōu)先搜索對(duì)有向無環(huán)圖進(jìn)行拓?fù)渑判蛩璧臅r(shí)間和 V+E 成正比。第一遍深度優(yōu)先搜索保證了不存在有向環(huán),第二遍深度優(yōu)先搜索產(chǎn)生了頂點(diǎn)的逆后序排列。

在實(shí)際應(yīng)用中,拓?fù)渑判蚝陀邢颦h(huán)的檢測總是一起出現(xiàn),因?yàn)橛邢颦h(huán)的檢測是排序的前提。例如,在一個(gè)任務(wù)調(diào)度應(yīng)用中,無論計(jì)劃如何安排,其背后的有向圖中包含的環(huán)意味著存在一個(gè)必須被糾正的嚴(yán)重錯(cuò)誤。因此,解決任務(wù)調(diào)度類應(yīng)用通常需要一下3步:

  • 1.指明任務(wù)和優(yōu)先級(jí)條件;
  • 2.不斷檢測并去除有向圖中的所有環(huán),以確保存在可行方案;
  • 3.使用拓?fù)渑判蚪鉀Q調(diào)度問題。

類似地,調(diào)度方案的任何變動(dòng)之后都需要再次檢查是否存在環(huán),然后再計(jì)算新的調(diào)度安排。

5.有向圖中的強(qiáng)連通性

如果兩個(gè)頂點(diǎn) v 和 w 是相互可達(dá)的,則稱它們?yōu)閺?qiáng)連通的。也就是說,即存在一條從 v 到 w 的有向路徑,也存在一條從 w 到 v 的有向路徑。如果一幅有向圖中的任意兩個(gè)頂點(diǎn)都是強(qiáng)連通的,則稱這副有向圖也是強(qiáng)連通的。

下面是強(qiáng)連通圖的例子,可以看到,環(huán)在強(qiáng)連通性的理解上起著重要的作用。

強(qiáng)連通分量

和無向圖中的連通性一樣,有向圖中的強(qiáng)連通性也是一種頂點(diǎn)之間的等價(jià)關(guān)系:

  • 自反性:任意頂點(diǎn) v 和自己都是強(qiáng)連通的。
  • 對(duì)稱性:如果 v 和 w 是強(qiáng)連通的,那么 w 和 v 也是。
  • 傳遞性:如果 v 和 w 是強(qiáng)連通的且 w 和 x 也是強(qiáng)連通的,那么 v 和 x 也是強(qiáng)連通的。

作為一種等價(jià)關(guān)系,強(qiáng)連通性將所有頂點(diǎn)分為了一些等價(jià)類,每個(gè)等價(jià)類都是由相互均為強(qiáng)連通的頂點(diǎn)的最大子集組成。我們稱這些子集為強(qiáng)連通分量。如下圖,一個(gè)含有 V 個(gè)頂點(diǎn)的有向圖含有 1~ V個(gè)強(qiáng)連通分量——一個(gè)強(qiáng)連通圖只含有一個(gè)強(qiáng)連通分量,而一個(gè)有向無環(huán)圖則含有 V 個(gè)強(qiáng)連通分量。需要注意的是強(qiáng)連通分量的定義是基于頂點(diǎn)的,而不是邊。有些邊連接的兩個(gè)頂點(diǎn)都在同一個(gè)強(qiáng)連通分量中,而有些邊連接的兩個(gè)頂點(diǎn)則不在同一強(qiáng)連通分量中。

強(qiáng)連通分量API

設(shè)計(jì)一種平方級(jí)別的算法來計(jì)算強(qiáng)連通分量并不困難,單對(duì)于處理實(shí)際應(yīng)用中的大型圖來說,平方級(jí)別的時(shí)間和空間需求是不可接受的。

Kosaraju算法

在有向圖中如何高效地計(jì)算強(qiáng)連通分量?我們只需修改無向圖連通分量的算法 CC,KosarajuCC 算法如下,它將會(huì)完成一下任務(wù):

1.在給定的一幅有向圖 G 中,使用 DepthFirstOrder 來計(jì)算它的反向圖 GR 的逆后序排列;

2.在 G 中進(jìn)行標(biāo)準(zhǔn)的深度優(yōu)先搜索,但是要按照剛才計(jì)算得到的順序而非標(biāo)準(zhǔn)的順序來訪問所有未被標(biāo)記的頂點(diǎn);

3.在構(gòu)造函數(shù)中,所有在同一個(gè)遞歸 Dfs() 調(diào)用中被訪問到的頂點(diǎn)都在同一個(gè)強(qiáng)連通分量中,將它們按照和 CC 相同的方式識(shí)別出來。

namespace Digraphs
{
    public class KosarajuCC
    {
        private bool[] marked;//已訪問的頂點(diǎn)
        private int[] id;//強(qiáng)連通分量的標(biāo)識(shí)符
        private int count;//強(qiáng)連通分量的數(shù)量

        public KosarajuCC(Digraph G)
        {
            marked = new bool[G.V()];
            id = new int[G.V()];
            DepthFirstOrder order = new DepthFirstOrder(G.Reverse());
            foreach (var s in order.ReversePost())
            {
                if (!marked[s])
                {
                    Dfs(G,s);
                    count++;
                }
            }
        }

        private void Dfs(Digraph G, int v)
        {
            marked[v] = true;
            id[v] = count;
            foreach (var w in G.Adj(v))
            {
                if (!marked[w])
                    Dfs(G,w);
            }
        }

        public bool StronglyConnected(int v, int w)
        {
            return id[v] == id[w];
        }

        public int Id(int v)
        {
            return id[v];
        }

        public int Count()
        {
            return count;
        }
    }
}

Kosaraju 算法的預(yù)處理所需的時(shí)間和空間與 V+E 成正比且支持常數(shù)時(shí)間的有向圖強(qiáng)連通性的查詢。

再談可達(dá)性

在無向圖中如果兩個(gè)頂點(diǎn) V 和 W 是連通的,那么就既存在一條從 v 到 w 的路徑也存在一條從 w 到 v 的路徑。在有向圖中如果兩個(gè)頂點(diǎn) v 和 w 是強(qiáng)連通的,那么就既存在一條從 v 到 w 的路徑也存在另一條從 w 到 v 的路徑。但對(duì)于一對(duì)非強(qiáng)連通的頂點(diǎn),也許存在一條從 v 到 w 的路徑,也許存在一條從 w 到 v 的路徑,也許兩條都不存在,但不可能兩條都存在。

頂點(diǎn)對(duì)的可達(dá)性:對(duì)于無向圖,等價(jià)于連通性問題;對(duì)于有向圖,它和強(qiáng)連通性有很大區(qū)別。 CC 實(shí)現(xiàn)需要線性級(jí)別的預(yù)處理時(shí)間才能支持常數(shù)時(shí)間的操作。在有向圖的相應(yīng)實(shí)現(xiàn)中能否達(dá)到這樣的性能?

有向圖 G 的傳遞閉包是由相同的一組頂點(diǎn)組成的另一幅有向圖,在傳遞閉包中存在一條從 v 指向 w 的邊當(dāng)且僅當(dāng)在 G 中 w 是從 v 可達(dá)的。

根據(jù)約定,每個(gè)頂點(diǎn)對(duì)于自己都是可達(dá)的,因此傳遞閉包會(huì)含有 V 個(gè)自環(huán)。上圖只有 22 條有向邊,但它的傳遞閉包含有可能的 169 條有向邊中的 102 條。一般來說,一幅有向圖的傳遞閉包中所含的邊都比原圖中多得多。例如,含有 V 個(gè)頂點(diǎn)和 V 條邊的有向環(huán)的傳遞閉包是一幅含有 V 的平方條邊的有向完全圖。因?yàn)閭鬟f閉包一般都是稠密的,我們通常都將它們表示為一個(gè)布爾值矩陣,其中 v 行 w 列的值為 true 當(dāng)且僅當(dāng) w 是從 v 可達(dá)的。與其計(jì)算一幅有向圖的傳遞閉包,不如使用深度優(yōu)先搜索來實(shí)現(xiàn)如下API:

下面的算法使用DirectedDFS 實(shí)現(xiàn):

namespace Digraphs
{
    public class TransitiveClosure
    {
        private DirectedDFS[] all;

        public TransitiveClosure(Digraph G)
        {
            all = new DirectedDFS[G.V()];
            for (var v = 0; v < G.V(); v++)
                all[v] = new DirectedDFS(G,v);
        }

        public bool Reachable(int v, int w)
        {
            return all[v].Marked(w);
        }
    }
}

該算法無論對(duì)于稀疏圖還是稠密圖,都是理想解決方案,但對(duì)于大型有向圖不適用,因?yàn)闃?gòu)造函數(shù)所需的空間和 V 的平方成正比,所需的時(shí)間和 V(V+ E) 成正比。

總結(jié)

到此這篇關(guān)于C#圖表算法之有向圖的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • C#中l(wèi)ock死鎖實(shí)例教程

    C#中l(wèi)ock死鎖實(shí)例教程

    這篇文章主要介紹了C#中l(wèi)ock死鎖的用法,對(duì)于共享資源的訪問及C#程序設(shè)計(jì)的安全性而言,有著非常重要的意義!需要的朋友可以參考下
    2014-08-08
  • Unity?UGUI?按鈕綁定事件的?4?種方式匯總

    Unity?UGUI?按鈕綁定事件的?4?種方式匯總

    UGUI?可視化創(chuàng)建以及關(guān)聯(lián)事件很方便,?動(dòng)態(tài)創(chuàng)建可以利用創(chuàng)建好的?Prefab?進(jìn)行實(shí)例化,?只是在關(guān)聯(lián)事件上有些復(fù)雜,這篇文章主要介紹了Unity?UGUI?按鈕綁定事件的?4?種方式,需要的朋友可以參考下
    2022-01-01
  • 關(guān)于C#中排序函數(shù)的總結(jié)

    關(guān)于C#中排序函數(shù)的總結(jié)

    下面小編就為大家?guī)硪黄P(guān)于C#中排序函數(shù)的總結(jié)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-05-05
  • 基于C#實(shí)現(xiàn)亂碼視頻效果

    基于C#實(shí)現(xiàn)亂碼視頻效果

    亂碼視頻效果可能很多人都在抖音看到過,即把一個(gè)短視頻,轉(zhuǎn)成數(shù)字、字母等亂碼組成的形式進(jìn)行播放。本文將用C#實(shí)現(xiàn)一下這一效果,感興趣的可以了解一下
    2023-01-01
  • C# 實(shí)現(xiàn)特殊字符快速轉(zhuǎn)碼

    C# 實(shí)現(xiàn)特殊字符快速轉(zhuǎn)碼

    這篇文章主要介紹了C# 實(shí)現(xiàn)特殊字符快速轉(zhuǎn)碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-01-01
  • C#獲取DataTable對(duì)象狀態(tài)DataRowState

    C#獲取DataTable對(duì)象狀態(tài)DataRowState

    這篇文章介紹了C#獲取DataTable對(duì)象狀態(tài)DataRowState的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-02-02
  • C#實(shí)現(xiàn)合并及拆分PDF文件的方法

    C#實(shí)現(xiàn)合并及拆分PDF文件的方法

    這篇文章主要為大家詳細(xì)介紹了C#合并及拆分PDF文件的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • C#中程序自刪除實(shí)現(xiàn)方法

    C#中程序自刪除實(shí)現(xiàn)方法

    這篇文章主要介紹了C# 程序自刪除實(shí)現(xiàn)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-01-01
  • SQL語句刪除和添加外鍵、主鍵的方法

    SQL語句刪除和添加外鍵、主鍵的方法

    本文將詳細(xì)介紹SQL語句刪除和添加外鍵、主鍵的方法,需要的朋友可以參考下
    2012-11-11
  • unity使用鏈表實(shí)現(xiàn)貪吃蛇游戲

    unity使用鏈表實(shí)現(xiàn)貪吃蛇游戲

    這篇文章主要為大家詳細(xì)介紹了unity使用鏈表實(shí)現(xiàn)貪吃蛇游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-04-04

最新評(píng)論