20172305 2018-2019-1 《Java软件结构与数据结构》第四周学习总结
Posted sanjinge
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20172305 2018-2019-1 《Java软件结构与数据结构》第四周学习总结相关的知识,希望对你有一定的参考价值。
20172305 2018-2019-1 《Java软件结构与数据结构》第四周学习总结
教材学习内容总结
本周内容主要为书第六章内容:
- 列表
- 有序列表(元素按照元素内在特性进行排序)
- 无序列表(元素间不具有内在顺序,元素按照他们在列表中的位置进行排序)
- 索引列表(元素可以用数字索引来引用)
列表集合比栈和队列更一般化,可以在列表的中间和末端添加和删除元素。
有序列表是基于列表中元素的某种特性的。对于任何已添加到有序列表中的元素,只要给定了元素的关键值,同时列表已定义了元素的所有关键值,那么它在列表中就会有一个固定的位置。
无序列表中各元素的位置并不基于元素的任何内在特性。列表中的元素是按照特殊顺序放置的,只不过这种顺序与元素本身无关。
索引列表的各元素间不存在能够决定他们在列表中的顺序的内在关系。每个元素都能够从一个数字索引值得到引用,该索引值从列表头开始从0连续增加直到列表末端。
索引值与数组的根本区别:索引列表的索引值总是连续的?
Java集合API提供的列表类主要是支持索引列表。Java API没有任何直接实现的有序列表。
- 有序列表与无序列表之间的差别主要体现在往列表添加元素上。
- 有序列表中只需制定要添加的新元素,该元素在列表中的位置取决于其键值。
- 无序列表中可以把元素添加到列表的前端、末端或是列表中某个已有的元素后面。
Serializable接口的作用是实现串行化,为了使某个对象能使用串行化进行存储。
Java 序列化技术可以将一个对象的状态写入一个Byte 流里(串行化),并且可以从其它地方把该Byte 流里的数据读出来(反串行化)。
只有Comparable对象才能存储在有序列表。
接口可以用来派生其它接口,子接口包含父接口的所有的抽象方法
接口名可以用来声明一个对象引用变量。一个接口引用可以用来引用实现了该接口的任意类的任意对象。
接口允许创建多态引用,其中被调用的方法是基于被引用时的特定对象的。
教材学习中的问题和解决过程
- 问题1:数组与索引列表的索引值是否连续的问题?
问题1解决方案:书上说的索引列表与数组的根本区别是索引列表的索引值是连续的。那么,数组的索引值就不连续么?可以从0开始,下一个直接到5么?仔细想想是不很可能的,那么书上的话是什么意思呢?网上并没有相关的答案,所以我想了一下觉得指的是数组可以在容量允许的前提下跳跃式的往数组内添加元素,可以在0索引值处添加元素,然后在5索引值处添加元素,中间的索引值内都为零。而所以索引列表不同,只能连续的往里面添加,不能中间出现断档。
- 索引列表为它的元素维护了一段连续的数字索引值。
- 问题2:列表用普通数组而不是环形数组的原因?
问题2解决方案:队列用环形数组的原因是只在一头出另一头进,而列表的添加方法比较多,其中就要求在中间进行添加,此外删除的时候也要求既可以删头又可以删尾的。所以,使用环形数组并不方便,头部变量和尾部变量都在动,插中间会更麻烦。
代码学习中的问题和解决过程
- 问题1:有序列表的add与无序列表的addafter、addToRear、addToFront的区别
问题1的解决方案:有序列表与无序列表在添加的方法下有很大的不同,有序列表的添加直接按元素的内在顺序进行排列,而无序列表的就比较任意可以根据用户的想法进行。此外,有序列表要求的是只有Comparable对象才可以存储在有序列表中。
数组表示有序列表的添加 public void add(T element) { if (!(element instanceof Comparable)) { throw new NonComparableElementException("OrderList"); } Comparable<T> comparableElement = (Comparable<T>) element; if (size() == list.length) expandCapacity(); int scan = 0; while (scan < rear && comparableElement.compareTo(list[scan]) > 0) scan++; for (int shift = rear; shift > scan; shift--) list[shift] = list[shift - 1]; list[scan] = element; rear++; modCount++; }
链表表示有序列表的添加 public void add(T element) { LinearNode<T> node = new LinearNode(element); LinearNode<T> temp1 = null,temp2 = head; while(temp2 != null && node.compareTo(temp2.getElement())> 0){ temp1 = temp2; temp2 = temp2.getNext(); } if(temp1 == null){ head = tail = node; } else{ temp1.setNext(node); } node.setNext(temp2); if(node.getNext() == null) tail = node; count++; modCount++; }
通过compareTo方法来进行比较,通过循环遍历队列元素来判断大小,然后数组把添加的索引值后面的元素进行后移,链表进行插入操作就行。但是,数组需要判断容量大小,以及循环次数不是数组的容量而是元素的总量;链表需要确定添加的元素之前链表是否有内容,没有内容的话直接头结点等于尾节点等于添加元素,有内容的话需要把后面的内容接上。
数组表示无序列表的添加--头插 public void addToFront(T element) { if (size() == list.length) expandCapacity(); for (int shift = rear; shift > 0; shift--) list[shift] = list[shift - 1]; list[0] = element; rear++; modCount++; } 数组表示无序列表的添加--尾插 public void addToRear(T element) { if (size() == list.length) expandCapacity(); list[rear] = element; rear++; modCount++; } 数组表示无序列表的添加--目标元素后面 public void addAfter(T element, T target) { if (size() == list.length) expandCapacity(); int scan = 0; while (scan < rear && !target.equals(list[scan])) scan++; if (scan == rear) throw new ElementNotFoundException("UnorderedList"); scan++; for (int shift = rear; shift > scan; shift--) list[shift] = list[shift - 1]; list[scan] = element; rear++; modCount++; }
无序列表的添加看似多实则比有序列表的简单多了就是进行尾插、头插、中间插三种方法。其中,尾插法相对简单,头插法需要把数组内的所有元素整体后移,遍历一遍,中间插的话要考虑插后的元素来确定后移的循环次数。链表表示无序列表的添加--头插 public void addToFront(T element) { LinearNode<T> node = new LinearNode<T>(element); LinearNode<T> temp = head; if(isEmpty()) head = node; else { node.setNext(head); while(temp.getNext() != null){ temp = temp.getNext(); } tail = temp; head = node; } count++; modCount++; } 链表表示无序列表的添加--尾插 public void addToRear(T element){ LinearNode<T> node = new LinearNode<T>(element); if(isEmpty()) head = node; else tail.setNext(node); tail = node; count++; } 链表表示无序列表的添加--目标元素后面 public void addAfter(T element, T target) { LinearNode<T> node = new LinearNode<T>(element); LinearNode<T> temp = head; if(temp == null){ head = tail = node; } while((temp != null)&&(temp.getElement() == target)){ temp = temp.getNext(); } if(temp.getNext() == null){ temp.setNext(node); tail = node; }else{ node.setNext(temp.getNext()); temp.setNext(node); } count++; modCount++; }
- 问题2:列表的常见操作removeFirst、removeLast、remove、first、last、contains、isEmpty、size方法
问题2的解决方法:无序列表和有序列表的共有方法
- removeFirst:从列表中删除第一个元素
- 数组:需要先判断数组是否为空,不为空的话索引值为0的内容直接等于索引值为1的内容,以此循环前移。
- 链表:先判断链表内的元素的个数,如果为1直接头结点等于尾节点等于空,不为1的话直接使链表的头等于头的下一个。
- removeLast:从列表中删除最后一个元素
- 数组:在数组不为空的前提下,直接使数组索引值最大的位置内为空,计数变量减一。
- 链表:确定链表内元素的个数,如果为1直接头结点等于尾节点等于空,不为1的话就通过遍历链表,从头找到尾,确定尾部为空,倒第二为尾部。
- remove:从列表中删除某个元素
- 数组:通过find方法的辅助可以确定删除元素是否在队列中以及索引值的位置,然后在此索引值后的元素依次前移,此前数组索引值最大的位置为空。
- 链表:确定列表是否为空或仅有一个元素、元素是否在列表中、并处理删除元素是列表的末尾元素、首元素、还是中间位置。如果是删除仅有的元素就直接令头结点等于尾结点等于空,删除头元素直接令头结点等于头结点的下一个,删除尾元素就遍历到倒数第二个为尾结点,删除的是中间的元素就直接把前一个元素与后一个元素连接。
- first:查看位于列表前端的元素
- 数组:先判断数组是否有元素,再直接输出索引值为0的内容。
- 链表:先判断链表内是否有元素,再直接调出头结点的内容。
- last:查看位于列表末端的元素
- 数组:先判断数组是否有元素,再直接输出索引值最大的内容。
- 链表:先判断链表内是否有元素,再直接调出尾结点的内容。
- contains:确定列表是否含有某个元素
- 数组:通过find方法辅助就可以(在一定程度上find方法和contains方法作用相同)。
- 链表:在不为空的情况下,遍历一遍链表找寻就可以。
- isEmpty:判定列表是否为空
- 数组:判断计数变量是否为零,为零即列表为空。
- 链表:头结点等于尾结点等于空或是计数变量是否为零。
- toString:
- 数组:以计数变量的值为循环次数进行循环输出。
- 链表:从头节点开始遍历一遍到尾结点。
- 关于数组列表的find方法:通过元素与数组内每一个索引下的内容的对比,来判断索引值的方法。
- 优点:可以使其他方法变得简单易懂,也可以利用find方法来辅助其他一堆方法。
- 数组实现的无序列表
- 链表实现的无序列表
- 问题3:
i++
与i+1
的区别 - 问题3解决方案:在用数组实现remove方法的时候在移动过程中发现
i++
与i+1
的不同意义,虽然之前老师说过两者意义不同,一直没有在意,直到两者在循环的时候才发现,如果用i++
的话并不会删除目标元素,相反会把数组的末尾元素删掉;如果用i+1
的话就会把删除目标元素,数组的末尾元素不会被删掉。我觉得是在第一次循环的时候,i++
并没有起到自增的作用,而是把i索引值内的元素又放到i索引值内,导致最后一次循环的时候尾部元素的索引值是最大的,被后面的赋空语句直接删掉了。而i+1
就在循环的第一次实现了自增,使得目标元素被删掉,最大索引值和最大索引值减1存储的都是一个元素,这样在通过赋空语句就可以了。- 删除“网络空间安全系”的
i++
版:
- 删除“网络空间安全系”的
i+1
版:
- 删除“网络空间安全系”的
- 问题4:PP6.17修改Course进行按系排序在按课程号排序
问题4解决方案:相同类型的编程项目在上学期第七章就实现过,实现Comparable接口,很多同学因为没有写implements Comparable<>直接写个方法完成而扣分,印象十分深刻。有了之前的经验,在练习这次的就没那么难,之前的是进行一重比较,题目要求的是两重比较,在系相同的情况下对课程号进行排序。直接把比较后的数值输出,然后在(有序列表)添加的方法里面融入比较的方法。但是,在上传到码云的时候看了一下别人的代码,觉得直接确定大小后输出-1、1和0三个固定的数没有差别,就没有进行修改。在用数组和链表去实现的时候,发现用数组的方法就会报错NullPointerException,找了半天也没有发现错误,后来使用链表的方法就很成功的实现了,会继续找数组的添加方法的bug的。
上周考试错题总结
第三章和第四章
- 错题1:In an array implementation of a Stack, the array is ___________ causing the implementation to have to create a new larger array and copy the contents of the stack into the new array in order to expand the capacity of the stack.()
- A .Dynamic
- B .Static
- C .Flexible
- D .Polymorphic
- 错误解析:扩容的方法是在原数组已满的前提下更换新容量更大的数组,但是在未更换之前数组的容量一直保持不变。
- 错题2:A reference variable can refer to any object created from any class related to it by inheritance.
- A .true
- B .false
- 错误解析:引用变量可以引用任何通过继承过来与之相关的类创建的对象,就是子类与父类的关系即子类可以用父类的任何方法对象,但是父类不能用子类的方法对象。
- 错题3:By using the interface name as a return type, the interface doesn’t commit the method to the use of any particular class that implements a stack.
- A .true
- B .false
- 错误解析:使用接口名作为返回类型,接口将不会把方法提交给任何实现堆栈的特定类。
- 错题4:The implementation of the collection operations should affect the way users interact with the collection.
- A .true
- B .false
- 错误解析:集合的实现和用户与集合的交互没有影响。第三章和第四章无论使用数组还是链表进行维护集合以及维护集合相关的方法都对集合本身毫无影响。
- 错题5:Inherited variables and methods can be used in the derived class as if they had been declared locally.
- A .true
- B .false
- 错误解析:继承的变量和方法都可以在子类中应用,通过super、extends等保留字就可以。
- 错题6:A linked implementation of a stack adds and removes elements from the _______ of the linked list.
- A .Front
- B .Rear
- C .Middle
- D .None of the above
- 错题解析:堆栈的链表用头和尾都可以当栈顶,但是用头来充当的话会避免每次循环找到尾,再在尾部进行添加的麻烦。
- 错题7:Objects that are stored in a collection should contain implementation details of the underlying data structure.
- A .true
- B .false
- 错误解析:存储在集合中的对象应该包含底层数据结构的实现细节,集合内可以存储任何内容。
第五章
- 错题1:Which of the following operations of a queue examines the element at the front of the queue?
- A .Enqueue
- B .isEmpty
- C .size
- D .first
错误解析:检查队列前面的元素用的是first方法,size是用来判断队列里元素的个数。
第三章和第四章的内容有一部分是上学期的内容,很多都不敢确定选项。第五章错的错的题目太可惜了,为了赶时间没有再细细看一遍就提前交卷,结果就错了一道特别特别简单的题目。其实,错题再看一遍就发现自己的选项错哪了,都不知道自己当时为什么选别的。
感悟
第六章的内容是用数组和链表表示列表的,在一定还曾独上列表和队列、栈都有一定程度上的相似,部分代码完全可以之前编写的,唯一觉得比较恶心的就是在添加的过程中就直接被排序。通过这几章学的内容,对链表和数组有了更多的认识,应用起来也比较顺手。勤能补拙,多联系多尝试总没有错的。
参考资料
以上是关于20172305 2018-2019-1 《Java软件结构与数据结构》第四周学习总结的主要内容,如果未能解决你的问题,请参考以下文章
20172305 2018-2019-1 《Java软件结构与数据结构》第五周学习总结
20172305 2018-2019-1 《Java软件结构与数据结构》第一周学习总结
20172305 2018-2019-1 《Java软件结构与数据结构》第九周学习总结
20172305 2018-2019-1 《Java软件结构与数据结构》第二周学习总结