讲算法前扫扫盲,简单说说几个机组(高富帅飞行员和白富美空姐)的概念。
- 什么是散段(航段)?:是指一次没有停顿的飞行(如广州-北京)。
- 什么是任务串?:任务串又叫做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 }