20172301 《Java软件结构与数据结构》实验一报告

20172301 《Java软件结构与数据结构》实验一报告

课程:《Java软件结构与数据结构》
班级: 1723
姓名: 郭恺
学号:20172301
实验教师:王志强老师
实验日期:2018年5月30日
必修/选修: 必修java

一.实验内容

实验1:基础链表创建

  • 经过键盘输入一些整数,创建链表;这些数是你学号中依次取出的两位数,再加上今天的时间。
  • 而后打印全部链表元素,并输出元素的总数
  • 在你的程序中,请用一个特殊变量名来纪录元素的总数,变量名就是你的名字。 例如你叫 张三, 那么这个变量名就是nZhangSan
  • 作完这一步,把你的程序git push

实验2:实现节点插入、删除、输出操做

  • 继续你上一个程序, 扩展它的功能,每作完一个新功能,或者写了超过10行新代码,就git push。
  • 从磁盘读取一个文件。这个文件有两个数字。
  • 从文件中读入数字1,插入到链表第5位,并打印全部数字,和元素的总数。保留这个链表,继续下面的操做。
  • 从文件中读入数字2, 插入到链表第 0 位,并打印全部数字,和元素的总数。 保留这个链表,并继续下面的操做。
  • 从链表中删除刚才的数字1 并打印全部数字和元素的总数。

实验3:实现链表的选择排序

  • 使用冒泡排序法根据数值大小对链表进行排序;
  • 在排序的每个轮次中,打印元素的总数,和目前链表的全部元素。

实验4:实现数组插入、删除、输出操做

  • 经过键盘输入一些整数,创建数组。这些数是你学号中依次取出的两位数。 再加上今天的时间。
  • 打印全部数组元素, 并输出元素的总数。
  • 在你的程序中,请用一个特殊变量名来纪录元素的总数,变量名就是你的名字。 例如你叫 张三, 那么这个变量名就是 nZhangSan
  • 作完这一步,把你的程序git push
  • 实现数组插入、删除、输出操做
  • 从磁盘读取一个文件。这个文件有两个数字。
  • 从文件中读入数字1,插入到数组第5位,并打印全部数字,和元素的总数。保留这个数组,继续下面的操做。
  • 从文件中读入数字2, 插入到数组第 0 位,并打印全部数字,和元素的总数。 保留这个数组,并继续下面的操做。
  • 从数组中删除刚才的数字1 并打印全部数字和元素的总数。

实验5:实现数组的选择排序

  • 使用选择排序法根据数值大小对数组进行排序
  • 在排序的每个轮次中,打印元素的总数,和目前数组的全部元素。

2.实验过程及结果

实验一:

  • 首先,要想创建一个链表,咱们须要有指针。须要新建一个指针类。这里我并无直接套用以前用过的LinearNode类,而是新的Point类。
public class Point {
    protected int number;

    protected Point next = null;
    public Point(int num)
    {
        this.number = num;
    }
}
  • 而后,创建链表一样须要须要插入和输出方法。这里的插入我一开始并无实现中间插入的,而是直接使用了尾插法。
public void add(Point element) {
        if ( head == null)
            head = element;
        else {
            Point temp = head;
            while (temp.next != null) {
                temp = temp.next;
            }
            temp.next = element;
        }

    }
  • 输出就比较简单了,遍历一遍链表就行了。
public void PrintLinkedList()
    {
        Point node = head;
        while (node != null)
        {
            System.out.print(node.number + " ");
            node = node.next;
        }
    }
  • 最后是代码测试类的截图以及结果截图。

这里我一开始用的是String.spit(),有必定的局限性,用户输入的时候必须输入一个空格才能成功,在后面的数组实践中已经更改,这里便很少赘述。node

实验二:

  • 根据题目要求,从磁盘中读取文件,那么确定会用到IO流。这里要注意抛出IO异常。 根据咱们学习,IO流可使用FileReaderBufferedReaderFileInputStream,这里我选择的是BufferedReaderBufferedReader的有关问题,详见问题一
  • 在实验二中,我根据题目要求实现了在中间插入的方法和删除方法。值得一提的是,起初的删除方法我并无写出循环中的else条件,致使了无限循环。详见问题二
public void Insert(int x, Point element)
    {
        Point temp = head;
        if (x == 0)
        {
            element.next = head;
            head = element;
        }
        else {
            for (int y = 1; y < x - 1; y++) {
                temp = temp.next;
            }
            element.next = temp.next;
            temp.next = element;
        }
    }
