软件构造Lab1心得
在实验过程中,了解到了java不同层面的许多知识,也遇到了许多问题,在这里分享这些经验和实验心得。
任务1 - 幻方
文件读写
了解到文件读写的方法主要有三种:
1. FileInputStream和FileOutputStream
FileInputStream用于从文件读取数据,可以用关键字new来创建。
构造方法有很多,一种是利用字符串类型的文件路径来创建:
InputStream s = new FileInputStream("C:/java/helloworld");
另外还可以利用文件对象来创建:
File f = new File("C:/java/helloworld"); InputStream in = new FileInputStream(f);
FileOutputStream用于向文件中写数据。 如果该流在打开文件进行输出前,目标文件不存在,那么该流会创建该文件。
有两个构造方法可以用来创建 FileOutputStream 对象。
使用字符串类型的文件名来创建一个输出流对象:
OutputStream f = new FileOutputStream("C:/java/hello")
也可以使用一个文件对象来创建一个输出流来写文件。我们首先得使用File()方法来创建一个文件对象:
File f = new File("C:/java/hello"); OutputStream fOut = new FileOutputStream(f);
2. BufferedWriter和BufferedReader
BufferedWriter 和BufferedRead是缓冲字节流,属于高级流,按行读取字符串。
由于这两个字符流不能直接处理字节流,所以需要InputStreamReader和OutputStreamWriter这两个转换流做纽带,
将低级字节流和BufferedReader、BufferedWriter关联起来。范例:
判断矩阵是否是magic
在做这部分内容时,观察到有很多同学都是对列进行判断,对行进行判断,对斜角线进行判断
这里可以利用一个双层的for循环完成判定
点击查看代码
// 判断是否是一个magic square
int sum = 0;
int colsum = 0;
int rowsum = 0;
int slashsum1 = 0;
int slashsum2 = 0;
for (int i = 0; i < row; i++) {
// 求取基准值
sum += matrix[0][i];
}
for (int i = 0; i < row; i++) {
// 分别对两斜线、行、列数据求和
colsum = 0;
rowsum = 0;
slashsum1 += matrix[i][i];
slashsum2 += matrix[i][row - i - 1];
for (int j = 0; j < col; j++) {
rowsum += matrix[i][j];
colsum += matrix[j][i];
}
if (sum != rowsum || sum != colsum) {
System.out.println("Not a Magic Square!");
return false;
}
}
if (sum != slashsum1 || sum != slashsum2 || sum != rowsum || sum != colsum) {
System.out.println("Not a Magic Square!");
return false;
}
else return true;
任务2 - 海龟画图
凸包问题
凸包(Convex Hull)是一个计算几何(图形学)中的概念。
在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,...Xn)的凸组合来构造.
在二维欧几里得空间中,凸包可想象为一条刚好包著所有点的橡皮圈。
用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。
凸包算法
Graham扫描法
思路:先找到凸包上一个点,从该点开始逆时针方向逐个寻找凸包上的点。
步骤:
-
把所有点放在二维坐标系中,则纵坐标最小的点一定是凸包上的点,设为P0
-
把所有点的坐标平移,使得P0成为新的原点
-
计算各个点相对于P0的角度,将它们按角度从小到大的顺序对各个点排序。当角度相同时,距离P0近的点排序放在前面。由几何知识我们可以知道,排序后的点中,第一个点P1和最后一个点Pn肯定是凸包上的点,否则的话,这两点始终在P0和其它任意点的连线的外侧。
-
以上是求取凸包的准备步骤,我们知道了凸包上的两个点P0,P1,我们将它俩放在栈中,接着我们从排序中的第二个点P2出发,开始找凸包上的第三个点。
-
连接P0和栈顶的点,得到直线L。看当前点是在直线L的右边还是左边。如果在直线的右边就执行步骤6,在直线上或左边就执行步骤7。
-
如果在右边,则栈顶的点不是凸包上的点,把栈顶元素出栈,执行步骤5。
-
当前点是凸包上的点,压入栈。执行步骤8。
-
检查当前点是不是排序的得到的点中最后一个点,是则结束,不是则当前点为下一个点,执行步骤5。
算法代码:略
任务3 - 社交图
getDistance函数
函数要求,给出两个参数Person a, Person b,求出两个人的社交距离。
分析来看,该问题就是图中两点的最短路径问题
我们采取比较经典的Dijkstra算法来解决问题。
对于计算机专业的人来说,对该算法的了解是最基本的。
这里作简要介绍:
数据结构(rep)用到了队列Queue,具体实现是LinkedList,和列表List,具体实现是ArrayList,和Map,具体实现是HashMap。
-
初始化:我们将Person a放入队列中,并将基础distance值0,二者一起键入HashMap。
-
判断栈空不空,空则执行步骤3,不空则执行步骤4。
-
退出循环,返回-1。
-
弹出队列头元素,判断是否与Person b相同,并将该元素放入ArrayList中,标记为已访问,相同则执行5,不同则执行6。
-
返回HashMap中该Person的对应distance值。
-
不同,则对从队列弹出的Person的朋友们,其中符合不在队列中(因为求最短路径,该路径一定比已经在队列中的大)而且没有访问过的元素的条件,将符合条件的朋友压入队列。执行步骤2。
最终返回值就是Person a, Person b的社交距离了。
实现代码:
点击查看代码
public int getDistance(Person a,Person b) {
// 利用队列进行广度优先搜索
Queue<Person> friendqueue = new LinkedList<Person>();
ArrayList<Person> visitedPerson = new ArrayList<Person>();
HashMap<Person, Integer> distanceMap = new HashMap<Person,Integer>();
friendqueue.add(a);
int distance = 0; // 用于计算两点间最短距离
distanceMap.put(a, distance); // a距离自身的距离是0
while (!friendqueue.isEmpty()) {
Person find = friendqueue.poll();
visitedPerson.add(find); // 从栈中取出后,访问完毕后不再访问
if (find == b) { // 如果等于b,返回距离
return distanceMap.get(find);
}
else {
for (Person p : find.list) { // 每次向栈中添加成员后,同时向hashmap中键入person以及对应distance
if ((!friendqueue.contains(p)) && (!visitedPerson.contains(p))) {
friendqueue.add(p);
distanceMap.put(p, distanceMap.get(find)+1);
}
}
}
}
return -1;
}
总之,软件构造实验并不太难,而是突然面对陌生的编程语言难以接受,学会适应,你会发现,语言之间也是可以相通的。
对于实验,只要细心研究,会有所收获的。