使用并查集算法判断任务串之间的关系

讲算法前扫扫盲,简单说说几个机组(高富帅飞行员和白富美空姐)的概念。

  • 什么是散段(航段)?:是指一次没有停顿的飞行(如广州-北京)。
  • 什么是任务串?:任务串又叫做Pairing,是一个从基地出发最后回到基地的航班序列。

打个比方,飞行员从广州基地出发,去北京,然后上海。。。。最后飞到广州的这个任务序列,我们把它叫说这个飞行员的任务串。

  • 什么是搭班?:理解了上面两个概念再理解搭班就容易了,搭班就是将航段首尾相接形成一个任务串。即从散段到任务串
  • 什么是排班?:搭好班的任务串只是形成一个概念,还没有具体执行的人,把具体的人(飞行员,乘务员等)安排到任务串上,就叫排班。
  • 什么是定员?:由于民航局对每一种机型或者有些特殊航线上要求的机组成员人数要求不同,我们把这种设定的人数,就叫说该机型的定员。比如飞新疆的航班安全员就比飞广州的安全员要多,至于这是为什么,你懂的。

这次主要是讲任务串的概念,从系统设计上来说,每一个任务串,都有一个任务串号,作为该任务串的标识,串号就相当于Pairing的ID。任务串一般是在航班飞行的前十天就已经搭好。如果正常情况的话,机组按照预定计划飞就可以了。

但理想是总是丰满的,现实往往都是那样的残酷。即使在航班飞行前十天内,航班还是有可能有大的变动,比如突然航班突然取消,或者航班的机型变更了等等。这些,都会影响到任务串的变化。任务串的变化也往往是带着任务串号的变化,有时会从一个任务串变化形成另外一个任务串,或者split成多个任务串。

比如有任务串号:A,B,C,D,E, F, H, I, J。他之间的关系是A->B,C->D,E->F,F->H,B->C,I->J。这种情况很常见,我们很容易知道A和B任务串有关,C和D任务串有关。但A与C是否有关,D与A是否有关等等,这时候要判断他们之间的关系就有点复杂了。

    我们把多个类似于这样的关系的任务串转换到数学集合上。有以下集合。

{D,A},{C,B}, {E,C},{H,F}, {B,D}, {I,J}

为找出任务串关系,我们希望能把上面的小集合,转换成以下集合。

{A,B,C,D,E},{F,H},{I,J}

形成以上集合后,只要知道集合中任意一任务串,就很容易把与该任务串相关的所有任务串都找到。同理判断两任务串是否相关,只需要判断这两个任务串是否在同一集合中。

 

从业务问题转换到了数学问题,为了完成以上集合的转换,我们分四步走。

1. 给Ⅰ中的集合编号,0,1,2,3,4,5

2. 生成一个map,key值为串号,value值为该串的集合编号

A:    0

B:    1, 4(表示B串号,在集合0,4中都存在)

C:    1, 2

D:    0, 4

E:     2

F:     3

H:    3

I:     5

J:     5

3. 创建一个长数为6的数组array,表示集合的对应关系array[i]=i(即初始化该值的root为自身)。再根据2中的对应关系处理数组。

遍历2中的map,修改array中的值

A    0, 1, 2, 3, 4, 5

B    0, 1, 2, 3, 1, 5(B在1,4集合存在)

C    0, 1, 1, 3, 1, 5(C在1,2集合存在)

D   0, 0, 1, 3, 1, 5(D在0,4集合存在,array[0]=0,array[4]=1把0设置为root值,因为array[4]=1,追溯到下标1,此时array[1]=1,因此把array[1]的root为array[0])

 

在遍历D之前,集合的关系,4的root为1

 

由于D在0,4集合中存在,所以他们关系如图。

 

遍历D之后,调整root关系如上图

E   0, 0, 1, 3, 1, 5

F    0, 0, 1, 3, 1, 5

H   0, 0, 1, 3, 1, 5

I    0, 0, 1, 3, 1, 5

J    0, 0, 1, 3, 1, 5

4. 合并,将array数组中,根据root关系合并集合:

如:0,1,2,4集合合并{A, B, C, D, E}

     3集合{F, H }

     5集合{I, J}

以上集合就是我们需要的结果Ⅱ,以上使用的算法模式就是并查集算法,即把多个集合合并成没有交集的集合。

