OO第三单元作业总结
OO第三单元作业总结
本单元作业目的是培养JML的阅读理解能力,以及按照JML描述规格进行架构设计与实现。
设计策略
第九次作业
要求概述
本次作业下发资料包包括输入输出文件、要求实现的接口文件和异常类文件,要求根据接口文件中的JML描述,实现一个初步的社交网络模型。
设计
本单元作业中JML要求已经给出整体设计思路,我们需要考虑细节的实现,以保证正确性、安全性和时间效率。
容器选用
本次作业中,人的集合、关系集合等可以采用HashMap存储,以简化查询过程中的代码量,并有效降低时间复杂度。
需要注意,在删除人或关系时,需要使用迭代器或remove类方法,以防止异常产生。
算法选择
在维护图的过程中,可以采用并查集,降低查询连通块的时间复杂度。
第十次作业
要求概述
本次作业要求增加群类的概念,需要实现群类中人的各项权值属性的统计;同时需要实现发送、接受消息等功能。
设计
算法选择
在统计过程中,需要对群类中人的各项属性进行预处理,即增加、删除人或关系的时候,就要对相关群类中的属性进行修改。这样可以避免在询问过程中暴力统计,在询问密度高的数据中表现出较好的时间效率。
第十一次作业
要求概述
本次作业要求增加不同类型的消息,并实现最短路查询。
设计
容器选用
选用PriorityQueue来实现堆,注意重载比较器。
算法选择
选用堆优化Dijistra算法计算最短路,时间复杂度$O(Nlog_{2}N)$。
基于JML规格设计测试
JUnit是一款强大的测试工具,极大简化了测试过程,方便进行功能覆盖性测试,可以查看测试覆盖代码。然而仍需要手动构造特殊测试数据,以保证正确性。
基于JML设计测试而非面向代码进行测试,体现了面向需求的思想,测试先于代码。
基于JML测试的另一大优势是方便分功能测试,JUnit可以对每一个函数细化测试,有利于细节跟踪。
容器选择
ArraryLIst
列表功能强大,支持快速增删改查,但是在遍历过程中不占优势,因而本次作业中ArraryList甚少使用。
LinkedList
java中的LinkedList是一个双向链表。但其实它还有一些其push和pop,可以当成栈来使用,这对于本单元作业中“最近接收到的消息”功能比较方便。
PriorityQueue
java中的优先级队列是二叉小顶堆,方便维护堆优化Dijskra。需要注意一些细节:
- element()和peek():获取但不删除堆顶(队首)元素。
前者失败时抛出异常,后者返回null。 - remove()和poll():获取并删除队首元素。
前者失败时抛出异常,后者返回null。
HashMap
是一个散列表,它存储的内容是键值对(key-value)映射。无序的,key不重复的,没有索引。不能用普通for循环来遍历,需要用迭代器或增强型for。
put本质
- 首先将k,v封装到Node对象当中(节点)。
- 第二步它的底层会调用K的hashCode()方法得出hash值。
- 第三步通过哈希表函数/哈希算法,将hash值转换成数组的下标
- 下标位置上如果没有任何元素,就把Node添加到这个位置上。
- 下标对应的位置上有链表。此时,就会拿着k和链表上每个节点的k进行equal。
- 如果所有的equals方法返回都是false,那么这个新的节点将被添加到链表的末尾。
- 如其中有一个equals返回了true,那么这个节点的value将会被覆盖。
iterator安全删除元素
lambda表达式的应用
性能问题
-
询问连通性
由于在社交网络中的边为双向边,不存在重边,因而可以采用并查集维护连通性。在加入人的时候,初始化人的并查集数。加入关系时,判断两人是否已经相连。如果不相连则加入同一集合。查询连通性过程中只需查询二者的并查集数即可。查询连通块数量只需查询每个人的并查集数是否为改变过即可。
-
询问最短路
最短路采用堆优化Dijsktra。
-
询问群体年龄平均数、方均根、边权和
不在查询过程中暴力统计,而是在独立维护平均数、方均根、边权和,仅在增、删过程中进行修改。增删过程中时间复杂度$O(N)$,查询过程O(1)
图的模型
并查集
维护可以结合的合并操作。按秩合并可以使得查询的单次时间复杂度达到$O(logN)$。增加路径压缩优化后可以达到均摊效果。
堆优化Dijsktra
Dijsktra算法时间复杂度为$O(N)$,制约因素在于每次需要寻找最近的点。采用堆来维护距离后,可以在$O(log_2N)$的时间复杂度内查询最近的点。因此可以达到$O(Nlog_2N)$的时间复杂度。