HIT软件构造:Lab1实验总结

Lab1主要分为 1.Magic Squares 2.Turtle Graphics 3.Social Network,下面简单记录各个任务中的一些思考总结。

1.Magic Squares(文件输入输出操作)

第一项任务首要一点是Java语言的文件输入输出操作,刚好在这一部分也不熟悉,就简单总结一下Java中的基本的文件操作。

首先:在Java中不论文件输入还是输出都会用到 File类 :File类是一个代表文件或文件目录(俗称文件夹)的类,通常使用路径来初始化,允许使用绝对路径和相对路径,如:

在Java程序中,对于数据的输入/输出操作以 “” 的方式进行:                                                       

字节流和字符流这两种读取方式的差别主要体现在读ascii码范围之外的字符时,字符流能够正常读取,而字节流可能会没办法正常读取汉字等字符。Java的IO设计多个类,但实际上基本都是由上面这些抽象基类派生的,如:FileInputStream,BufferedReader,PrintWriter 。而对于访问文件常见的有FileInputStream、FileOutputStream、FileReader、FileWriter四个类。

1.1.FileReader读入数据的操作

    读入的文件必须存在,否则会报FileNotFoundException。


    步骤:
        1. 提供File对象,指明要操作的文件。
        2. 提供FileReader对象,用于数据的读入。
                read() 返回读入的一个字符的字节码,如果到达文件末尾则返回-1。                                        3. 文件读完后进行流资源关闭 

1.2.FileWriter写出数据的操作 

如果对应的文件在硬盘中不存在,则创建此文件。
如果对应的文件在硬盘中存在:
    如果使用的构造器是 FileWriter(file) 或 FileWriter(file, false),则对原来的文件覆盖。
    如果使用的构造器是 FileWriter(file, true),则在原来的文件追加内容。

1.3.FileInputStream写入数据操作 

1.常用构造方法:

1.1.FileInputStream(File file),参数传入一个File类型的对象。
1.2.FileInputStream(String name),参数传入文件的路径。

2.FileInputStream常用方法

2.1.read()方法:从文件的第一个字节开始,read()方法每执行一次,就会将一个字节读取,并返回该字节ASCII码,如果读出的数据是空的,即读取的地方是没有数据,则返回-1。

2.2.int read(byte b[]):方法与int read()方法不一样,该方法将字节一个一个地往byte数组中存放,直到数组满或读完,然后返回读到的字节的数量,如果一个字节都没有读到,则返回-1。

1.4FileOutputStream写出数据操作 

1.常用构造方法

1.1FileOutputStream(File file),该流向File对象表示的文件写出数据。

1.2FileOutputStream(String filename),该流向指定文件名的文件写出数据。

2.FileOutputStream常用write()方法

2.1void write(int d),将指定字节写入此文件输出流,参数是只给定的int值的"低八位",也就是一个字节

2.2void write(byte[] b),将指定的byte数组中的数据写入此文件输出流中

2.Turtle Graphics(Convex Hulls)

任务二要根据MIT的实验要求和代码注释,完成TurtleSoup.java中待实现的方法,操作一个叫Turtle的绘图工具进行绘图。其中有Problem 7:Convex Hulls(凸包问题)涉及礼品包装算法(Gift Wrapping)

2.1凸包问题

凸包(Convex Hull),是一个计算几何(图形学)中的概念。简而言之,就是给定平面点集,找出该点集中最外围的点构成凸多边形,使得该平面点集中的点全都在该凸多边行内部或者边上。其他凸包还分为最大凸包和最小凸包;最大凸包:既构成该凸多边形的顶点数最多;最小凸包:既构成该凸多边形的顶点数最少。某些情况下,最大凸包和最小凸包是可以相等的。如下,红色圈圈部分即为该平面点集的凸包。

2.2Gift Wrapping算法

基本思路:首先,先找到凸包上的一点,然后以此点开始,顺时针(或逆时针)寻找下一个点,然后再从下一个点寻找下下一个点,依此循环,直至寻找到凸包集上所以点。

前提条件:默认输入的是平面点集上的各点的坐标。

问题1:如何确定第一个点?

依照凸包的定义,我们可以选取最左上(x轴坐标最小且y轴坐标最大)、左下、右上或者右下的点作为第一个点,该点一定是在凸包上的。如下:搜索输入点集中的每一个点(p),找出最左上的一个点(start)加入凸包集,并以此点开始(ptr),寻找下一个点。

        //搜索输入点集中的每一个点(p),找出最左上的一个点(start)加入凸包集,并以此点开始(ptr),寻找下一个点。
        for(Point p:points) {
            if( (p.x()<start.x()) || (p.x()==start.x()&&p.y()>start.y()) )
                start = p;
        }
        result.add(start);
        Point ptr = start;

问题2:如何在已知点上确定下一个点?

计算该点(P0)与其他点的极角并进行比较,选择极角最小的点作为下一个点,该点必在凸包上。倘若有两个点(P1,P2)极角大小相同,则根据所求的最大/最小凸包来选取:若求最大闭包,则选取距离P0点最近的点;若求最小闭包,则选取距离P0点最远的点。

