20172311 2017-2018-2 《程序设计与数据结构》第十周学习总结

教材学习内容总结

本周对集合与数据结构进行了初步的学习

  • 集合是一种对象,类似于保存其他对象的存储库
  • 同构的集合保存类型全部相同的对象,异构的集合可以保存各种类型的对象
  • 集合可用各种方式实现,即保存对象的基础数据结构可以用各种技术实现
  • 对象非常适用于定义集合。对象具有定义良好的接口,从而成为一种是西安集合的完善机制
  • 一个动态数据结构用链来实现,动态数据结构的大小规模随需要增长和收缩
  • 通过保存和更新对象引用来实现一个链表的管理
  • 经典线性数据结构有列表、队列和堆栈
  • 队列是一种以先进先出方式管理数据的线性数据结构
  • 堆栈是一种以后进先出方式管理数据的线性数据结构
  • 常见的非线性数据结构包括树和图
  • Java集合类API中的类定义为泛型,是指一个集合所管理的对象的类型要在实例化该集合对象时才确定

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


问题一:数据结构和抽象数据类型的区别和联系

  • 问题一解决方案:

数据结构:总是为了完成一个功能或者目的写程序,但不管什么程序、代码实际上都是一些指令的集合,说白了就是在描述“怎么做”,而光知道怎么做还只是问题的一半,还要知道“做什么”,也就是刚才那些指令的对象是谁,自然肯定是相关的数据,比如说学生信息管理中,指令是增加学生,那他的对象就是学生信息这种数据,指令是成绩统计,那对象就是学生的成绩数据,而在我们的程序中,数据也必须要有一种很明确的组织表示方式,只要这样我们才能在这种具体明确的实体上编写指令,比如说学生数据可以定义为一个多维的数组,只有这样我们再写增加学生时,才能知道具体增加就是增加一个数组元素并为其赋值。所以数据结构就是相互之间有联系的具有某种组织方式的数据集合。

抽象数据类型相比较数据结构要具体一些,我们光有了数据结构还不够,因为数据是各种各样的,对于不同数据,我们能采取的方法也不一样,比如说学生数据可以增减,成绩数据可以进行算数运算,但是为什么说抽象呢,也就说他并不是具体整型还是字符型这种基本类型,而是我们根据我们要解决的实际问题,对应现实世界所描述的一种和现实世界中的实体对应的数据类型,而且这种抽象的数据类型还包括能够对于他实行的操作,比如说我们定义一种数据类型叫“学生”,具体的数据我可以定义一中类似表的结构存储,而且还要定义一些操作,比如说添加学生,删除学生,这两部分就共同组成了“学生”这个抽象的数据类型。


问题二:动态链表节点增加、插入和删除步骤的原理

  • 问题二解决方案:

1.向链表中添加元素。判断一个链表已经到达末尾的依据是该结点的next引用已经为Null,所以要向末尾添加一个结点,先要把新增结点放在最后,再把末尾结点向后移位,具体操作过程如下图:

代码如下:

/**  
 * 向指定链表添加元素的方法  
 * @param obj   插入的元素  
*/  
public void add(Object obj){    
    Node node=new Node(obj);//新建结点     
    if(head==null){//如果链表为空    
        head = node;    
    }else{    
        last.next=node;//先把新增结点放在最后    
    }    
    last=node;//再把最后一个结点向后移位    
}  

2.插入元素。要插入一个新元素首先要创建一个新结点来存放它,而在具体实现的时候最让人头疼的时候无疑是怎样找到指定位置的索引了,这里所说的方法在下面的其他操作基本上都是这样衍生的,先了解一下插入结点的具体实现,根据这个结构的逻辑定义,如果我们要在结点A之后插入一个结点,那么就还需要修改结点A的next引用,实际上就是让A结点的next引用指向新增结点的元素域,然后再让新增结点的next引用指向A原本next结点(B)的元素域,用图来表示更加直观:

代码如下:

