BUAA OO UNIT3 社交网络单元总结
BUAA OO UNIT3 社交网络单元总结
规格实现
以翻译JML为主,对其中影响性能或不方便直接实现的方法做优化处理。
hw9
isCircle()
:用并查集实现isLinked()
:用独立的静态类维护边表,再通过调用其查询方法实现
hw10
queryGroupValueSum()
:直接翻译JML是\(O(n^2)\)的,因此在加人、删人、加边时分别维护queryGroupAgeVar()
:显然可以用\([(\sum x^2)-2E(x)(\sum x)+nE^2(x)]/ n\)实现
hw11
sendIndirectMessage()
:堆优化Dijkstra板子
基于JML的设计与测试
JML只描述方法执行前后的状态,而不关心过程。因此面向JML编程只需保证前后状态符合描述即可。
显然单元测试更符合JML本来的目的。相比于黑盒测试,单元测试解耦程度更高,测试颗粒度更低,更有利于bug定位。但单元测试也有问题,就是模拟软件真实运行时的上下文通常并不容易。相比之下,黑盒测试自由度更高,模拟真实场景更方便,但同样难以保证覆盖软件的全部运行状态。
我在作业中实际采用的做法是黑盒测试+对拍。整个过程分两阶段。第一阶段大家的代码都存在bug,通过对拍发现问题后分头修复,不断重复这个过程直到不出现明显bug。第二阶段是巨量测试,把电脑放寝室里一直跑,如果不出现新bug那么就有把握通过强测。
hw9
数据构造:主要卡并查集实现,数据生成器倾向于生成一两条长链
阶段二测试点总量:2k+
hw10
数据构造:纯随机生成,异常数量报表,主要用同学的生成器
阶段二测试点总量:1w+
hw11
数据构造:主要卡堆优化Dijkstra,生成10个接近团的稠密图,然后用少量边连接起来。同时使用了同学提供的其他指令的生成器
阶段二测试点总量:8k+
容器选用
遇事不决,上hashMap,准没错
由于存在id这种东西,所以很多容器可以直接设为Map<Integer, Integer>mp = new hashMap<>();
的形式。
hw9
并查集fa[]
数组用hashMap实现
边表用hashMap套hashMap实现
hw10
receivedMessages:由于需要支持头插,而且懒得用ArrayList实现栈,于是就用链表实现了
hw11
Dijkstra:显然要用priorityQueue做堆优化,还要用hashMap实现dis[]数组,同时还使用了hashSet维护了已计算完毕的点有哪些。
性能优化
维护嘛,平摊嘛,把\(O(n^2)\)的东西维护掉就好了
hw9
并查集:路经压缩+按秩合并
hw10
receivedMessage:链表实现,头插效率比ArryList高
QueryGroupValueSum:加人删人的时候\(O(n)\)维护sum,加边时\(O(1)\)维护sum。当然有些同学遍历邻接表也是可以的,因为总边数是\(O(n)\)的,而计算时每条边只会算两次。
hw11
最短路:堆优化Dijkstra板子。居然没看到有人用spfa,菊花图没用了,略失望
架构设计
hw9
独立的边表维护、查询静态类,可以算单例模式?
hw10
照抄JML,略
hw11
Dijkstra放在静态类中实现,简化了sendIndirectMessage()
啥,太短了???
你看这个类图,它又大又圆
hw9
![hw9diagram](https://img2020.cnblogs.com/blog/2285223/202105/2285223-20210528193010314-1313434425.png)
hw10
hw11
你看这个复杂度表,它又长又方
hw9
Method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
Ei.add(int,int,int) | 3 | 2 | 3 | 4 |
Ei.be(int) | 0 | 1 | 1 | 1 |
Ei.be(int,int) | 2 | 2 | 2 | 3 |
Ei.lst(int) | 1 | 1 | 1 | 2 |
Ei.v(int,int) | 1 | 1 | 2 | 2 |
EpiE.EpiE(int) | 0 | 1 | 1 | 1 |
EpiE.print() | 0 | 1 | 1 | 1 |
ErE.ErE(int,int) | 1 | 1 | 2 | 2 |
ErE.print() | 0 | 1 | 1 | 1 |
Main.main(String[]) | 0 | 1 | 1 | 1 |
Net.Net() | 0 | 1 | 1 | 1 |
Net.addPerson(Person) | 1 | 2 | 2 | 2 |
Net.addRelation(int,int,int) | 3 | 4 | 3 | 4 |
Net.compareName(int,int) | 2 | 3 | 2 | 3 |
Net.contains(int) | 0 | 1 | 1 | 1 |
Net.find(int) | 2 | 1 | 2 | 2 |
Net.getPerson(int) | 0 | 1 | 1 | 1 |
Net.isCircle(int,int) | 2 | 3 | 2 | 3 |
Net.mrg(int,int) | 5 | 2 | 3 | 4 |
Net.queryBlockSum() | 0 | 1 | 1 | 1 |
Net.queryNameRank(int) | 4 | 2 | 2 | 4 |
Net.queryPeopleSum() | 0 | 1 | 1 | 1 |
Net.queryValue(int,int) | 3 | 4 | 3 | 4 |
PinfE.PinfE(int) | 0 | 1 | 1 | 1 |
PinfE.print() | 0 | 1 | 1 | 1 |
Psn.Psn(int,String,int) | 0 | 1 | 1 | 1 |
Psn.compareTo(Person) | 0 | 1 | 1 | 1 |
Psn.equals(Object) | 1 | 2 | 1 | 2 |
Psn.getAge() | 0 | 1 | 1 | 1 |
Psn.getId() | 0 | 1 | 1 | 1 |
Psn.getName() | 0 | 1 | 1 | 1 |
Psn.isLinked(Person) | 1 | 1 | 2 | 2 |
Psn.queryValue(Person) | 0 | 1 | 1 | 1 |
RnfE.RnfE(int,int) | 1 | 1 | 2 | 2 |
RnfE.print() | 0 | 1 | 1 | 1 |
Class | OCavg | OCmax | WMC | |
Ei | 2.2 | 4 | 11 | |
EpiE | 1 | 1 | 2 | |
ErE | 1.5 | 2 | 3 | |
Main | 1 | 1 | 1 | |
Net | 2.38 | 4 | 31 | |
PinfE | 1 | 1 | 2 | |
Psn | 1.12 | 2 | 9 | |
RnfE | 1.5 | 2 | 3 |
hw10
Method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
ntw.Grp.Grp(int) | 0 | 1 | 1 | 1 |
ntw.Grp.addPerson(Person) | 0 | 1 | 1 | 1 |
ntw.Grp.addRela(int,int,int) | 2 | 1 | 2 | 3 |
ntw.Grp.delPerson(Person) | 1 | 2 | 1 | 2 |
ntw.Grp.equals(Object) | 1 | 1 | 2 | 2 |
ntw.Grp.getAgeMean() | 1 | 1 | 2 | 2 |
ntw.Grp.getAgeVar() | 1 | 1 | 2 | 2 |
ntw.Grp.getId() | 0 | 1 | 1 | 1 |
ntw.Grp.getSize() | 0 | 1 | 1 | 1 |
ntw.Grp.getValueSum() | 0 | 1 | 1 | 1 |
ntw.Grp.hasPerson(Person) | 0 | 1 | 1 | 1 |
ntw.Grp.peo() | 0 | 1 | 1 | 1 |
ntw.Main.main(String[]) | 0 | 1 | 1 | 1 |
ntw.Msg.Msg(int,int,Person,Group) | 0 | 1 | 1 | 1 |
ntw.Msg.Msg(int,int,Person,Person) | 0 | 1 | 1 | 1 |
ntw.Msg.equals(Object) | 1 | 1 | 2 | 2 |
ntw.Msg.getGroup() | 0 | 1 | 1 | 1 |
ntw.Msg.getId() | 0 | 1 | 1 | 1 |
ntw.Msg.getPerson1() | 0 | 1 | 1 | 1 |
ntw.Msg.getPerson2() | 0 | 1 | 1 | 1 |
ntw.Msg.getSocialValue() | 0 | 1 | 1 | 1 |
ntw.Msg.getType() | 0 | 1 | 1 | 1 |
ntw.Msg.toString() | 0 | 1 | 1 | 1 |
ntw.Net.addGroup(Group) | 1 | 2 | 2 | 2 |
ntw.Net.addMessage(Message) | 3 | 3 | 4 | 4 |
ntw.Net.addPerson(Person) | 1 | 2 | 2 | 2 |
ntw.Net.addRelation(int,int,int) | 4 | 4 | 4 | 5 |
ntw.Net.addToGroup(int,int) | 4 | 4 | 4 | 5 |
ntw.Net.cmpNm(int,int) | 0 | 1 | 1 | 1 |
ntw.Net.compareName(int,int) | 2 | 3 | 2 | 3 |
ntw.Net.contains(int) | 0 | 1 | 1 | 1 |
ntw.Net.containsMessage(int) | 0 | 1 | 1 | 1 |
ntw.Net.delFromGroup(int,int) | 3 | 4 | 3 | 4 |
ntw.Net.find(int) | 2 | 1 | 2 | 2 |
ntw.Net.getGroup(int) | 0 | 1 | 1 | 1 |
ntw.Net.getMessage(int) | 0 | 1 | 1 | 1 |
ntw.Net.getPerson(int) | 0 | 1 | 1 | 1 |
ntw.Net.isCircle(int,int) | 2 | 3 | 2 | 3 |
ntw.Net.mrg(int,int) | 5 | 2 | 3 | 4 |
ntw.Net.queryBlockSum() | 0 | 1 | 1 | 1 |
ntw.Net.queryGroupAgeMean(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryGroupAgeVar(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryGroupPeopleSum(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryGroupSum() | 0 | 1 | 1 | 1 |
ntw.Net.queryGroupValueSum(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryNameRank(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryPeopleSum() | 0 | 1 | 1 | 1 |
ntw.Net.queryReceivedMessages(int) | 1 | 2 | 1 | 2 |
ntw.Net.querySocialValue(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryValue(int,int) | 4 | 4 | 4 | 5 |
ntw.Net.sendMessage(int) | 7 | 4 | 8 | 8 |
ntw.Psn.Psn(int,String,int) | 0 | 1 | 1 | 1 |
ntw.Psn.addSocialValue(int) | 0 | 1 | 1 | 1 |
ntw.Psn.compareTo(Person) | 0 | 1 | 1 | 1 |
ntw.Psn.equals(Object) | 1 | 1 | 2 | 2 |
ntw.Psn.getAge() | 0 | 1 | 1 | 1 |
ntw.Psn.getId() | 0 | 1 | 1 | 1 |
ntw.Psn.getMessages() | 0 | 1 | 1 | 1 |
ntw.Psn.getName() | 0 | 1 | 1 | 1 |
ntw.Psn.getReceivedMessages() | 2 | 1 | 3 | 3 |
ntw.Psn.getSocialValue() | 0 | 1 | 1 | 1 |
ntw.Psn.isLinked(Person) | 1 | 1 | 2 | 2 |
ntw.Psn.queryValue(Person) | 0 | 1 | 1 | 1 |
ntw.Psn.toString() | 0 | 1 | 1 | 1 |
ntw.Re.add(int,int,int) | 3 | 2 | 3 | 4 |
ntw.Re.be(int) | 0 | 1 | 1 | 1 |
ntw.Re.be(int,int) | 1 | 1 | 3 | 3 |
ntw.Re.lst(int) | 1 | 1 | 1 | 2 |
ntw.Re.v(int,int) | 1 | 1 | 2 | 2 |
Class | OCavg | OCmax | WMC | |
ntw.Eegi | 1 | 1 | 2 | |
ntw.Eemi | 1 | 1 | 2 | |
ntw.Eepi | 1 | 1 | 2 | |
ntw.Eer | 1.5 | 2 | 3 | |
ntw.Eginf | 1 | 1 | 2 | |
ntw.Eminf | 1 | 1 | 2 | |
ntw.Epinf | 1 | 1 | 2 | |
ntw.Ernf | 1.5 | 2 | 3 | |
ntw.Grp | 1.33 | 2 | 16 | |
ntw.Main | 1 | 1 | 1 | |
ntw.Msg | 1 | 1 | 10 |
hw11
Method | CogC | ev(G) | iv(G) | v(G) |
---|---|---|---|---|
ntw.Grp.Grp(int) | 0 | 1 | 1 | 1 |
ntw.Grp.addPerson(Person) | 0 | 1 | 1 | 1 |
ntw.Grp.addRela(int,int,int) | 2 | 1 | 2 | 3 |
ntw.Grp.delPerson(Person) | 1 | 2 | 1 | 2 |
ntw.Grp.equals(Object) | 1 | 1 | 2 | 2 |
ntw.Grp.getAgeMean() | 1 | 1 | 2 | 2 |
ntw.Grp.getAgeVar() | 1 | 1 | 2 | 2 |
ntw.Grp.getId() | 0 | 1 | 1 | 1 |
ntw.Grp.getSize() | 0 | 1 | 1 | 1 |
ntw.Grp.getValueSum() | 0 | 1 | 1 | 1 |
ntw.Grp.hasPerson(Person) | 0 | 1 | 1 | 1 |
ntw.Grp.peo() | 0 | 1 | 1 | 1 |
ntw.Main.main(String[]) | 0 | 1 | 1 | 1 |
ntw.Msg.Msg(int,int,Person,Group) | 0 | 1 | 1 | 1 |
ntw.Msg.Msg(int,int,Person,Person) | 0 | 1 | 1 | 1 |
ntw.Msg.equals(Object) | 1 | 1 | 2 | 2 |
ntw.Msg.getGroup() | 0 | 1 | 1 | 1 |
ntw.Msg.getId() | 0 | 1 | 1 | 1 |
ntw.Msg.getPerson1() | 0 | 1 | 1 | 1 |
ntw.Msg.getPerson2() | 0 | 1 | 1 | 1 |
ntw.Msg.getSocialValue() | 0 | 1 | 1 | 1 |
ntw.Msg.getType() | 0 | 1 | 1 | 1 |
ntw.Msg.toString() | 0 | 1 | 1 | 1 |
ntw.MsgEM.MsgEM(int,int,Person,Group) | 0 | 1 | 1 | 1 |
ntw.MsgEM.MsgEM(int,int,Person,Person) | 0 | 1 | 1 | 1 |
ntw.MsgEM.equals(Object) | 1 | 1 | 2 | 2 |
ntw.MsgEM.getEmojiId() | 0 | 1 | 1 | 1 |
ntw.MsgEM.getGroup() | 0 | 1 | 1 | 1 |
ntw.MsgEM.getId() | 0 | 1 | 1 | 1 |
ntw.MsgEM.getPerson1() | 0 | 1 | 1 | 1 |
ntw.MsgEM.getPerson2() | 0 | 1 | 1 | 1 |
ntw.MsgEM.getSocialValue() | 0 | 1 | 1 | 1 |
ntw.MsgEM.getType() | 0 | 1 | 1 | 1 |
ntw.MsgEM.toString() | 0 | 1 | 1 | 1 |
ntw.MsgNT.MsgNT(int,String,Person,Group) | 0 | 1 | 1 | 1 |
ntw.MsgNT.MsgNT(int,String,Person,Person) | 0 | 1 | 1 | 1 |
ntw.MsgNT.equals(Object) | 1 | 1 | 2 | 2 |
ntw.MsgNT.getGroup() | 0 | 1 | 1 | 1 |
ntw.MsgNT.getId() | 0 | 1 | 1 | 1 |
ntw.MsgNT.getPerson1() | 0 | 1 | 1 | 1 |
ntw.MsgNT.getPerson2() | 0 | 1 | 1 | 1 |
ntw.MsgNT.getSocialValue() | 0 | 1 | 1 | 1 |
ntw.MsgNT.getString() | 0 | 1 | 1 | 1 |
ntw.MsgNT.getType() | 0 | 1 | 1 | 1 |
ntw.MsgNT.toString() | 0 | 1 | 1 | 1 |
ntw.MsgRE.MsgRE(int,int,Person,Group) | 0 | 1 | 1 | 1 |
ntw.MsgRE.MsgRE(int,int,Person,Person) | 0 | 1 | 1 | 1 |
ntw.MsgRE.equals(Object) | 1 | 1 | 2 | 2 |
ntw.MsgRE.getGroup() | 0 | 1 | 1 | 1 |
ntw.MsgRE.getId() | 0 | 1 | 1 | 1 |
ntw.MsgRE.getMoney() | 0 | 1 | 1 | 1 |
ntw.MsgRE.getPerson1() | 0 | 1 | 1 | 1 |
ntw.MsgRE.getPerson2() | 0 | 1 | 1 | 1 |
ntw.MsgRE.getSocialValue() | 0 | 1 | 1 | 1 |
ntw.MsgRE.getType() | 0 | 1 | 1 | 1 |
ntw.MsgRE.toString() | 0 | 1 | 1 | 1 |
ntw.Net.addGroup(Group) | 1 | 2 | 2 | 2 |
ntw.Net.addMessage(Message) | 5 | 4 | 6 | 6 |
ntw.Net.addPerson(Person) | 1 | 2 | 2 | 2 |
ntw.Net.addRelation(int,int,int) | 4 | 4 | 4 | 5 |
ntw.Net.addToGroup(int,int) | 4 | 4 | 4 | 5 |
ntw.Net.cmpNm(int,int) | 0 | 1 | 1 | 1 |
ntw.Net.compareName(int,int) | 2 | 3 | 2 | 3 |
ntw.Net.contains(int) | 0 | 1 | 1 | 1 |
ntw.Net.containsEmojiId(int) | 0 | 1 | 1 | 1 |
ntw.Net.containsMessage(int) | 0 | 1 | 1 | 1 |
ntw.Net.delFromGroup(int,int) | 3 | 4 | 3 | 4 |
ntw.Net.deleteColdEmoji(int) | 1 | 1 | 2 | 2 |
ntw.Net.find(int) | 2 | 1 | 2 | 2 |
ntw.Net.getGroup(int) | 0 | 1 | 1 | 1 |
ntw.Net.getMessage(int) | 0 | 1 | 1 | 1 |
ntw.Net.getPerson(int) | 0 | 1 | 1 | 1 |
ntw.Net.isCircle(int,int) | 2 | 3 | 2 | 3 |
ntw.Net.linked(int,int) | 0 | 1 | 1 | 1 |
ntw.Net.mrg(int,int) | 5 | 2 | 3 | 4 |
ntw.Net.queryBlockSum() | 0 | 1 | 1 | 1 |
ntw.Net.queryGroupAgeMean(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryGroupAgeVar(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryGroupPeopleSum(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryGroupSum() | 0 | 1 | 1 | 1 |
ntw.Net.queryGroupValueSum(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryMoney(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryNameRank(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryPeopleSum() | 0 | 1 | 1 | 1 |
ntw.Net.queryPopularity(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryReceivedMessages(int) | 1 | 2 | 1 | 2 |
ntw.Net.querySocialValue(int) | 1 | 2 | 1 | 2 |
ntw.Net.queryValue(int,int) | 4 | 4 | 4 | 5 |
ntw.Net.sendIndirectMessage(int) | 3 | 3 | 2 | 4 |
ntw.Net.sendMessage(int) | 10 | 4 | 10 | 10 |
ntw.Net.sendT1(Message) | 2 | 1 | 3 | 3 |
ntw.Net.storeEmojiId(int) | 1 | 2 | 1 | 2 |
ntw.Psn.Psn(int,String,int) | 0 | 1 | 1 | 1 |
ntw.Psn.addMoney(int) | 0 | 1 | 1 | 1 |
ntw.Psn.addSocialValue(int) | 0 | 1 | 1 | 1 |
ntw.Psn.compareTo(Person) | 0 | 1 | 1 | 1 |
ntw.Psn.equals(Object) | 1 | 1 | 2 | 2 |
ntw.Psn.getAge() | 0 | 1 | 1 | 1 |
你看这个代码,它朴实无华
hw9
并查集:路经压缩+按秩合并
private final Map<Integer, Integer> fa = new HashMap<>();
private final Map<Integer, Integer> frnk = new HashMap<>();
private int blk = 0;
private int find(int x) {
return fa.get(x) == x ? x : fa.merge(x, find(fa.get(x)), (a, b) -> b);
}
private boolean mrg(int xx, int yy) {
int x = find(xx);
int y = find(yy);
if (x == y) {
return false;
}
if (frnk.get(x) < frnk.get(y)) {
fa.merge(x, y, (a, b) -> b);
} else {
fa.merge(y, x, (a, b) -> b);
if (frnk.get(x).equals(frnk.get(y))) {
frnk.merge(x, 1, Integer::sum);
}
}
--blk;
return true;
}
hw10
维护边权和:加人、减人、加边时维护
public void addPerson(Person person) {
peo.add(person.getId());
sum += person.getAge();
sum2 += person.getAge() * person.getAge();
peo.forEach((p) -> sume += Re.v(p, person.getId()));
}
public void delPerson(Person person) {
if (!peo.contains(person.getId())) {
return;
}
sum -= person.getAge();
sum2 -= person.getAge() * person.getAge();
peo.remove(person.getId());
peo.forEach((p) -> sume -= Re.v(p, person.getId()));
}
public void addRela(int a, int b, int v) {
if (peo.contains(a) && peo.contains(b)) {
sume += v;
}
}
public int getValueSum() {
return sume << 1;
}
hw11
堆优化Dijkstra:
private static class Nd implements Comparable<Nd> {
private final int id;
private final int dis;
public Nd(int id, int dis) {
this.id = id;
this.dis = dis;
}
@Override
public int compareTo(Nd o) {
return Integer.compare(dis, o.dis);
}
public int i() {
return id;
}
}
public static int dij(int u, int v) {
Queue<Nd> q = new PriorityQueue<>();
Map<Integer, Integer> dis = new HashMap<>();
Set<Integer> in = new HashSet<>();
dis.put(u, 0);
q.offer(new Nd(u, 0));
while (!q.isEmpty()) {
int s = q.poll().i();
if (in.contains(s)) {
continue;
}
in.add(s);
Map<Integer, Integer> es = lst(s);
int sval = dis.get(s);
es.forEach((t, val) -> {
int tval = sval + val;
if (dis.getOrDefault(t, INF) > tval) {
dis.put(t, tval);
q.offer(new Nd(t, tval));
}
});
}
return dis.getOrDefault(v, INF);
}