20172309 《Java软件结构与数据结构》实验三报告

课程:《程序设计与数据结构(下)》
班级:1723
姓名: 王志伟
学号:20172309
实验教师:王志强老师
实验日期:2018年11月2日
必修/选修: 必修

实验内容:

实验一:

1333063-20181204214219706-418684583.png

实验二:

1333063-20181204214340871-113553659.png

实验三:

1333063-20181204214403395-482364714.png

实验四:

1333063-20181204214631742-1741522442.png

实验五:

1333063-20181204214659973-847335760.png

实验过程及结果:

实验一:

  • 定义一个Searching和Sorting类,并在类中实现linearSearch(教材P162 ),SelectionSort方法(P169)。
    • Searching类的实现linearSearch方法。
     public static <T> boolean linearSearch(T[] data, int min, int max, T target)
     {
         int index = min;
         boolean found = false;
    
         while (!found && index <= max)
         {
             found = data[index].equals(target);
             index++;
         }
    
         return found;
     }//顺序/线性查找
    这个算法的理念是一个一个查找,直到找到为止。不过个人觉得这个算法的缺点是当有两个目标值时,将不会查到第二个。比如:在数组{1,2,3,4, 5, 5,6 }中查找数字5时,将只能查找到索引为4的数字5,而不会查找到索引值为5的那个。
    • 测试代码:
      1333063-20181204220727378-1187733438.png
    • 测试结果:
      1333063-20181205084426216-22129514.png
    • Sorting的selectsort方法:
    public static <T extends Comparable<T>>//选择排序
     String selectionSort(T[] data) {
         int min;
         T temp;
    
         for (int index = 0; index < data.length - 1; index++) {
             min = index;
             for (int scan = index + 1; scan < data.length; scan++)
                 if (data[scan].compareTo(data[min]) < 0)
                     min = scan;
    
             swap(data, min, index);
         }
    
         return Arrays.toString(data);
     }
    选择排序的理念就是把一排数列中的东西中的最大(最小)放到最前面并与之交换位置,之后又从剩下的算法中选择出最大(最小)的与第二个项目交换位置·····直至全部完成。这里值得说的一个重点是Arrays.toString(data);这个方法,这个方法的用处是把一个数组自动ToString(),也就是不用自己写toString方法。
    • 代码截图:
      1333063-20181204222054114-1203147654.png
    • 结果截图:
      1333063-20181205084405182-706496089.png
  • 要求不少于10个测试用例,提交测试用例设计情况(正常,异常,边界,正序,逆序),用例数据中要包含自己学号的后四位。
    • 这里要求测试用例需要正常、异常、边界、正序、逆序我感觉有点错误,比如线性查找哪来的正序、逆序测试,选择排序哪来的边界测试。不是很懂,感觉有问题~~~

      实验二:

  • 把Sorting.java,Searching.java放入cn.edu.besti.cs1723.(姓名首字母+四位学号)包中(例如:cn.edu.besti.cs1723.G2301)。并把测试代码放test包中。
    1333063-20181205084928603-585059599.png
    1333063-20181205084947908-1772901325.png

    一开始并不知道cn.edu.besti.cs1723.什么意思,其实到文件中去看就会发现他这并不是一个文件夹,而是cn的文件夹下有edu,edu的文件夹下有besti....

  • 重新编译,运行代码,提交编译,运行的截图(IDEA,命令行两种)
    • 用IDEA测试结果与实验一一样,因此在这不放图。
    • 用命令行进行测试:由于老师没有明确表达必须使用Junit测试,因此我选择了使用main函数测试(因为虚拟机使用命令行还得安装一个Junit.jar包~~~ 而安装包的什么命令都是上学期的事儿了,哪还记得 ?)。
      查找结果:
      1333063-20181205090210079-1690634163.png
      排序结果:
      1333063-20181205090231551-1759990086.png
  • 开心的是好像IDEA也有命令行模式,不过这是在我做完实验之后才发现的o(╥﹏╥)o
    1333063-20181205090610310-234443201.png
    如果没有显示的话,可以手动开启:
    1333063-20181205090716192-2133450224.png

