OO第三单元作业总结
OO第三单元作业总结
第一次作业
设计策略
第一次作业主要实现社交网络中人与人的联系,抽象成数学问题就是图的联通问题。由于人的id是唯一的,很适合用id作为Key,人作为Valus,用一个HashMap来存放人的集合,这样既方便通过id来找人,也方便通过id判断是否已经有相同id的人。
测试方法和策略
测试方法为构造一些特定数据,来验证一些关键功能的正确性,其中主要测试的部分为计算组数和是否连通的正确性。
容器选择和使用经验
在第一次作业时,由于JML设计规格的“误导”,使用ArrayList进行人的存储,发现每次通过id找人时都需要遍历一次容器,产生巨大的性能开销,经过仔细研究JML规则,发现HashMap也是符合设计要求的容器,且在通过id找人和判断id是否存在时都较ArrayList有很大的优势,故选择HashMap来存放人的集合。同理,在MyPeron类中,acquaintance和value同样使用HashMap来代替ArrayList,以便于通过id寻找数据。
性能问题
本次作业的性能问题主要出现在判断两人是否有联系,即图中两点是否连通的问题。如果通过遍历图来判断,无论是广度优先还是深度优先都有着O(n2)的复杂度,在互测时也被无情hack了。后来发现,由于不需要找到连通路径,可以使用并查集来管理连通的点,复杂度仅为O(n)与广度优先和深度优先相比复杂度大大降低。
架构设计
为了实现并查集,为每个人添加一个集合编号来表示这个人当前所在集合,在集合合并时进行修改,这样每次判断两人是否有关系只需要看这两个人集合编号是否相等即可。在两个集合进行合并时,通过这个编号也便于找到需要合并的两个集合,进行合并。
第二次作业
设计策略
本次作业在第一次作业的基础上增加了小组的概念,并增加了消息类。由于消息和小组和人一样,id都是唯一的,因此选择使用HashMap来存放小组和消息。而每个人收到的信由于需要按照收到顺序排列,故使用ArrayList来存放。
测试方法和策略
本次作业的测试方法主要测试新增加的功能的实现,通过构造一些数据来确认关键功能的正确性。
容器选择和使用经验
有了第一次作业的经验,第二次作业很自然地想到了要用HashMap来存放消息和小组,而在存放信的时候,主要考虑queue和ArrayList的选择,为了方便后来扩展功能,选择使用ArrayList来管理,手动实现队的一些功能。
性能问题
本次作业的性能问题发生在求小组年龄的标准差,如果不进行优化,每次求标准差之前都需要先求一次年龄的平均值,再通过这个平均值来求标准差,实际上,如果小组中的人没有变化,可以直接返回上次求出的标准差,而不需要再求一次,有改进的空间。
架构设计
从上面的性能问题可以看出,优化年龄标准差可以减少一些重复的计算,通过给MyGroup增加年龄的平均值flag1和标准差flag2,如果更新过平均值或标准差则将flag1或flag2设为true,当小组中人发生变动,即增加或减少时,将flag1和flag2设为flase。这样一来,当请求返回平均年龄或标准差时,就会先判断当前值是否需要更细,如果已经更新过则直接返回,否则再进行计算,并将计算结果保存后再返回,实现数据的更新。
第三次作业
设计策略
第三次作业增加了信息的种类和间接发送信息的功能,并增加了表情类。与人,小组,信息一样,表情也有唯一的id,果断选择使用HashMap存放表情。对于表情,有两个操作:统计表情的使用频率,每当发送表情信息,表情信息对应的表情使用次数加一;删除使用数量较少的表情,如果一个表情使用次数少于给定值,则删除对应表情和使用该表情的信息。第一个功能较为容易,只需要建一个Key为表情id,Value为使用次数的HashMap就可以很轻易的管理表情使用次数,而第二个功能在删除表情时较为容易,只需要通过id来移除HashMap中表情和使用次数即可,较为复杂的是将含有该表情的消息删除,这就需要将表情和消息进行一个映射,建立一个能通过表情id找到使用该表情的消息的容器。至于间接发送信息的功能,实际上可抽象为找两点最短路径的问题,由于路径长度均为正值,两点都是确定点,找最短路径选择迪杰斯特拉算法。
测试方法和策略
由于本次实验内容较多,也较为复杂,通过构造数据来测试很难覆盖全部功能,选择通过其他同学设计的自动数据生成器,和其他同学进行对拍来进行测试。
容器选择和使用经验
本次实验在容器选择时与前两次区别不大,而在寻找最短路径的时候,为了堆优化迪杰斯特拉算法,使用了优先队列PriorityQueue来避免遍历HashMap。
性能问题
本次作业的性能问题主要是迪杰斯特拉算法。基本的迪杰斯特拉算法复杂度为O(n2),事实证明如果不去优化在强测时会出现Runtime Error。原因在于每次寻找最短路径时都需要遍历整个存放路径长度的容器才能找到最小值,才能找到下一个节点,如果使用堆优化,用一个优先队列来存放这些路径,那么最短路径始终存放在队首,在寻找最短路径时就无需遍历,直接获得下一个节点,将复杂度降为O(mlogn)。
架构设计
对于表情管理,为了实现表情到信息的映射,使用一个Key为表情id,Value为存放使用该表情的信息的id的ArrayList,这样通过表情id就可以获得使用该表情信息的id,就可以在删除表情时删除这些信息。增加这个HashMap也导致了删除信息的麻烦,在添加和删除信息的时候除了从messages中添加和删除外,如果是表情信息还要再对应表情的ArrayList中将其添加和删除。
关于迪杰斯特拉算法,为了使用优先队列,新建了一个Inode类来实现Comparable,Inode类中除了存放人和距离外,还重写了方法compareTo,使其返回这个类中距离与另一个类中距离的差值,返回值的正负反映了二者的大小关系。这样,在实现迪杰斯特拉算法时,不再将人和距离用HashMap存放,改为存入PriorityQueue,这样一来,当所有人的距离都更新完毕,最短路径也找到了,就是PriorityQueue中第一个元素,将其poll出就可继续寻找路径。
心得体会
浙公网安备 33010602011771号