OO第三单元单元总结

OO第三单元单元总结

测试过程

黑箱操作和白箱操作

黑箱操作我认为就是通过随机构造合法数据对代码进行测试,在测试过程中应当着重对边界条件、前置条件、结果正确性等方面进行判断。不需要具体到代码的逻辑,通过大量暴力的数据尝试找到代码的bug

白箱操作就是通过看代码的方式用人工逻辑的方式找到代码的bug,这样更具体、更有针对性。但是旁人看代码总是会因为每个人代码风格的问题不容易理解、找出bug(纯个人认为,因为在互测的时候看别人的代码我从来找不到别人的错误)

单元测试、功能测试、集成测试、压力测试、回归测试

单元测试 :以模块为单元,以验证模块正确性和稳定性为目的,通过预先设计设计的单元测试,程序员可以编写自动化测试工具对某个模块代码进行针对性测试

功能测试:测试软件功能能否按照需求正确完成。

集成测试:是单元测试的逻辑扩展。它最简单的形式是:把两个已经测试过的单元组合成一个组件,测试它们之间的接口。(摘自百度百科)

压力测试:使用大数据,测试代码跑通数据的时间、所需要的空间是否足够,主要在是测试算法的优化、数据结构的使用是否合理。

回归测试:修改了旧代码后,使用跟以前的测试数据测试代码会不会有bug(防止出现改了1个bug,生成100个bug的情况)

测试工具

本次测试使用了往届学长的测试工具稍微修改后和同学进行了对拍测试,在测试中发现了不少bug,但是难过的是还是在强测中发现问题

数据构造

使用随机构造方式

图模型构建和维护策略

图模型

一般来说我们的图一般采用的有两种

第一种是矩阵形式,即通过一个n*n的矩阵来存储所有n个人之间的关系

第二种方式就是邻接表的方式,构造n个MyPerson,每个MyPerson中需要存储一个Acquaintance列表(数组、hashmap等)然后在Network类中存储所有的MyPerson(数组、hashMap等)

本次采用的是第二种方式,我使用了一个Hashmap来存储每个人的Acquaintance,起初我以侥幸的心理用了ArrayList,结果在同学的对拍中我的时间特别特别慢,所以果断换成了HashMap,哈希表的好处是可以快速get、remove元素。

维护策略

首先是要维护一些寻常变量,这在第一次作业中非常常见,比如三角关系(tripleSum)、块数(blockSum)之类的,后续我们还有bestAcquaintance之类的变量。维护的方式就是在增减关系和增加人的时候增减这些变量,好消息是这些变量大多数只需要加1或者减1

其次要维护的就是整个图了,整个图的维护主要维护的就是关系和人,如果是加人那么就是new一个MyPerson然后put到Network里面。加关系就是直接在人关系的双方的Acquaintance里面增加双方各自即可。

比较麻烦的是modifyRelation,这个方法本质上就是减关系,如果两个人的value值在被修改后小于0的话会直接被删去。这个问题主要在于维护那些个寻常变量之中。比如TripleSum,如果删除关系的话,就要遍历两者的共同好友的数量,然后删减这些TripleSum。blockSum需要加1。

并查集的使用

在本次作业中我两个地方用到了并查集(不过都是看了佬的算法才知道怎么写的)

第一个是本单元第二次作业中,删除关系后我们还需要判断两个人是否有相同的路径。这个地方我们用了并查集,如果确定两个人的关系终结了。我们必须维护一个并查集,他们的共同属性是他们是否可以相连,如果可以相连的话,我们就需要把他们放在一颗子树中。(关于判断两个人是否在删除直接关系后依然相连,方法是使用bfs遍历全图,做出以两者中的一人为根节点的并查集,再查看另一个人是否已经被包含到这棵树中)

第二个地方是在第三次作业中我使用了Dijkstra算法判断从一个点出发的最小环,具体参照的算法是一下这个链接。https://blog.csdn.net/qq_33362864/article/details/77466841

