OO第三单元总结
作者:互联网
OO第三单元总结
架构设计
本单元主要任务为阅读JML的属性和方法规格,来模拟一个社交网络模型,通过三次迭代,实现简单社交关系的模拟和查询、群组和消息功能以及社交关系系统中不同消息类型和操作。
由于是在JML规格的基础上编写代码,架构设计基本仿照代码模板进行设计,相比前两单元较为简单,除了独立出少数工具类以简化代码之外,其他部分按照JML实现接口即可。
图模型构建和维护策略
此题本质上将Network视为一张图,person为点,relation为边,各个方法即为建立、查询、维护图的方法。代码的关键也在于Network类的方法中
hw9:并查集维护连通图
本次作业的isCircle
和queryBlockSum
方法利用并查集维护较为简单,同时也具备性能上的优势。
//最朴素的并查集算法
int get(int x) {
if(fa[x] == x) {
return x;
}
return get(fa[x]);
}
void merge(int x, int y) {
x = get(x);
y = get(y);
if(x != y) {
fa[y] = x;
}
}
hw10:kruskal维护最小生成树
本题中,queryLeastConnection
这个函数用于Network图中的最小生成树,由于上次作业中实现了并查集,故使用依赖于并查集的kruskal算法更为合适。
Kruskal 算法
Kruskal 算法是最小生成树算法的一种。算法过程如下:
首先我们定义带权无向图G的边集合为E,接着我们再定义最小生成树的边集为T,初始集合T为空。接着执行以下操作:
-
首先,我们把图G看成一个有N-1棵树的森林,图上每个顶点对应一棵树;
-
接着,我们将边集E的每条边,按权值从小到大进行排序;
-
按边权从小到大的顺序遍历每条边e=(u,v) ,我们记顶点u所在的树为Tu,顶点v所在的树为Tv,如果Tu和Tv不是同一棵树,则我们将边e加入集合T并将两棵树Tu和Tv进行合并;否则不进行任何操作。
算法执行完毕后,如果集合T包含N-1条边,则T就代表最小生成树中的所有边。
第三步操作需要对集合进行操作,用并查集来维护。
int kruskal(int n, int m) {
int sum = 0;
for(int i = 1; i <= n; i++) {
fa[i] = i;
}
sort(E, E + m, cmp);
for(int i = 0; i < m; i++) {
int fu = get(E[i].u);
int fv = get(E[i].v);
if(fu != fv) {
fa[fv] = fu;
sum += E[i].len;
}
}
return sum;
}
hw11:dijkstra维护最短路
sendIndirectMessage
方法所求的是两点之间的最短路。利用dijkstra算法处理较为合适。
void dijkstra(int u) {
memset(vis, false, sizeof(vis));
memset(dis, 0x3f, sizeof(dis));
dis[u] = 0;
for(int i = 0; i < n; i++) {
int mind = 1000000000, minj = -1;
for(int j = 1; j <= n; j++) {
if(!vis[j] && dis[j] < mind) {
minj = j;
mind = dis[j];
}
}
if(minj == -1) {
return;
}
vis[minj] = true;
for(int j = head[minj]; ~j; j = e[j].next) {
int v = e[j].v;
int w = e[j].w;
if(!vis[v] && dis[v] > dis[minj] + w) {
dis[v] = dis[minj] + w;
}
}
}
}
性能问题
容器选择
JML并未规定类中数据存储容器的具体种类。因此可以视情况选择Arraylist或者Hashmap,以适应不同的查询、维护要求。
//MyNetwork类中所使用的容器
private HashMap<Integer, Person> people;
private HashMap<Integer, Group> groups;
private ArrayList<Message> messages;
private Dsu dsu;
private ArrayList<Integer> emojiIdList;
private ArrayList<Integer> emojiHeatList;
private HashMap<Integer, ArrayList<Message>> emoCod;
算法优化
并查集路径压缩
路径压缩的思想是,我们只关心每个结点所在集合的根结点,而并不太关心树的真正的结构是怎么样的。这样我们在一次查询的时候,可以直接把查询路径上的所有结点的fa[i]都赋值成为根结点。实现这一步只需要在我们之前的查询函数上面进行很 小的改动。
堆优化dijkstra
优先队列实现的堆优化dijkstra算法,能将时间复杂度降为O(nlogn),java中的PriorityQueue
结构使优化较为简便。
代码维护
在查询指令中,有一个trick是对于某些需要查询且随对数据结构的维护而发生变化的信息,可以用变量来储存,并且在建立维护关系时对其进行更新。比如联通块的数量就可以用qbs
来存储,并且在addPerson、addReletion的时候加以更新。
bug修复
hw9
第一次强测得分不高,主要问题在于测试不充分,一些特殊情况未做特殊处理,比如删除最后一个Person时程序会报错,之类的错误。
互测未出问题。
hw10
强测没有问题,但是互测被hack了两处,主要是性能方面的缺陷,比如Group类中的查询函数的for循环中出现了不必要的函数嵌套,导致时间过慢。
hw11
强测无问题,互测被hack的原因是出现了将emojiHeatList错写为emojiIdList的笔误,但可能此处代码对程序影响较小,自测和强测都未能测出来。
自测
自测采取了程序随机/定向生成数据+与同学对拍的方式,一方面使用随机数据对所有指令进行全面覆盖式测试,以减少BUG出现;另一方面针对时间复杂度较高的函数构造极端数据进行测试,寻求优化的可能。
Network扩展
题目要求
假设出现了几种不同的Person
-
Advertiser:持续向外发送产品广告
-
Producer:产品生产商,通过Advertiser来销售产品
-
Customer:消费者,会关注广告并选择和自己偏好匹配的产品来购买 -- 所谓购买,就是直接通过Advertiser给相应Producer发一个购买消息
-
Person:吃瓜群众,不发广告,不买东西,不卖东西
如此Network可以支持市场营销,并能查询某种商品的销售额和销售路径等 请讨论如何对Network扩展,给出相关接口方法,并选择3个核心业务功能的接口方法撰写JML规格(借鉴所总结的JML规格模式)
解答
建立Advertiser、Producer、Customer三种Person扩展类,以及Advertisement和Product两种Message扩展类。
发送广告:
/*
@ public normal_behavior
@ requires (\exists int i; 0 <= i && i < people.length; people[i].getId() == id && people[i] instanceof Advertiser);
@ ensures getPerson(id).advertisements.length == \old(getPerson(id).advertisements.length) - 1;
@ ensures (\forall int j; 0 <= j && j < people[i].messages.length; people[i].messages[j] == \old(people[i].messages[j])))) && (\forall int i; 0 <= i && i < people.length; !(getPerson(id).isLinked(people[i])) ==> (people[i].messages.length == \old(people[i].messages.length;
@ ensures (\forall int j; 1 <= j && j < people[i].messages.length; people[i].messages[j] == \old(people[i].messages[j - 1])))) && (\forall int i; 0 <= i && i < people.length; (getPerson(id).isLinked(people[i])) ==> (people[i].messages.length == \old(people[i].messages.length) + 1 && people[i].messages[0] == \old(getPerson(id).advertisements[0]);
*/
public void sendAdvertisement(int id);
查询销售量:
/*
@ public normal_behavior
@ ensures \result == numbers;
@*/
*/
public void getSalingNumber();
统计销售量:
/*
@ public normal_behavior
@ assignable numbers
@ ensures numbers == \old(numbers) + 1;
*/
public void addSalingNumber();
学习体会
本单元的学习,不太push的同时也收获满满;不仅学习掌握了面对JML规格书写代码的能力,同时复习拓展了数据结构相关知识,熟悉了利用JAVA写图论算法,其包含的容器不可谓不强大。
同时,与同学共同对拍,共享测试数据,也是一次良好的体验。
标签:OO,总结,minj,int,private,查询,算法,单元,dis 来源: https://www.cnblogs.com/taosuozi/p/16341216.html