20172303 2018-2019-1《程序设计与数据结构》第4周学习总结
教材学习内容总结
一、列表概述
- 概念:列表是使事物以线性的方式进行组织的线性集合。
- 特点:列表集合的容量可以随着需要而增大;列表集合可以在列表的中间和末端添加或删除元素。
- 分类:列表有有序列表、无序列表和索引列表三种类型。
1.Java集合API中的列表
- Java集合API提供的列表类主要是支持索引列表。
- Java集合API中提供了使用数组实现的
ArrayList类
和使用链表实现的LinkedList类
,它们都可以存储由泛型参数E定义的元素,同时也都实现了List接口。 - List接口中的一些方法:
- 列表ADT:列表ADT中包含的是有序列表和无序列表通用的一些操作,包括:
- removeFirst:从列表中删除第一个元素。
- removeLast:从列表中删除最后一个元素。
- remove:从列表中删除某个元素。
- first:查看位于列表前端的元素。
- last:查看位于列表末端的元素。
- contains:确定列表是否含有某个元素。
- isEmpty:确定列表是否为空。
- size:确定列表中的元素数量。
2.有序列表
- 概念:其元素按照元素的某种内在特性进行排序的列表。
- 只有Comparable对象才能存储到有序列表中。
- 在列表中添加元素
- 由于有序列表基于元素的某个关键值排序,所以对于任何一个要添加进列表的元素,只要给定了元素的关键值,它在列表中就会有一个固定的位置。
- 所以要实现在有序列表中添加元素的操作,只需要设置一个add操作即可。因为在实现该操作时它会自己根据关键值的比较来确定在列表的头部、中部或尾部进行插入。
3.无序列表
- 概念:其元素按照他们在列表中的位置进行排序的列表。
- 注意:无序列表虽然叫做“没有顺序的列表”,但并不代表它就是随机生成的,相反,它其实也是按照某种特殊顺序排列的,但这种顺序是由使用者来决定的,与元素自身性质无关。
- 在列表中添加元素
- 由于无序列表的顺序是由使用者决定的,所以要实现无序列表添加元素的操作,需要给使用者提供所有能添加的可能,即addToFront(在列表前端添加)addToRear(在列表末端添加)和addAfter(把元素添加到列表中某元素的后面)三种操作。
4.索引列表
- 概念:其元素可以用数字索引来引用的列表。
- 索引列表与无序列表类似,唯一的不同之处是索引列表中的每个元素都能通过一个索引值来得到引用,索引值从0开始。
- 索引列表与数组的区别:当在数组中删除元素后,其他的元素位置不会发生改变。而在索引列表中删除元素后,其他的元素位置会产生移动以消除因删除元素而产生的间隙。
二、用数组实现列表
- 基于数组实现的列表首先要把列表的一端固定在索引0处,设置一个整数变量rear表示列表中的元素数目,同时表示列表末端的下一个可用位置。
- remove操作:这个操作用于删除列表中的指定元素,在一般情况下需要进行数次比较和平移操作,最多的时候(当要删除的元素是列表的最后一个元素时)需要进行n次比较和平移操作,所以其时间复杂度为O(n)。
- remove操作使用了一个find操作,其作用是用于查找指定的元素是否存在。将find操作独立出来的好处有三点:(1)使remove操作变得简单。(2)find操作同样可以应用于其他操作,例如contains操作。(3)该方法不会抛出异常,所以调用该方法的程序可以自行定义处理元素未找到时的操作。
- contains操作:该操作用于判断指定元素是否在列表中,所以会进行多次比较操作,最多的时候(当所找元素不在列表中时要遍历整个列表)需要进行n次比较操作,所以其时间复杂度为O(n)。
- add操作:无论是有序列表的add操作还是无序列表的addToFtont和addAfter操作,都与remove操作类似,需要进行多次比较和平移操作,所以其时间复杂度为O(n)。addToRear操作与栈的push操作类似,时间复杂度为O(1)。
在队列中使用环形数组时,可以把dequeue的效率从O(n)变为O(1),因为它不需要平移数组,但是在列表中因为我们可以在任何位置随意添加删除元素,所以平移数组的操作无法省略,因此是否使用环形数组就没有那么重要了。
三、用链表实现列表
- 使用链表实现列表在某些方面与上一章实现的Deque有些类似,很多方法都是相同的。
- remove操作:链表实现的remove操作不需要平移元素,但是这个操作可能会遇到四种情况:列表只有一个元素、删除列表的首元素、删除列表的尾元素和删除列表的中间元素。而在其中仍然存在要进行n次比较操作的情况,因此其时间复杂度也为O(n)。
- contains操作:链表的contains操作需要寻找元素,因此其内容与remove操作十分相似,最开始都要编写确定目标元素的方法,所以其时间复杂度也为O(n)。
教材学习中的问题和解决过程
- 问题1:书上有些代码中提到的
instanceof
是什么?怎么用?
public boolean equals(Object other)
{
boolean result = false;
if (other instanceof Course)
{
Course otherCourse = (Course) other;
if (prefix.equals(otherCourse.getPrefix()) &&
number == otherCourse.getNumber()) {
result = true;
}
}
return result;
}
- 问题1解决方案:查JDK无果,网上的解释是:instanceof是一个简单的二元操作符,它是用来判断一个对象是否是一个类实例的。简单来说它和“==”的作用有些类似,但又不完全一样,instanceof是存在许多限定条件的,比如说不是随便两个类型之间都能用instanceof的,如果两个操作数的类型都是类,其中一个必须是另一个的子类型。
boolean b1 = new String() instanceof String;
//b1为true
boolean b2 = "Sting" instanceof Object;
//b2为true,因为String是Object的子类
boolean b3 = new Object() instanceof String;
//b3为false,Object是父类
boolean b4 = 'A' instanceof Character;
//编译不通过,‘A’在此处视为基本数据类型char,instanceof操作符只能用作对象的判断
boolean b5 = null instanceof String;
//b5为false,这是instanceof特有的规则:若左操作数为null,结果就直接返回false,不再运算右操作数是什么类。
boolean b6 = (String)null instanceof String;
//b6为false,即使类型转换还是个 null
boolean b7 = new Date() instanceof String;
//编译不通过,instanceof操作符的左右操作数必须有继承或实现关系,否则编译出错
代码调试中的问题和解决过程
- 问题1:在进行数组实现的无序列表测试时,addToFront方法出现错误。
- 问题1解决方法:通过DeBug发现我在for循环里写的条件是错的,导致除了最新加入的元素,其余元素都等于第二个元素。
这是由于条件的顺序问题导致的,将原来的顺序变为逆序即可解决问题。
- 问题2:在进行链表实现无序列表的测试中,addAfter总是将所插元素插到指定位置之前而不是之后。
- 问题2解决方法:问题出在了与
target
进行比较的元素上面,假如我设定的元素是temp
,它指的是用于遍历链表的指针现在所指的节点的下一个节点,当它指向的是所要找的目标元素时,实际要插入的位置(即current指向的节点)是目标元素的前一个,所以最后会导致,addAfter将所插元素插到指定位置之前而不是之后。将与target
进行比较的元素改为current
即可。
- 问题3:在使用链表实现的列表的removeLast操作和last操作时,弹出
NullPointerException
- 问题3解决方法:
作为本学期敲代码的过程中遇到的次数最多的异常,现在每次看到它我感觉我的内心都是平静与想摔电脑交织的。因为我在完成这部分的时候参考的是上一章写过的Deque类
,当时它的add和remove都是在一个类中写的,所以tail会发生改变。但是在列表中remove操作与add操作并不在一个类里,所以LinkedList类
中的tail从开始设定为null之后,如果直接引用的话理所当然要抛出异常。
//原last方法
public T last() throws EmptyCollectionException
{
if (isEmpty()){
throw new EmptyCollectionException("LinkedList");
}
return tail.getElement();
}
//修改过的last方法
public T last() throws EmptyCollectionException
{
if (isEmpty()){
throw new EmptyCollectionException("LinkedList");
}
LinearNode<T> temp = head;
T result = temp.getElement();
while (temp.getNext() != tail){
temp = temp.getNext();
}
tail = temp;
return tail.getElement();
}
代码托管
上周考试错题总结(正确为绿色,错误为红色)
第3章和第4章
- 错题1:The implementation of the collection operations should affect the way users interact with the collection.
- A .true
- B .false
- 错题1解决方法:做题的时候感觉自己是见过这个表述的,但是当时翻书没有找到,后来在好好看了一遍发现书的37页和44页都有提到这句话。的确,假如集合操作在实现的过程中如果影响到交互方式,比如这个方法用链表,那个方法用数组,那么结果可想而知将会是非常杂乱的。
- 错题2:An array dynamically grows as needed and essentially has no capacity limitations.
- A .true
- B .false
- 错题2解决方法:这个题纯粹就是手残点错了...很生气自己错的两道题都不是什么难题,错误原因也不是知识点没掌握而是粗心大意,以后要尽量避免这种情况再次发生。
第5章
- 本章没有错题。
结对及互评
点评模板:
- 博客中值得学习的或问题:
- 优点:代码编写时遇到的问题记录非常详细。
- 问题:上周的错题分析应该有两次,我的结对伙伴又和上学期一样忘掉了其中一次的。
- 代码中值得学习的或问题:
- 问题:依旧是commit的问题,本周没有改进反而显得更敷衍了...看情况所以代码是一次commit全部提交的。
点评过的同学博客和代码
- 本周结对学习情况
- 20172322
- 结对学习内容
- 主要探讨了如何用链表实现有序列表和无序列表。
- 相互帮忙解决运行过程中出现的问题。
其他(感悟、思考等,可选)
- 早早结束国庆假期,四号就做到教室里开始学习,可能因为每天都用很大块很大块的时间来学习,所以觉得本周学习的内容并不是很难,学习的过程中还很有乐趣。
- 非常欣慰的是这回终于记起来在完成作业的过程中遇到问题就及时截图记录,不会再像之前明明做的过程中问题不少但是最后总结的时候什么都想不起来。
- 可能比起过程我更重视结果吧_(:з」∠)_
学习进度条
代码行数(新增/累积) | 博客量(新增/累积) | 学习时间(新增/累积) | 重要成长 | |
---|---|---|---|---|
目标 | 5000行 | 30篇 | 400小时 | |
第一周 | 10/10 | 1/1 | 10/10 | |
第二周 | 246/366 | 2/3 | 20/30 | |
第三周 | 567/903 | 1/4 | 10/40 | |
第四周 | 2346/3249 | 2/6 | 20/60 |
- 计划学习时间:20小时
- 实际学习时间:20小时
- 改进情况:刚看到本周的代码量时真的吓了一跳,能在一个星期左右的时间里敲两千行代码,感觉还是有点厉害的_(:з」∠)_