/**  
 * 向链表中插入新元素的方法   
*/  
public void insert(int index,Object obj){    
    Node node=head;    
    int j=0;    
    while(node!=null&&j<index-2){    
        //查找到第index-1个元素    
        node=node.next;    
        j++;    
    }    
    Node sert=new Node(obj);//被插入的结点    
    sert.next=node.next;    
    node.next=sert;    
}  

3.删除元素。知道了插入元素的具体操作之后,删除元素就显得相对简单了,比如说我们要删除一个结点b,就是要使这个结点失去引用,但是注意不要直接写b=b.next,这样的话b的引用还是存在,而且还会出现另一种错误,如图所示,正确的删除结点的方法如下:

代码如下:

/**  
 * 删除指定位置的结点  
 * @param index 索引  
 */  
public void delete(int index){    
    Node node=head;    
    int j=0;    
    while(node!=null&&j<index-2){    
        //查找到第i-1个元素    
        node=node.next;    
        j++;    
    }    
    node.next=node.next.next;//删除第index个元素    
}  

4.最后就是修改元素了。
代码如下:

/**  
* 改变指定位置的元素  
 * @param index 索引  
 * @param obj     
 */  
public void modify(int index,Object obj){    
    Node node=head;    
    int j=0;    
    while(node!=null&&j<index-1){    
        //找到第index个结点    
        node=node.next;    
        j++;    
    }    
    node.obj=obj;    
}  


问题三:单向链表和双向链表的区别

  • 问题三解决方案:

单链表(单向链表):由两部分组成 数据域(Data)和结点域(Node),单链表就像是一条打了很多结的绳子,每一个绳结相当于一个结点,每个节结点间都有绳子连接,这样原理的实现是通过Node结点区的头指针head实现的,每个结点都有一个指针,每个节点指针的指向都是指向自身结点的下一个结点,最后一个结点的head指向为null,这样一来就连成了上述所说绳子一样的链,对单链表的操作只能从一端开始,如果需要查找链表中的某一个结点,则需要从头开始进行遍历。

双链表(双向链表):双链表和单链表相比,多了一个指向尾指针(tail),双链表的每个结点都有一个头指针head和尾指针tail,双链表相比单链表更容易操作,双链表结点的首结点的head指向为null,tail指向下一个节点的tail;尾结点的head指向前一个结点的head,tail 指向为null,是双向的关系;

双向链表的节点类代码:

public class Node {  
  
    //存储的数据  
    private Object data;  
    //前一个节点  
    private Node prev;  
    //后一个节点  
    private Node next;  
      
      
    public Object getData() {  
        return data;  
    }  
  
    public void setData(Object data) {  
        this.data = data;  
    }  
  
    public Node getPrev() {  
        return prev;  
    }  
  
    public void setPrev(Node prev) {  
        this.prev = prev;  
    }  
  
    public Node getNext() {  
        return next;  
    }  
  
    public void setNext(Node next) {  
        this.next = next;  
    }  
  
    //定义构造函数  
    public Node(){  
          
    }  
  
    public Node(Object data, Node prev, Node next) {  
        super();  
        this.data = data;  
        this.prev = prev;  
        this.next = next;  
    }     
      
}  

双向链表的部分具体实现代码:

//实现双向链表  
public class myDoubleLinkImpl {  
  
    //记录链表的节点数  
    int length=0;  
    //定义上一个节点  
    private Node header;  
    //定义下一个节点  
    private Node tail;  
      
    public myDoubleLinkImpl(){  
        //定义的一个空的链表  
        header=null;  
        tail=null;  
        length=0;  
    }  
      
    //获取链表的节点值  
    public int getLength()  
    {  
        return length;  
    }  
      