代码如下:

  1 import java.util.ArrayList;
  2 import java.util.Arrays;
  3 import java.util.HashMap;
  4 import java.util.HashSet;
  5 import java.util.Iterator;
  6 import java.util.List;
  7 import java.util.Map;
  8 import java.util.Set;
  9   
 10   
 11 public class DisjointSet {  
 12     private List<Integer> father;//the root in disjion set.  
 13 
 14     public static void main(String[] args) { 
 15         String[] str0={  
 16                 "D",  
 17                 "A",  
 18                 };  
 19         String[] str1={  
 20                 "C",
 21                 "B",  
 22                 };  
 23         String[] str2={  
 24                 "E",
 25                 "C",  
 26                 };  
 27         String[] str3={  
 28                 "H",
 29                 "F"};  
 30         String[] str4={  
 31                 "B",  
 32                 "D",};  
 33         String[] str5={  
 34                 "I",
 35                 "J",  
 36                 };  
 37         String[][] strs={str0,str1,str2,str3,str4,str5}; 
 38         List<Set<String>> resultList = new ArrayList<Set<String>>();
 39         //change String[][] to List<Set>  
 40         for(String[] str:strs){  
 41             Set<String> set=new HashSet<String>();  
 42             set.addAll(Arrays.asList(str));  
 43             resultList.add(set);  
 44         }  
 45         DisjointSet disjointSet=new DisjointSet();  
 46         resultList = disjointSet.disjoin(resultList);  
 47         System.out.println(resultList);
 48     }  
 49     /**
 50      * 并查集算法,将有联系的任务串合并到一个集合中
 51      * 
 52      */
 53 
 54     private List<Set<String>> disjoin(List<Set<String>> disjoinSetList){  
 55         if(disjoinSetList==null||disjoinSetList.size()<2)
 56             return null;  
 57         //Make set步骤,初始化父节点
 58         makeSet(disjoinSetList);  
 59         //第二步  生成一个map,key值为串号,value值为该串的集合编号
 60         Map<String,List<Integer>> map = storeInHashMap(disjoinSetList); 
 61         //查找各集合之间的root关系
 62         findSetAll(map);
 63         //合并
 64         List<Set<String>> result = union(disjoinSetList);
 65         return result;  
 66     }  
 67       
 68     //初始化父节点,初始化父节点为本身 
 69     private void makeSet(List<Set<String>> disjoinSetList){  
 70         father=new ArrayList<Integer>();  
 71         for(int i=0;i<disjoinSetList.size();i++){  
 72             father.add(i);
 73         }  
 74     }  
 75       
 76     /**
 77      * 找出每个元素所在的集合,保存在map中
 78      * 如: B : 1,4
 79      */  
 80     private Map<String,List<Integer>> storeInHashMap(List<Set<String>> disjoinSetList){  
 81         Map<String,List<Integer>> map=new HashMap<String,List<Integer>>();  
 82         for(int i=0;i<disjoinSetList.size();i++){  
 83             for(String each : disjoinSetList.get(i)){  
 84                 if(!map.containsKey(each)){  
 85                     List<Integer> list=new ArrayList<Integer>();  
 86                     list.add(i);  
 87                     map.put(each, list);  
 88                 }else{  
 89                     map.get(each).add(i);  
 90                 }  
 91             }  
 92         }  
 93         return map;  
 94     } 
 95     
 96     /**
 97      * 合并
 98      * 
 99      */  
100     private List<Set<String>> union(List<Set<String>> disjoinSetList){ 
101         List<Set<String>> resultList=disjoinSetList;
102         //merge two sets  
103         for(int i=0;i<disjoinSetList.size();i++){  
104             if(i!=father.get(i)){  
105                 Set<String> dest=resultList.get(getFather(i)); //找到最终的祖先
106                 Set<String> source=resultList.get(i);  
107                 dest.addAll(source);  
108             }  
109         }  
110         //clear a set which has been added.  
111         List<Set<String>> afterList= new ArrayList<Set<String>>();
112         for(int i=0;i<disjoinSetList.size();i++){  
113             if(i==father.get(i)){  
114                 afterList.add(resultList.get(i));
115             }  
116         }
117         
118         return afterList;
119     }
120     //遍历map,找到root修改array中的值
121     private void findSetAll(Map<String,List<Integer>> map){
122         Iterator<Map.Entry<String, List<Integer>>> it=map.entrySet().iterator();  
123         while(it.hasNext()){  
124             Map.Entry<String, List<Integer>> entry=it.next();  
125             List<Integer> value=entry.getValue();  
126             findSet(value);//the arrays whose indexes are in the same list should be merged to one set.  
127         }  
128     }
129     private void findSet(List<Integer> list){  
130         int root=getFather(list.get(0));//查找list.get(0)根节点  
131         for(int i=1,size=list.size();i<size;i++){
132           //将list.get(i)的根节点,设为list.get(0)根节点,即把list.get(0)当作共同的根节点
133             father.set(getFather(list.get(i)), root);
134         }  
135     }  
136     //查找共同的祖先
137     private int getFather(int x){  
138         if(x!=father.get(x)){ 
139             x=getFather(father.get(x));  
140         }  
141         return x;  
142     }  
143       
144 }  
DisjointSet

 

 

posted on 2013-09-07 16:00  飞凡_gz  阅读(451)  评论(0编辑  收藏  举报

导航