实验三:

  • 代码详情
  • 参考http://www.cnblogs.com/maybe2030/p/4715035.html 在Searching中补充查找算法并测试提交运行结果截图。我们将一个个讲解下查找算法:以前在博客中讲过的一笔带过,没讲的是重点
    • 顺序查找:
      • 主要思想:顺序查找也称为线形查找,属于无序查找算法。从数据结构线形表的一端开始,顺序扫描,依次将扫描到的结点关键字与给定值k相比较,若相等则表示查找成功;若扫描结束仍没有找到关键字等于k的结点,表示查找失败。说明:顺序查找适合于存储结构为顺序存储或链接存储的线性表。
      • 时间复杂度为:O(n)
      • 方法代码:
        ```
        public static <T extends Comparable>
        boolean binarySearch(T[] data, int min, int max, T target)
        {
        boolean found = false;
        int midpoint = (min + max) / 2; // determine the midpoint

        if (data[midpoint].compareTo(target) == 0)
        found = true;

        else if (data[midpoint].compareTo(target) > 0)
        {
        if (min <= midpoint - 1)
        found = binarySearch(data, min, midpoint - 1, target);
        }

        else if (midpoint + 1 <= max)
        found = binarySearch(data, midpoint + 1, max, target);

        return found;

    }//二分查找
    ```

    • 二分查找:
      • 主要思想:也称为是折半查找,属于有序查找算法。用给定值k先与中间结点的关键字比较,中间结点把线形表分成两个子表,若相等则查找成功;若不相等,再根据k与该中间结点关键字的比较结果确定下一步查找哪个子表,这样递归进行,直到查找到或查找结束发现表中没有这样的结点。元素必须是有序的,如果是无序的则要先进行排序操作。
      • 时间复杂度:O(log2n)
      • 方法代码:
        ```
        public static <T extends Comparable>
        boolean binarySearch(T[] data, int min, int max, T target)
        {
        boolean found = false;
        int midpoint = (min + max) / 2; // determine the midpoint

      if (data[midpoint].compareTo(target) == 0)
      found = true;

      else if (data[midpoint].compareTo(target) > 0)
      {
      if (min <= midpoint - 1)
      found = binarySearch(data, min, midpoint - 1, target);
      }

      else if (midpoint + 1 <= max)
      found = binarySearch(data, midpoint + 1, max, target);

      return found;

    }//二分查找

    ```

    • 插值查找:
      • 主要思想:
        1. 基于二分查找,属于有序查找,相对于二分查找有时能够提升效率。
        2. 对于表长较大,而关键字分布又比较均匀的查找表来说,插值查找算法的平均性能比折半查找要好的多反之,数组中如果分布非常不均匀,那么插值查找未必是很合适的选择
        3. 个人觉得最重要的就是这个了:mid=low+(key-a[low])/(a[high]-a[low])*(high-low)为什么是这样?在下面的的问题中讲。
      • 时间复杂度:查找成功或者失败的时间复杂度均为O(log2(log2n))。
      • 方法代码:
      public static   boolean interpolationSearch(int[] data,int low,int high,int target){
       while (low<high) {
           int mid=low+(high-low)*((target-data[low])/(data[high]-data[low]));
           if (target<data[mid]) {
               interpolationSearch(data, low, mid-1, target);
           }
           else if (target>data[mid]) {
               interpolationSearch(data, mid+1, high, target);
           }
           else {
               return data[mid]==target;
           }
       }
       return false;
       }//插值查找,不能使用泛型
    • 斐波那契查找:
      • 主要思想:也是二分查找的一种提升算法,通过运用黄金比例的概念在数列中选择查找点进行查找,提高查找效率。同样地,斐波那契查找也属于一种有序查找算法
      • 斐波那契查找算法
      • 时间复杂度:O(log2n)
      • 方法代码:
       public static boolean FibonacciSearch(int[] data,int target){
      int low = 0;
      int high = data.length - 1;
      int mid = 0;
      // 斐波那契分割数值下标
      int k = 0;
      // 序列元素个数
      int count = 0;
      // 获取斐波那契数列
      int[] f = new int[20];
      int m = 0;
      f[0] = 1;
      f[1] = 1;
      for (m = 2; m < 20; m++) {
          f[m] = f[m - 1] + f[m - 2];
      }
      // 获取斐波那契分割数值下标
      while (data.length > f[k] - 1) {
          k++;
      }
      // 创建临时数组
      int[] temp = new int[f[k] - 1];
      for (int j = 0; j < data.length;j++){
          temp[j] = data[j];
      }
    • 二叉树查找:
      • 主要思想:
        1. 二叉查找树是先对待查找的数据进行生成树,确保树的左分支的值小于右分支的值,然后在就行和每个节点的父节点比较大小,查找最适合的范围。
        2. 这个算法的查找效率很高,但是如果使用这种查找方法要首先创建树
        3. 对二叉查找树进行中序遍历,即可得到有序的数列。
      • 时间复杂度:它和二分查找一样,插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度。
      • 方法代码:
      详情看上面链接
    • 分块查找:
      • 主要思想:将n个数据元素"按块有序"划分为m块(m ≤ n)。每一块中的结点不必有序,但块与块之间必须"按块有序";即第1块中任一元素的关键字都必须小于第2块中任一元素的关键字;而第2块中任一元素又都必须小于第3块中的任一元素,……
      • 算法步骤:
      1. 先选取各块中的最大关键字构成一个索引表;
      2. 查找分两个部分:先对索引表进行二分查找或顺序查找,以确定待查记录在哪一块中;然后,在已确定的块中用顺序法进行查找。
      • 时间复杂度:
      • 方法代码:
      //详情看上面链接
    • 哈希查找:
      • 主要思想:哈希的思路很简单,如果所有的键都是整数,那么就可以使用一个简单的无序数组来实现:将键作为索引,值即为其对应的值,这样就可以快速访问任意键的值。这是对于简单的键的情况,我们将其扩展到可以处理更加复杂的类型的键
      • 算法步骤:
        1. 用给定的哈希函数构造哈希表
        2. 根据选择的冲突处理方法解决地址冲突;常见的解决冲突的方法:拉链法和线性探测法。详细的介绍可以参见:浅谈算法和数据结构: 十一 哈希表。
        3. 在哈希表的基础上执行哈希查找
      • 时间复杂度:O(1)
      • 方法代码:
      //详情看上面链接

      实验四:

  • 补充实现课上讲过的排序方法:希尔排序,堆排序,二叉树排序等(至少3个),测试实现的算法(正常,异常,边界)。
    • 希尔排序:
      • 主要思想:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。
      • 举个例子:
        1333063-20181205161600230-1655538038.png
    • 堆排序:
      • 这个因为以前就写好了最小堆,每次把最小的顶堆取出来,就完成了从小到大的排序。
    • 二叉排序:
      • 和上面一样,因为二叉查找树的左子树小于或等于父结点,父结点小于或等于右结点。所以使用中序遍历就可以完成从小到大的排序。
      • 代码截图:
        1333063-20181205184957954-1979513127.png
      • 提交运行结果截图。
        1333063-20181205161956775-132413101.png

实验五:

  • 编写Android程序对各种查找与排序算法进行测试
    • emmm·····本来看到这的时候就准备使用一个下拉列表的,但是做了四五个小时后发现出现了bug,所以为了提交作业只好提交了一种比较low的方法。
    • 运行结果:
      1333063-20181205192704623-1248264066.png
    • 测试结果:
      1333063-20181205192842222-1934613418.png
    • 不过在这里学到了一种比较好用的方法:
      • 在xml文件中编写按钮Button控件时,可以使用onclick属性。像这样:
      <Button
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="创建数据库"
       **android:onClick="creatDb"**<--这儿
       android:background="#ffbbff"
       />
      • 那么这个方法有什么用呢?
        个人总结就是当有很多按钮时,我们不必每个按钮都写点击事件了只要写一个就OK了,例:
      public void onclick(View view){
      switch (条件){
       case R.id.button1:
       ···逻辑代码···
       break;
       case R.id.button2:
       ···逻辑代码···
       break;
       ·····
      }
      

试样过程中遇到的问题:

  • 问题一:如何进行Junit测试?
  • 问题一解决方案:现在自己来总结下吧。
    • emmm·····,首先选中要进行Junit测试的类名并右击:Go to->Generate->Test
      1333063-20181205185816487-1478284137.png
    • 之后点击Create new Test
      1333063-20181205185851734-708386203.png
    • 之后选择Junit4,其次选择要进行测试的方法。
      1333063-20181205190116201-815431108.png
    • 之后会看到自动生成的方法:例:
     @Test
     public void shellSort() {
    
     }
    • 然后使用assertEquals(参数一,参数二)方法,例:
    @Test
    public void shellSort() {
       assertEquals(预定结果,测试的方法);
       //例:assertEquals("[2301, 2302, 2303, 2305, 2307, 2309]",Sorting.shellSort(list1,3));//正常
    }
  • 问题二:插值查找中的mid=low+(key-a[low])/(a[high]-a[low])*(high-low)是怎么来的?
  • 问题而解决方案:
  • 先举两个例子
    • 打个比方,在英文字典里面查“apple”,你下意识翻开字典是翻前面的书页还是后面的书页呢?如果再让你查“zoo”,你又怎么查?很显然,这里你绝对不会是从中间开始查起,而是有一定目的的往前或往后翻。
    • 同样的,比如要在取值范围1 ~ 10000 之间 100 个元素从小到大均匀分布的数组中查找5, 我们自然会考虑从数组下标较小的开始查找。
  • 经过以上分析,折半查找这种查找方式,不是自适应的(也就是说是傻瓜式的)。二分查找中查找点计算如下:
      mid=(low+high)/2, 即mid=low+1/2(high-low);
    • 通过类比,我们可以将查找的点改进为如下:
        mid=low+(key-a[low])/(a[high]-a[low])*(high-low),
    • 也就是将上述的比例参数1/2改进为自适应的,也就是将1/2改进为 (key-a[low])/(a[high]-a[low])。根据关键字在整个有序表中所处的位置,让mid值的变化更靠近关键字key,这样也就间接地减少了比较次数

收获感悟:

补博客可真是麻烦,拿起了好久都没动过的IDEA(因为最近在做实战项目)。

参考文献:

转载于:https://www.cnblogs.com/dky-wzw/p/10067415.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值