须注意:calculateBearingToPoint()方法计算极角值,其第一个参数确定以哪个朝向作为极轴正方法(下面以0°代表正上方),且每次找出一个点后,计算下一个的极角值时,应将每次的极角值累加作为下次计算的朝向。否则,计算可能会陷进死循环。

        while(true){
            //首先,找出任意一个不同点,计算极角值
            for(Point p:points){
                if(p!=ptr){
                    target=p;
                    break;
                }
            }
            mindegree=calculateBearingToPoint(degree,(int)ptr.x(),(int)ptr.y(),(int)target.x(),(int)target.y());

            //之后,搜索整个平面点集,找出极角值最小的点
            for(Point p :points){
                if(p==ptr)
                    continue;
                tempdegree=calculateBearingToPoint(degree, (int)ptr.x(), (int)ptr.y(), (int)p.x(), (int)p.y());
                if(tempdegree<mindegree){
                    mindegree=tempdegree;
                    target=p;
                }
                //若极角值相等,判断距离
                else if(tempdegree==mindegree){
                    double dist1=TurtleSoup.calculateDistance(ptr.x(), ptr.y(), target.x(), target.y());
                    double dist2=TurtleSoup.calculateDistance(ptr.x(), ptr.y(), p.x(), p.y());
                    if(dist2>dist1)
                        target = p;
                }
            }

            //当再次找到start点时,退出循环
            if(target==start)
                break;
            result.add(target);
            ptr=target;
            //累加极角值
            degree=(degree+mindegree)%360.0;
        }
        return result;
        //throw new RuntimeException("implement me!");
    }

问题3:如何确定已找完所有的点?

按找问题2的思路寻找,若求出的下一个点与初始确定的第一个点相同,则已找到全部的凸包集上的点。

Gift Wrapping算法过程说白了,就是往钉子板上围皮筋的过程,先将皮筋固定在最外面的一个点上,然后顺时针(或逆时针)转动皮筋,每次碰到的点就是凸包集上的一点,直至将全部钉子包围起来。

3.Social Network(BFS求最短路径)

任务三实现Person和FriendshipGraph两个类,模拟社交网络,实现添加节点以及节点之间添加边,通过BFS计算两节点之间最短路径的方法。其中难点在于利用设计好的两个类,求两点间的最短路径(getDistance())方法的实现。

3.1实现getDistance()方法的类属性设计

 首先,Person类中:

    private boolean visit;       //标识person是否在BFS中访问过
    private List<Person> friends;//person的朋友表(边集)
    private int distance;        //BFS中表示源点离该person最短距离

然后,FriendShipGragh类中:

//图中已有的人名列表(顶点集)
private ArrayList<Person> personlist = new ArrayList<Person>(); 

3.2 getDistance()方法

基本思路:首先,进行初始化(消除多次调用该方法之间的影响),将社交网络图中各顶点(person)设置为未被访问状态,和与源点的距离(distance)为0。然后,利用BFS搜索,将源点(person1)入队列,走遍源点的朋友圈,将各个朋友入队,设置各个朋友的访问状态(true)到源点的距离(队首点的距离+1),检测是否到达目标点(person2),若无,则将当前队首点出队,计算下个队首点及其朋友。循环直至person2被访问,返回该距离;或者队列为空,源点不可达person2。

须注意:该方法存在缺陷,因为在社交网络图中,各顶点间的边关系(既边集)属性应该存储于类FriendshipGragh类中较合适,而该实现方案将之存储于Person类中的List<Person> friends中;这将导致在连续创建多个社交网络图时,若在第一个社交网络图(gragh1)中添加点person1及其相应的部分朋友(person2,person3等)的边关系后,再创建第二个社交网络图(gragh2)添加顶点person1 ,person2 ,person3等时,他们之间的边关系无需添加,也已经出现在gragh2图中,这将造成难以预料的结果。但是,由于考虑到本题限制条件无重名的person和一个人的朋友无论在哪个社交网络图中,其原有的朋友关系是不会消失的,所以在此题中该方法是可行的

具体代码实现:

    public int getDistance(Person person1, Person person2){
        int distance=0;
        if(person1==person2)
            return distance;
        //初始化
        for(int i=0;i<personlist.size();i++){
            personlist.get(i).setVisit(false);
            personlist.get(i).setdis(0);
        }
        Queue<Person> queue= new LinkedList<Person>();
        queue.offer(person1);
        person1.setVisit(true);
        //BFS求最短路径
        while(!queue.isEmpty()){
            Person temp=queue.poll();
            for(int i=0;i<temp.getFriends().size();i++){
                if(!temp.getFriends().get(i).getVisit()){
                    temp.getFriends().get(i).setVisit(true);
                    temp.getFriends().get(i).setdis(1+temp.getdis());
                    queue.offer(temp.getFriends().get(i));
                }
            }
            if(person2.getVisit()){
                return person2.getdis();
            }
        }
        return -1;
    }

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值