20172323 2018-2019-1 《程序设计与数据结构》第五周学习总结

20172323 2018-2019-1 《程序设计与数据结构》第五周学习总结

教材学习内容总结

本周学习了第六章排序与查找


一些概念

  • 静态方法可以通过类名来激活,不用特意实例化该类的一个对象调用该静态类的方法。在方法声明中,通过使用static修饰符就可以把它声明为静态的
  • 创建泛型方法,只需在方法头前的返回类型前插入一个泛型声明即可。泛型声明必须在返回类型之前,这样泛型才可作为返回类型的一部分
public static <T extends Comparable<T>> boolean
  linearSearch (T[] data, int min, int max, T target)

查找

  • 在某个项目组中寻找某一指定目标元素,或确定该指定目标不存在,是为查找。进行查找的项目组被称为查找池
  • 线性查找法:从头开始依次比较每一个值,直到找到目标元素,或者得出不存在该目标值的结论,这种方式称为线性查找(linear search)
  • 二分查找(折半查找):当查找池中的项目组是已排序的,那么利用二分查找将会更有效率。
    • 二分查找的思路: 二分查找从排序列表的中间开始查找,如果中间元素不是目标元素,根据两个元素的大小关系,再判断从列表的前一半或是后一般进行查找。每次的查找都是从当前一串数字的中间元素开始的。直到最后找到该元素或是没有找到抛出一个信息。
    • 二分查找的每次比较都会删除一半的可行候选项,当查找池中有偶数个待查找值时,选择的是两个中间值的第一个。
    • 二分查找的复杂度是对数级的,这使得它对于大型查找池非常有效率。线性查找具有线性时间复杂度O(n),二分查找具有时间复杂度O(log2n)。

排序

  • 基于某一标准,将某一项目组按照某个规定排序排列
  • 选择排序法
    • 反复寻找当前列表的最小值,然后将其放在列表的前端。
      1332964-20181017171332621-297534876.jpg
  • 插入排序法
    • 将数字插入前端已排序的列表的合适位置直至排序完成。
      1332964-20181017171346421-1037185328.jpg
  • 冒泡排序法
    • 反复比较相邻元素的大小关系进行移位操作直至排序完成
      1332964-20181017171359952-1370433057.gif
  • 快速排序法
    • 选择一个列表元素进行分区元素,然后对两个分区进行递归排序直至排序完成(一个分区只含有一个元素2)。
    • 排序时两个分区同时进行查找,一旦两边都找到一个元素,将两者进行互换
      1332964-20181017171412876-1529752139.jpg
  • 归并排序法
    • 将列表递归式分成两半直至每一子列表都含有一个元素,然后再归并到一个排序顺序中完成排序
      1332964-20181017171428761-1773964114.jpg
  • 基数排序法
    • 基数排序基于队列处理
      1332964-20181017213056294-1050728927.gif

教材学习中的问题和解决过程

  • 问题1:选择排序的代码实现中出现了public static <T extends Comparable<? super T>> void selectionSort (T[] data)之中<? super T>表示的是什么意思
  • 问题1解决方案:查阅资料在网上找到了一个通俗易懂的回答,大致上把<? super T>三个部分都讲得比较清楚了。按照我的理解,在这里?代表的是通配符,这一整串代表的是一个下界通配符,用在此处代表Comparable接口中的泛型元素都是T或者T的父类,这样就放松了存入数组的类型限制,但是引起的缺点就是往外读取元素就会变得困难,可能使元素类型信息丢失。

  • 问题2:几种排序方法时间复杂度的比较
  • 问题2解决方案
排序方法时间复杂度
选择排序O(n^2)
插入排序O(n^2)
冒泡排序O(n^2)
快速排序O(n*log2n)
归并排序O(nlog2n)
基数排序O(n)

三种顺序排序方法都使用了内外两层循环进行排序操作,所以时间复杂度都为nxn-->O(n^2);
快速排序和归并排序两种对数排序在排序中采取了递归的方法,因此在元素比较中只用到了大约nlog2n次比较,时间复杂度为O(log2n);还有一种特殊的基数排序,因为在排序过程中不涉及到元素之间的比较,只需要将元素在队列之间进行移动操作,操作时只用遍历数据即可,因此可以用c*n来表示(c是常数),所以时间复杂度为O(n)。

代码调试中的问题和解决过程

  • 问题1:PP9.2中间隔排序的思路有些混乱,为什么每次迭代完成之后i要减少某个大于1的数量直至i的值小于1.
  • 问题1解决方案:在网上没能找到关于间隔排序的资料,所以不太懂原理,应该也和冒泡排序差不太多吧,就按照题目的要求拼好了代码,可运行出来的结果排序似乎并没有完成。

    假设现在有一个长度为6的数组,第一次的i为3,每一次迭代后i减少的值为2,那么在第一次迭代中,第一个元素与第三个元素进行比较,第二个元素与第四个元素进行比较,第三个元素与第五个元素进行比较,第四个元素与第六个元素进行比较,第一次迭代结束,i的值为1,第一个元素与第二个元素进行比较...相邻元素分别进行比较,迭代结束,i的值为-1小于1,此时循环结束,排序完成。
    1332964-20181017162424046-2013865490.jpg