public void DeleteNode(Point element)
    {
        Point ProNode = head, CurrentNode = head;
        while (CurrentNode != null)
        {
            if(CurrentNode.number != element.number)
            {
                ProNode = CurrentNode;
                CurrentNode = CurrentNode.next;
            }
            else{
                break;
            }
        }
        ProNode.next = CurrentNode.next;
    }
  • main方法的代码截图和代码结果截图。

实验三:

  • 实验三的重点也就是如何用链表实现冒泡排序而且如何在每一轮次中输出链表和个数。
  • 首先,咱们要清楚冒泡排序的原理。由于涉及到三种排序,选择排序,插入排序,冒泡排序。那么我就在这列统一辨析一下。接下来的选择排序便很少赘述。
    • 选择排序:应该是最好理解的,最为简单直观的排序方法,它每次都遍历数组, 从中选择出最大 (最小) 的元素,放在数组的第一位, 直到全部元素所有排序完成。 选择排序在不一样的场景下都会执行相同次数的遍历,因此性能不是很高。它的时间复杂度是O (n^2) 。
    • 插入排序:原理是经过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。 若是数组原本就是有序的, 那么此时的复杂度为O (n) ;若是数组原本是倒序的,那么插入排序的时间复杂度就为O (n^2)。 插入排序较适应于元素少的数组进行排序。
    • 有个形象的图来分析插入排序,斗地主大部分人都玩过吧:
    • 冒泡排序:原理是将先后每两个数进行比较,较大的数日后排,一轮下来最大的数就排到最后去了。而后再进行第二轮比较,第二大的数也排到倒数第二了,以此类推。它的最佳时间复杂度是O(n^2)。
  • 冒泡排序算法:
public void BubbleSort()
    {
        Point Node , CurrentNode;
        int temp;int nGuoKai = 0;
        for (Node = head;Node.next != null;Node = Node.next)
        {
            for (CurrentNode = head;CurrentNode.next != null; CurrentNode = CurrentNode.next)
            {
                if (CurrentNode.number>CurrentNode.next.number)
                {
                    temp = CurrentNode.number;
                    CurrentNode.setNumber(CurrentNode.next.number);
                    CurrentNode.next.setNumber(temp);
                    nGuoKai = length();
                }
            }
            PrintLinkedList();
            System.out.println();
            System.out.println("个数:" + nGuoKai);

        }
    }
  • 冒泡排序实现代码中,由于冒泡排序须要进行两个数的交换,因此咱们的指针类Point类须要有一个setNumber(int num)的方法。因此,我修改了Point类
public void setNumber(int number)
    {
        this.number = number;
    }
  • 在这个时候我是有疑问的,在每一轮中,输出链表简单,那么如何输出每次链表的个数。 链表不像数组,有固定的长度,那么也意味着咱们没法经过直接调用某个变量来计算链表的长度。因此,我给出了length()方法来计算链表的长度
public int length()
    {
        int length = 0;
        Point temp;
        temp = head;
        while (temp != null)
        {
            length++;
            temp = temp.next;
        }
        return length;
    }

其实也就是一个遍历链表的过程。而后让个人nGuoKai变量 = length(),即可以了。git

  • 实验结果截图:

实验四:

  • 建立数组的实现很是简单,这里用户交互的时候,我使用的是StringTokenizer类以弥补上一个链表实验的不足。
    这里直接给出测试类截图:
  • 在数组的添加和删除操做中,须要考虑数组内元素的移动。
public static int[] Insert(int[] nums, int position, int num)
    {
        // 对数组进行扩容
        int[] nums1 = new int[nums.length + 1];
        // 对数组进行扩容,不用新建一个数组。
        // nums = Arrays.copyOf(nums, nums.length + 1);
        //将原数组数据赋值给新数组
        for(int i = 0; i < nums.length; i++)
        {
            nums1[i] = nums[i];
        }
        //将大于position的数据向后移动一位
        for(int j = nums1.length-2;j>position - 1;j--)
        {
            nums1[j+1] = nums1[j];
        }
        //赋值到position位置
        nums1[position] = num;
        return nums1;
    }
  • 这里的添加操做,我是先对数组的尾端进行操做。由于数组扩容了,因此此时的尾端应该是空的。
    • 因此我让前一个元素来覆盖后一个的元素。这样并不会出现丢失元素的状况。一直到我要插入的那个索引值position那里,跳出循环,把要插入的值赋给position。最后返回数组。
    • 这里在测试类测试时发生了问题。详见问题三
    public static int[] Delete(int[]nums, int position)
      {
          // 对数组进行扩容
          int[] nums1 = new int[nums.length - 1];
          int i = 0;
          // 对数组进行扩容,不用新建一个数组。
          // nums = Arrays.copyOf(nums, nums.length - 1);
          //将原数组数据赋值给新数组
          for(int j = 0;j < nums.length ;j++)
          {
              if (position != j) {
                  nums1[i] = nums[j];
                  i++;
              }
          }
          //赋值到position位置
          return nums1;
      }
  • 删除操做也一样如此,把原来的数组从新复制到新的数组当中。当条件等于索引值时,就跳过便可。
  • 特别注意一点,像我注释中所说的那样,对数组进行扩容,不用新建一个数组。 咱们能够直接使用Arrays类中的CopyOf()方法。固然它的实现也是基于新建一个数组来进行赋值。异曲同工。
  • 最后是测试类代码和代码结果截图。

