HIT 软件构造Lab1实验总结

本文分享了作者在完成Java实验过程中的心得体会,包括MagicSquares、TurtleGraphics和FriendshipGraph等任务的解决思路与技巧,详细介绍了如何处理字符串解析、实现绘图功能及解决凸包问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

其实写这篇的时候我刚lab2做完了,当时写完lab1休息了几天又开始了lab2,相比较下来lab1简直是。。。怎么说呢,工作量少很多吧。不过还是有不少的感悟可以说一下的。

Magic Squares

总的来说幻方这个任务不太难,java有很多很方便的方法,相比较c语言,简直是省了一大笔劲。

难点:读取字符串然后解析成数字。
我们根据spec,要从文本中读入内容,java中读取文本你的方法有很多,我这里使用的是BufferedReader ,并且每次只读入一行内容,所以可以用个循环来一直读,每读一次,都要对字符串进行解析,将其转化为数字存储在数组中。我在其中也有加入一些判断,判断是否满足幻方,是否存在非数字输入。

while ((str = bufferredReader.readLine()) != null) {
   int s = 0;
   row += 1;
   String[] array = str.split("\t");
   for (int i = 0; i < array.length; i++) {
    if (!array[i].matches("[0-9]+") || array[i].equals("0")) {
     if (array[i].contains(" ")) {
      System.out.println("有不是用“\\t”分隔的数字" + array[i]);
      bufferredReader.close();
      fileReader.close();
      return false;
     }
     System.out.println("there is a illegal input: 有不是大于0的非法输入" + array[i]);
     bufferredReader.close();
     fileReader.close();
     return false;
    }
   }
   int[] iarray = new int[array.length];
   for (String strs : array) {
    if (strs != null)
     iarray[s++] = Integer.parseInt(strs);
   }
   list.add(iarray);
   if (row > 1) {
    if (list.get(row - 1).length != list.get(row - 2).length) {
     System.out.println("this is not a square!不是正方形");
     bufferredReader.close();
     fileReader.close();
     return false;
    }
   }
  }

这个将字符串转成数字的方法还是特别好用了,以后会经常用到。

Turtle Graphics

P2就比P1有意思多了,内容上比幻方问题好有趣,要求实现画图功能。

前几个函数其实都是中学的数学题,实现几个简单的计算就可以完成。根据方法的spec编写代码。

public static void drawSquare(Turtle turtle, int sideLength) {
     for ( int i = 0 ; i < 4 ; i++ ) {
         turtle.forward(sideLength);
         turtle.turn(90);
        }
    }
    
 public static double calculateRegularPolygonAngle(int sides) {
     if ( sides > 2) {
      double angle =(double) (sides-2)*180 / sides ;
      return angle ;
     }
     else
      throw new IllegalArgumentException("sides is illegal");
    }
    
public static int calculatePolygonSidesFromAngle(double angle) {
        if ( angle > 0 && angle < 180 ) {
          int sides = (int)Math.round((360 / (180 - angle)));
          return sides ;
        }
        else
         throw new IllegalArgumentException("angle is illegal");
    }

public static void drawRegularPolygon(Turtle turtle, int sides, int sideLength) {
        double angle = calculateRegularPolygonAngle(sides);
     for ( int i = 0 ; i < sides ; i++ ) {
         turtle.forward(sideLength);
         turtle.turn(180-angle);
        }
    }

public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY,
                                                 int targetX, int targetY) {
        double degree = Math.toDegrees(Math.atan2((targetY - currentY),(targetX - currentX)));
        double angle = (90-degree)-currentBearing ;
        if ( angle < 0 )
         angle = angle + 360 ;
        return angle ;
    }

public static List<Double> calculateBearings(List<Integer> xCoords, List<Integer> yCoords) {
        List<Double> turn_degree = new ArrayList<Double>();
        for ( int i = 0 ; i < xCoords.size()-1 ; i++ ) {
         if ( i == 0 ) {
          turn_degree.add(calculateBearingToPoint(0,xCoords.get(i),yCoords.get(i),xCoords.get(i+1),yCoords.get(i+1)));
         }
         else
          turn_degree.add(calculateBearingToPoint(turn_degree.get(i-1),xCoords.get(i),yCoords.get(i),xCoords.get(i+1),yCoords.get(i+1)));
        }
        return turn_degree ;
    }