性能问题

本次的算法我误解了助教所说的“在算法层面上会有所减弱 希望大家不要卷错方向~” 我以为的算法层面的减弱是减弱到用暴力就可以完成强测,但是助教的意思应该是需要一些算法和数据结构。比如我在第一次的作业中,我最开始使用了ArrayList存储所有信息,然后完全照抄JML的要求来写,后来根据和同学的交流才明白这么写的复杂度是有多么多么的大~

动态维护

我认为第一个重要的优化是动态维护一些变量,这样强测的时候就不需要通过复杂度很高的方法来求取blockSum,tripleBlockSum等变量了。这样queryBlockSum的复杂度会是O(1).queryTripleSum的复杂度也是O(1).

并查集

第二个我的教训是在并查集的处理上。并查集其实只需要一个hashMap就可以实现,而我还是用传统的树结构,用类似于C语言的指针数组的方式存储所有的子节点。这种虽然也行,但是,访问每个子节点就慢很多了,因为调用方法的速度明显慢于直接调用HashMap的get方法

遍历范围的确定

第三个教训是在一些方法,比如modifyRelation的过程中,我们需要进行dfs,但是事实上,dfs遍历的时候只需要遍历根节点的Acquaintance中的所有元素即可,而我遍历了整个图中的所有人。这个小优化看起来没啥,在复杂图中确实没有太多的时间节省,但是在稀疏图中差别超级超级大。在我使用了时间戳工具计算后,在极端情况(整个图中有10000个人,但是根节点的Acquaintance只有10个),性能优化可以在10s以上!

Dijkstra算法优化

我最后一个问题就是在第三次作业中的Dijkstra算法使用堆优化后可以大大减少时间复杂度。堆优化前是O(n^2)的复杂度,但是堆优化后是O(n*log(n))的复杂度

规格与实现分离

关于这个问题,在第一次作业中,我可以说是根本没有理解规格的作用。甚至误以为规格就是让我们按照规格实现一遍算法即可。但是事实上,许多规格只是指出了最终结果和实现前提条件,完全照抄只会收获一堆复杂度为O(n2)甚至O(n3)的代码。

另外,规格的实现首先一个就是冗长的规格中找到需要的目的。有些需求其实可以用中文一行说清楚(当然在不考虑语义中的歧义)比如说,queryLeastMoment方法的规格非常非常长,但是总结下来的一句话就是:返回目标id的最小环。但是规格还是对于程序员的规范化代码又很大很大的帮助(虽然我实在读不下去那些特别冗长的JML)

okTest

这一部分我认为是本单元中收获最大的部分。为了完成本单元作业的okTest,我每次都额外建立了一个类okTest,他传入所有的参数后建立一个个的ensure方法进行分析

以下是伪代码

public class OkTest{
    private Element beforeData;
    private Element afterData;
    private int result;
    
    public OkTest(Element beforeData, Element afterData, int result) {
        this.beforeData = beforeData;
        this.afterData = afterData;
        this.result = result;
    }
    
    public int okTest() {
        if (ensure1()) {
            return 1;
        }
        if (ensure2()) {
            return 2;
        }
        if (ensure3()) {
            return 3;
        }
        return 0;
    }
    
   public boolean ensure1() {
        if (xxx) {
            return true;
        }
        return false;
    }
   public boolean ensure2() {
        if (xxx) {
            return true;
        }
        return false;
    }
    public boolean ensure3() {
        if (xxx) {
            return true;
        }
        return false;
    }
}

这种架构我认为是比较简单明了,而且在对拍中可以快速定位哪里有错误

总结

在本次单元中,我学会了如何阅读规格,如何写规格。虽然来说,日后在工作中很难会去写规格,但是这种严谨的开发流程是我们应当学习的。另外,这次作业的算法也让我更加深入了解了Dijkstra,并查集等优秀的算法和它们的优化方式,算法一直都是我们计算机学习者一个很重要的学习环节。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值