    //向链表中添加值  
    public void add(Object data)  
    {  
        //判断链表是否是空的哈  
        if(header==null)  
        {  
            header=new Node(data,null,null);  
            tail=header;  
            length++;     
        }else{  
            //非空链表哈  
            Node tempNode=new Node(data,tail,null);  
            tail.setNext(tempNode);  
            tail=tempNode;  
            length++;  
        }  
          
          
    }  

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

问题1:在MaganizeList类中编写删除某个节点方法时出现逻辑错误

错误的代码如下:

public void delete(Magazine delNode){
        MagazineNode node=new MagazineNode(delNode);
        MagazineNode current=list;
        MagazineNode behind=current;

        if (list==null)
            list=null;
        else
            if (current.magazine.toString().equals(node.toString()))
                current=current.next;
            else{
                while (!current.next.magazine.toString().equals(node.toString()))
                {
                    behind.next=current;
                    current=current.next;
                }
                current=current.next;
                behind.next=current.next;
                list=behind;
            }
    }  
  • 问题1解决方案:结合正确的思路通过仔细的分析发现是因为自己未能理解其中两个指针的声明方式和他们各自的作用

正确的思路:

单链表的操作:

添加:上图可以看出 单向链表只有一个指向,原来head为p,p指向s,添加结点只需要把p指向q,q指向s就可以了,即:p--->q ; q--->s ; 这样就实现了单向链表的添加;

删除:原理与添加相反,若此时链表为 p---> q --->s ; 若删除q节点只需要更改p的指向就可以了 p--->s,这样就删掉了;

修改之后的正确代码如下:

public void delete(Magazine delNode) {
        MagazineNode front = list;
        MagazineNode current = front.next;

        if (delNode.toString().equals(front.magazine.toString()))
            list = front.next;
        else {
            while (!current.magazine.toString().equals(delNode.toString()))
                front = front.next;
            front.next = current.next;
        }

    }  

问题2:pp13.3修改Sorting类,使其能够操作一个以整型数作为节点的链表时遇到较大困难

  • 问题2解决方案:通过大量尝试和实践解决了问题!修改后的代码如下:
import java.util.LinkedList;

public class Sorting3 {
    //-----------------------------------------------------------------
    //  Sorts the specified array of objects using the selection
    //  sort algorithm.
    //-----------------------------------------------------------------
    public static void sortLink(LinkedList<Integer> link) {
        int min;
        int temp;

        for (int index = 0; index < link.size() - 1; index++) {
            min = index;
            for (int scan = index + 1; scan < link.size(); scan++) {
                if (link.get(scan).compareTo(link.get(min)) < 0) {
                    min = scan;
                }
            }

            // Swap the values
            temp = link.get(min);
            link.set(min, link.get(index));
            link.set(index, temp);

        }
    }
}    

测试类的代码及运行截图如下:

代码托管

上周考试错题总结

  • 错题1

    理解:无限循环和递归都是相似的,它们不断地无限重复。编译器或运行时(JVM)都不能捕获它们。

  • 错题2

    错因:忽略了输入为0的时候这种特殊情况!!同样输入参数0,第一个return为0,第二个则会陷入无限递归。

  • 错题3

    理解:递归和迭代在计算上是等价的——每一个都可以被另一个取代。

结对及互评

这周我和伙伴对之前的四则运算项目进行了最后的修改和测试,伙伴编写了一个很棒的交互程序,同时对代码进行了格式化以及漏洞的完善,贡献很大!!

点评过的同学博客和代码

感悟

我深深地感觉到Java是一门实践性很强的学问!Java的很多概念难以理解,而且就算理解了也不等于会灵活应用,所以对这门学科的学习还是应该以实践为主!!

学习进度条

代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
目标 5000行 30篇 400小时
第一周 28/28 1/1 16/16
第二周 710/738 1/2 20/36
第三周 426/1164 1/3 16/52
第四周 1068/2232 2/5 20/72
第五周 604/2928 1/6 22/94
第六周 609/3537 1/7 22/116
第七周 599/4136 1/8 18/134
第八周 1052/5188 3/11 20/154
第九周 866/6054 1/12 20/174
第十周 970/7024 1/13 20/194
  • 计划学习时间:22小时

  • 实际学习时间:20小时

  • 改进情况:这部分学习的时候对概念的理解遇到了较大的麻烦,只能是通过不断查阅各种资料去加深自己的理解,但是有的概念总感觉理解得不是那么到位!!
    在查阅资料的过程中也了解到实践的重要性,要学着把更多的精力放到实践中去,而不是一直钻概念的牛角尖!!

参考资料

posted on 2018-05-20 22:13  socialsea  阅读(305)  评论(3编辑  收藏  举报