实验五:

  • 选择排序在上面也已经具体介绍过,并且上学期实现过数组的选择排序。因此这里很少介绍。
  • 数组的每一轮次的输出和个数就要比链表的简单了,由于数组的长度是固定的,而排序也并未改变数组的个数。
  • 这里直接给出代码和截图。


3. 实验过程当中遇到的问题和解决过程

  • 问题1:在思考IO流BufferedReader的时候,我记得须要有BufferedReader.close()的操做,来关闭输入的流,释放内存。
    可是我记得还有一个flush操做,彷佛是刷新缓冲区的,可是不记得具体的用法和用途了。 算法

  • 问题1解决方案:
    • 参考资料: FileWriter的flush问题
    • 首先,能够得知,flush方法是用于BufferedWriter的操做,是为了刷新缓冲区,使其写入文件。
    • 一样,FileOutputStream 继承 OutputStream 并不提供flush方法的重写。因此不管内容多少write都会将二进制流直接传递给底层操做系统,flush无效果,而Buffered系列的输入输出流函数。单从Buffered这个单词就能够看出他们是使用缓冲区的,应用程序每次IO都要和设备进行通讯,效率很低,所以缓冲区为了提升效率,当写入设备时,先写入缓冲区,等到缓冲区有足够多的数据时,就总体写入设备。
      flush会刷新缓冲区,而close方法内部是会先调用flush方法再执行关闭方法的,殊途同归。
  • 问题2:链表删除操做,致使了无限循环。没法进行后续的任何操做。
    如图:
    数组

  • 问题2解决方案:
    • 由图可见,我左边的滚动条已经拉到最低了。那么首先,要清楚问题出现的位置。这里一看就是删除中的操做陷入了无限循环,条件没有跳出,致使没法中止操做。
    • 这是个人删除操做。注释后,是更改之后的操做。
    • 以前没有加else,我并无想到会陷入无限循环。觉得当CurrentNode.number == element.number,跳出if,进行删除操做。
      可是,并无跳出while循环,这时,我还犯了第二个错误,我觉得无限循环是整个链表的从头至尾从头至尾的不断循环。其实否则,当CurrentNode.number != element.number时,CurrentNode就再也不改变了,也就是说CurrentNode一直不为空,而且一直不等于element.number。
    • 在小组成员的帮助下,成功解决了问题,在此感谢段志轩和李馨雨同窗。这个问题,暴露出了两个问题。第一,对于代码的分析和理解能力还不足。没有正确理解循环和跳出if和循环的条件。第二,对于代码的细心程度不够。没有考虑到if的多种可能,没有考虑到后续操做致使的后果。但愿之后以此为戒,多加改进。
  • 问题3:数组进行了添加操做,可是数组却没有变化。添加失败?
    性能优化

  • 问题3解决方案:
    • 在我进行了调试之后,发现Insert()方法后返回值是没有错误的。也就是说,对于Insert()方法是没有任何错误的。
    • 那么错误,就颇有可能发生在测试类的代码编写当中。问题中的截图分别是个人第一次错误代码,和第一次更改的代码。然而,虽然,第一次更改解决了插入而且输出新数组的操做,可是没法再对新数组进行操做。因此,最后只能,建立另外一个新的数组来存储排序后的数组。

其余(感悟、思考等)

  • 线性结构的应用,在上学期已经接触过,而且实现过部分。这是数据结构的基础。不少时候,咱们并不能熟练的掌握,好比说让你直接说出某种排序的原理和关键代码。不少仍是须要咱们不断积累和沉淀。
  • 慎终如始,则无败事。

参考资料

相关文章
相关标签/搜索