BUAA OO 第三单元总结
设计策略
第九次作业中比较重要的方法有两个,Network中的isCircle和queryBlockSum。该社交网络实际上是无向图的模型,阅读二者规格后发现两个方法分别是判断两点是否连通以及查询连通分量的个数。为了降低时间复杂度,我采用了并查集的方法,包含三个函数,如下:
blockSum = 0; // 连通分量初始化为0
CreatSet(x) {
x.parent = x;
x.height = 0;
blockSum++;
}
FindSet(x) {
while (x.parent != x)
x = x.parent;
return x;
}
UnionSet(x, y) {
a = FindSet(x);
b = FindSet(y);
if (a.height <= b.height) {
if (a.height == b.height) {
b.height = b.height + 1;
}
a.parent = b;
} else {
b.parent = a;
}
blockSum--;
}
各点的parent以及blockSum需要在addPerson以及addRelation中通过调用并查集的三个方法进行维护。这样一来,查询两点是否连通只需判断二者是否在同一个集合中(通过FindSet),查询连通分量的个数只需返回blockSum。
第十次作业
第十次作业新增了group类和message类,其中新增的重要方法是Group类中的getAgeVar、getAgeMean和getValueSum分别是查询group中的平均年龄、年龄的方差以及Group中边权值的总和×2,介于规格中规定的返回值均为整数,小数部分被舍去,所以要注意实现方法,要保持与规格相统一。
如果每次查询时计算,复杂度较高,所以我采用每次更新时迭代计算,查询时返回的策略。这样在更新少于查询时,或在更新并不明显多于查询时会获得较好的时间复杂度,测试规定的数据限制就在这个范围内,因而我采用了更新时迭代计算。
介于getAgeVar以及agetAgeMean返回的并不是精确值,所以不能像数学公式那样迭代计算平均值及方差。但是可以迭代计算年龄总和(没有误差),然后由年龄总和得到平均值,而方差则需要重新计算。这两个值只需要在Group类中的addToGroup以及delFromGroup方法内进行维护即可。注:注意除0的问题!
对于getValueSum方法,因为Group中边权值×2计算出的就是整数,所以不会出现误差的情况,更新时可以迭代计算。对于ValueSum的维护要细心,除了在Group类中的addToGroup以及delFromGroup方法中维护,还要在NetWork类中的addRelation方法中进行维护。
第十一次作业
第十一次作业新增了几个Message的子类,新增了重要方法是NetWork中的sendIndirectMessage。阅读规格发现,该方法在正常情况下(二者连通)要返回两个点的最短距离。
我采用的是优先队列实现的Dijkstra算法,优先队列可用java的容器prorityQueue,节点可用java的Pair类,至于算法离散2也讲解过,网上也有相应代码就不赘述了。
测试的方法和策略
我采用的测试方法有两步,先通过JML规格对一些较复杂的方法单独测试,然后手动构建样例对整体进行测试,这样会测试会相对比较全面些,也较好实现。
本单元作业的中测与强测的强度差距较大,所以即使过了中测,也可能有很明显的BUG,本地测试十分重要。在单独对方法进行测试后,并不一定保证没有BUG,因为即使单个方法的返回值是正确的,但是这个过程中他可能改写了某些不该改写的数据值,导致在调用其它方法时出错。所以还是要构造样例来总体测试,我对每次作业会手动构造两组设计过的数据来进行测试。
本单元作业有部分同学因为时间复杂度过高导致CPU_TIME超时,我在实现规格时考虑到了这点,所以才采用的实现方法的时间复杂度在合理范围内,所以本地测试就没有测试CPU_TIME。
此外,后两次作业本地测试时,可以用前面的强测样例来检验下,防止改写方法后导致前面的BUG,强测样例的数据量较大,所以还是比较有效。
容器的选择与使用
本单元作业用到最多的容器就是HashMap,因为这个容器在查询时复杂度较低。而且其自带的containsKey、get、put方法来时实现我们的规格很简单,只要一行代码即可。对于异常类计数时我也是采用的HashMap,其中更新发生异常的次数时只需要用merge方法即可,十分方便,时间复杂度也可以接受。
此外在实现message数组时,介于每次加入message要加在头部,二查询时只需要返回前4个message,因而我采用了LinkedList来实现,因为这个容器插入时复杂度为0(1)(通过addFirst),而每次我们只要前4个元素复杂度也为0(1)。
性能问题的总结与分析
第九次作业
本次作业性能问题可能出现在queryBlockSum,如果按照规格去实现时间复杂度会是O(n2),采用并查集后只需加入或删除时更新,返回时的复杂度为O(1)。
第十次作业
本次作业性能问题可能出现在getAgeVar、getAgeMean和getValueSum函数中,如果采用查询时计算复杂度会很高。而且更新时计算,查询时返回还有的好处是它利用了之前的结果,而不是重新计算,在计算更新平均年龄以及边权值总和时复杂度只有O(1),在更新年龄方差时复杂度为O(n)。而查询时计算,计算三者都为O(n)。
第十一次作业
本次作业可能出现的性能问题在于sendIndirectMessage,主要在于最短路径计算的复杂度。由于边权值均为非负值,所以一般采用Dijkstra算法,如果没有优先队列进行优化复杂度为O(V2),通过优先队列进行优化后降到O(V logV)。
作业架构设计
其实我的架构设计在设计策略以及容器的选择两部分上已经体现的差不多了。我对于图模型的存储采用的HashMap(在容器的选择上体现),在更新时更新需要维护的变量(在设计策略上体现)。

浙公网安备 33010602011771号