难点:凸包问题
凸包问题,我在做这个实验前也不知道什么是凸包问题,想了解的小伙伴们可以去问问度娘,大概就是找一个点集,这个点集里面的点的连线能够把整个图的所有点都包括在内。在优快云上有很多博客都描述了这个问题的解法,我记得我当时也看了好几种,其中最好理解的一种就是边界漫游法,很通俗易懂。
边界漫游法,也就是通过计算找到最外围的一个点,以这个点为起点,找和它夹角最大的点,这个点就是要找的凸包的点,若有相同的情况就判断这两点到这一点的距离,远的那个加入到凸包的点集中,再以这个点为新起点,重复上述操作。当回到一开始的起点的时候,也就结束了,找全了。这就是为什么前面要实现计算Bearing方法的原因了,都是为了求凸包做准备。代码是我自己写的,仅做参考:

public static Set<Point> convexHull(Set<Point> points) {
        Set<Point> convexHullPoint = new HashSet<Point>();
        Point startPoint = null ;
        double currentBearing ;
        double nextBearing ;
        double Bearing ;
        double nextLength ;
        Point currentPoint ;
        Point nextPoint = null ;
        if ( points.isEmpty())
         return points ;
        if ( points.size() < 4 )
         return points ;
        for ( Point point : points ) {
         if ( startPoint == null ) {
          startPoint = point ;
          continue ;
         }
         if ( startPoint.x() > point.x() )
          startPoint = point ;
         else if ( startPoint.x() == point.x()) {
          if ( startPoint.y() > point.y() )
           startPoint = point ;
         }
        }
        convexHullPoint.add(startPoint);
        currentPoint = startPoint;
        Bearing = 0 ;
        double Length ;
        while (true) {
         nextBearing = 360 ;
         nextLength = Double.MAX_VALUE ;
         for ( Point point : points ) {
          if ( point.equals(currentPoint))
           continue ;
          currentBearing = calculateBearingToPoint(Bearing ,(int)currentPoint.x(),(int)currentPoint.y(),(int)point.x(),(int)point.y());
          Length = Math.pow(point.x()-currentPoint.x(),2)+Math.pow(point.y()-currentPoint.y(),2);
          if ( nextBearing == currentBearing && nextLength < Length) {
            nextLength = Length ;
            nextPoint = point ;
          }
          else if ( nextBearing > currentBearing ) {
           nextLength = Length ;
           nextPoint = point ;
           nextBearing = currentBearing ;
          }
         }
         if ( startPoint.equals(nextPoint) )
          break ;
         currentPoint = nextPoint ;
         Bearing += nextBearing ;
         convexHullPoint.add(nextPoint);
        }
        return convexHullPoint ;
    }

FriendshipGraph

FriendshipGraph这个实验的整体难度还是比较低的,但是有一个地方我觉得我做的不太好,就是getdistance这个方法,我当时一直在想用广搜吧,嗯,那就用队列,嗯,但是没想到怎么计算广搜层数的问题,因为不知道该在循环的哪个地方加,然后我就去用dj算法了。直到我做到了实验二,还要实现这个玩意,我觉得还是得想一下的,最后我发现其实用Map特别好实现,之前想的其他方案都觉得太复杂太捞了,就没使。话不多说,看代码,主体循环应该就10行左右。不过这里我放的是实验二的代码,在实验一中将判断条件改改就行。

Set<Person> visit = new HashSet<>();
  //类似一个数组一样的功能,存每个点到起点的距离。
  Map<Person, Integer> distance = new HashMap<>();
  //初始化这个Map,-1表示没有相邻。
  for (Person src : this.vertices) {
   distance.put(src, -1);
  }
  //利用队列,使用广度优先搜索,一层层的遍历整个图
  Queue<Person> queue = new LinkedList<>();
  //自身入队,visit表示已经入队了的点集
  queue.add(source);
  visit.add(source);
while (!queue.isEmpty()) {
   Person src = queue.poll();
   for (Person srt : this.personGraph.targets(src).keySet()) {
    if ( !visit.contains(srt)) {
     queue.add(srt);
     visit.add(srt);
     int i = distance.get(src);
     //i表示src距离源点的距离,srt是src的邻接点,所以srt到源点的距离等于src的距离加上它两间的权值
     distance.put(srt, i + personGraph.targets(src).get(srt));
    }
   }
  }

总结

整体来讲,对于我这种现学现卖的学生来说,实验一更像是一种尝试,缓冲。我通过实验一也是了解了java的很多语法,但还有很多东西要学,要知道一本java核心技术基础知识有600多页。。。还只是卷一,又是一本深入理解计算机系统。。。正是有了这次实验的基础,我才敢在实验二中做更多的尝试,包括想着用更优的算法。特别是Person类,一个person有的属性太多了,又怎么会是只有姓名,朋友呢,还有很多属性可以去扩展,构建,和其他事物进行联系,不然怎么能叫ADT呢?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值