zoukankan      html  css  js  c++  java
  • 不相交集合森林的启发式策略

    在文章《Union-Find 操作检测无向图有无环算法》中介绍了 Union-Find 算法的一个简单实现,使用一维数组来处理不相交集合数据结构(Disjoint-set data structure)

    Union-Find 算法为该数据结构提供了两种非常有用的操作:

    • Find:判断子集中是否存在特定的元素。可以用于检测是否两个元素存在于相同的子集中。
    • Union:将两个不子集合并成新的子集合。
     1   private int Find(int[] parent, int i)
     2   {
     3     if (parent[i] == -1)
     4       return i;
     5     return Find(parent, parent[i]);
     6   }
     7 
     8   private void Union(int[] parent, int x, int y)
     9   {
    10     int xset = Find(parent, x);
    11     int yset = Find(parent, y);
    12     parent[xset] = yset;
    13   }

    上述算法的实现的运行时间是 O(n)

    另一种实现方式是使用有根树来表示集合,树中的每个节点都包含集合的一个成员,每棵树表示一个集合,形成不相交集合森林(Disjoint-set forest)。每棵树的根包含了集合的代表,并且是它自己的父节点。

    如上图中,(a) 中两棵树表示两个集合 {b, c, e, h} 和 {d, f, g},其中 c 和 f 是代表。(b) 为 Union(e, g) 的结果。

    实际上,上面这种树的表示法不会比采用链表表示的算法更快。但是,通过引入两种启发式策略,可以获得目前已知的、渐进意义上最快的不想交集合数据结构。

    • 第一种启发式策略是按秩合并(Union by Rank),其思想是使包含较少节点的树的根指向包含较多节点的树的根。
    • 第二种启发式策略是路径压缩(Path Compression),在 Find 操作中,使查找路径上的每个节点都直接指向根节点。路径压缩并不改变节点的秩(Rank)。

    如上图中,(a) 为执行 Find 操作之前的表示集合的树;(b) 为执行 Find(a) 操作后的树,查找路径上的每个节点都直接指向了根。

    通过两种启发式策略对运行时间的改进,新的算法的运行时间为 O(logn)

      1 using System;
      2 using System.Collections.Generic;
      3 using System.Linq;
      4 
      5 namespace GraphAlgorithmTesting
      6 {
      7   class Program
      8   {
      9     static void Main(string[] args)
     10     {
     11       Graph g = new Graph(6);
     12       g.AddEdge(0, 1, 16);
     13       g.AddEdge(0, 2, 13);
     14       g.AddEdge(1, 2, 10);
     15       g.AddEdge(1, 3, 12);
     16       //g.AddEdge(2, 1, 4);
     17       g.AddEdge(2, 4, 14);
     18       //g.AddEdge(3, 2, 9);
     19       g.AddEdge(3, 5, 20);
     20       //g.AddEdge(4, 3, 7);
     21       //g.AddEdge(4, 5, 4);
     22 
     23       Console.WriteLine();
     24       Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
     25       Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
     26       Console.WriteLine();
     27 
     28       Console.WriteLine("Is there cycle in graph: {0}", g.HasCycle());
     29 
     30       Console.ReadKey();
     31     }
     32 
     33     class Edge
     34     {
     35       public Edge(int begin, int end, int weight)
     36       {
     37         this.Begin = begin;
     38         this.End = end;
     39         this.Weight = weight;
     40       }
     41 
     42       public int Begin { get; private set; }
     43       public int End { get; private set; }
     44       public int Weight { get; private set; }
     45 
     46       public override string ToString()
     47       {
     48         return string.Format(
     49           "Begin[{0}], End[{1}], Weight[{2}]",
     50           Begin, End, Weight);
     51       }
     52     }
     53 
     54     class Subset
     55     {
     56       public int Parent { get; set; }
     57       public int Rank { get; set; }
     58     }
     59 
     60     class Graph
     61     {
     62       private Dictionary<int, List<Edge>> _adjacentEdges
     63         = new Dictionary<int, List<Edge>>();
     64 
     65       public Graph(int vertexCount)
     66       {
     67         this.VertexCount = vertexCount;
     68       }
     69 
     70       public int VertexCount { get; private set; }
     71 
     72       public IEnumerable<int> Vertices { get { return _adjacentEdges.Keys; } }
     73 
     74       public IEnumerable<Edge> Edges
     75       {
     76         get { return _adjacentEdges.Values.SelectMany(e => e); }
     77       }
     78 
     79       public int EdgeCount { get { return this.Edges.Count(); } }
     80 
     81       public void AddEdge(int begin, int end, int weight)
     82       {
     83         if (!_adjacentEdges.ContainsKey(begin))
     84         {
     85           var edges = new List<Edge>();
     86           _adjacentEdges.Add(begin, edges);
     87         }
     88 
     89         _adjacentEdges[begin].Add(new Edge(begin, end, weight));
     90       }
     91 
     92       private int Find(Subset[] subsets, int i)
     93       {
     94         // find root and make root as parent of i (path compression)
     95         if (subsets[i].Parent != i)
     96           subsets[i].Parent = Find(subsets, subsets[i].Parent);
     97 
     98         return subsets[i].Parent;
     99       }
    100 
    101       private void Union(Subset[] subsets, int x, int y)
    102       {
    103         int xroot = Find(subsets, x);
    104         int yroot = Find(subsets, y);
    105 
    106         // Attach smaller rank tree under root of high rank tree
    107         // (Union by Rank)
    108         if (subsets[xroot].Rank < subsets[yroot].Rank)
    109           subsets[xroot].Parent = yroot;
    110         else if (subsets[xroot].Rank > subsets[yroot].Rank)
    111           subsets[yroot].Parent = xroot;
    112 
    113         // If ranks are same, then make one as root and increment
    114         // its rank by one
    115         else
    116         {
    117           subsets[yroot].Parent = xroot;
    118           subsets[xroot].Rank++;
    119         }
    120       }
    121 
    122       public bool HasCycle()
    123       {
    124         Subset[] subsets = new Subset[VertexCount];
    125         for (int i = 0; i < subsets.Length; i++)
    126         {
    127           subsets[i] = new Subset();
    128           subsets[i].Parent = i;
    129           subsets[i].Rank = 0;
    130         }
    131 
    132         // Iterate through all edges of graph, find subset of both
    133         // vertices of every edge, if both subsets are same, 
    134         // then there is cycle in graph.
    135         foreach (var edge in this.Edges)
    136         {
    137           int x = Find(subsets, edge.Begin);
    138           int y = Find(subsets, edge.End);
    139 
    140           if (x == y)
    141           {
    142             return true;
    143           }
    144 
    145           Union(subsets, x, y);
    146         }
    147 
    148         return false;
    149       }
    150     }
    151   }
    152 }

    本篇文章《不相交集合森林的启发式策略》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。

  • 相关阅读:
    什么是em?
    数据结构与算法:快速排序
    flex中flexshrink的理解
    前端的padding是参照父元素的宽度还是高度?
    子元素的border不能通过百分比设置的
    数据库去重的简易方法
    windows2003 iis6.0站点打不开,找不到服务器或 DNS 错误。
    常用WebServices返回数据的4种方法比较
    手机身份证IP地址开放接口(很实用哦)
    从创业失败中学到的七条教训
  • 原文地址:https://www.cnblogs.com/gaochundong/p/disjoint_set_forests_heuristics.html
Copyright © 2011-2022 走看看