如果每次i的值减少1
1332964-20181017162500487-1578960474.jpg

与题目给的信息恰恰想反,反倒是每次减一时能得出正确的排序,多次修改代码中的间隔值,发现i每次减一时排序都能正确完成但是减二、减三时只能是在部分间隔值的情况下才能排序完成


  • 问题2:PP9.3如何用代码实现总的比较次数和总执行时间的计数
  • 问题2解决方案
    计时
    最开始应用的代码如下
Date da = new Date();
long s1 = da.getTime();
...//具体运行的代码
long s2 = System.currentTimeMills();
System.out.println(s2 - s1);

代码开始运行时记录当前时间,代码结束前再记录一下当前时间,最后s2-s1即为运行时间,思路是没有问题,可是运行结果出来就有点小瑕疵,如图
1332964-20181017162642242-1824267549.png

运行时间全为0且没带单位,我觉得系统并没有错,可能真是运行时间太短吧,小于一秒的计算机就自动四舍五入归为0了(?),我是这样认为的,所以我又寻找到另一种比较精密计量时间的方法
以毫秒为单位计算的

long startTime = System.currentTimeMillis();//获取当前时间
doSomeThing();
long endTime = System.currentTimeMillis();
System.out.println("程序运行时间:"+(endTime-startTime)+"ms");

以纳秒为单位计算的

long startTime = System.nanoTime();//获取当前时间
doSomeThing();
long endTime = System.nanoTime();
System.out.println("程序运行时间: "+(end-start)+"ns"); 

好似万事俱备,运行一下试试,问题还是出现了,毫秒计时依然为0额。
此时我又找到了Date.getTime的说明
Returns the number of milliseconds since January 1, 1970, 00:00:00 GMT represented by this Date object
,显然这个方法同样是以毫秒为单位来计量代码运行的时间的,之前什么四舍五入的解释就有问题了,在网上寻找答案也只得到一些毫不沾边的东西,有点一筹莫展,后来我再仔细一瞅,发现纳秒计时的方法似乎没有出错啊,于是改成纳秒计时,然后就运行成功???有点莫名其妙。
1332964-20181017162737576-1378843602.png

原以为计时在稀里糊涂之中就可以全部解决了,把五大排序法中分别加上s1、s2,然后运行一下,这次又是归并排序的计时出现了些问题,如图
1332964-20181017162754209-1773895888.png

debug一下,错误应该出在忽略了递归上面,就是计算机每执行一次mergeSort的方法,就得重新开始计时一次,所以最后的结果会输出很多个归并排序的计时,参照快速排序的方法,相当于在归并排序的外面加一层壳专门用来计时,其他也想不出更好的办法了,最终结果如图
1332964-20181017162806822-46607610.png


比较次数

  • 对于选择排序、插入排序和冒泡排序,其内层循环都为比较相邻两元素的大小,外层控制存储的位置,所以只需在内层写入一个计数的变量count,每执行一次内层循环count+1.
  • 对于快速排序和归并排序,只要进行了一次compareTo操作count就需要加一,无论是否满足while循环的条件,因此while循环内外都应该有语句count++。

代码托管

1332964-20181017163456348-996280884.png

上周考试错题总结

上周没有错题哦

结对及互评

  • 博客中值得学习的或问题:
    • 谭鑫的博客一直保持很高的水准,特别是问题的总结和解决能力都值得学习,能看出来是在用心学习
    • 方艺雯的博客教材学习内容总结认真详细,并配有图示和自己的理解,点个赞
  • 基于评分标准,我给谭鑫的博客打分:8分。得分情况如下:
    正确使用Markdown语法(加1分):
    模板中的要素齐全(加1分)
    教材学习中的问题和解决过程, 三个问题加3分
    代码调试中的问题和解决过程, 三个问题加3分

  • 基于评分标准,我给方艺雯的博客打分:8分。得分情况如下:、
    正确使用Markdown语法(加1分):
    模板中的要素齐全(加1分)
    教材学习中的问题和解决过程, 两个问题加2分
    代码调试中的问题和解决过程, 三个问题加3分
    排版精美加1分

  • 本周结对学习情况
    • 20172305
    • 20172314
    • 结对学习内容
      • 排序与查找
      • PP9.4中的比较次数经过反复讨论得到了比较一致的答案
  • 上周博客互评情况

其他

这周在代码学习上还是遇到了一些小的问题,在资料缺乏的情况下,自己动手写代码的能力、分析问题的能力有了一些加强,但还是稍显欠缺,需要继续努力

学习进度条

代码行数(新增/累积)博客量(新增/累积)学习时间(新增/累积)重要成长
目标5000行30篇400小时
第一周0/01/18/8
第二周470/4701/212/20
第三周685/11552/410/30
第四周2499/36542/612/42
第六周1218/48722/810/52

参考资料

转载于:https://www.cnblogs.com/Lewandodoski/p/9805039.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值