zoukankan      html  css  js  c++  java
  • 第七周

    学号 2019-2020-20182321 《数据结构与面向对象程序设计》第七周学习总结

    教材学习内容总结

    • 栈是一个线性集合,其内部元素遵循先进后出的规律,即先进来的最后才能出去。(因其删除和添加元素都是在栈的一段进行)
    • 栈的操作都在栈顶进行,如果把栈看成是一长串数组,那么栈顶就类似于数组的末尾,栈通常有着五种基本操作
    1. pop 删除栈顶元素
    2. push 将元素添加到栈顶
    3. peek 查看栈顶元素
    4. isEmpty 栈是否为空
    5. size 判定栈中元素的个数
    • java可以用泛型来定义类,即可以定义一个类,它保存操作管理直到实例化时才会需要决定类型的对象。如Competition 是一个类型为T的类,当实例化对象的时候,它可以实例化为整数型,也可以实例化为浮点型等等。
    • 实现栈的方法可以是利用数组或者是利用链表,但在实现的时候也必须要顾忌几个方面的异常
    1. 入栈时如果栈满
    2. 出栈时如果栈空
    3. 扫描完表达式时栈中的值如果多余一个
    • 栈满的原因应该是数据结构出的问题,即算法出了问题,严格意义上说,满栈是不存在的,这辈子都不可能满栈的,虽然听起来是不太可能,但是从栈的角度来说,栈是无限的,是一个无限集,只不过是数据结构有限罢了
    • 链表相对于数组的缺点是,链表在指定性上不如数组,如果想要直接访问某个在栈中的元素,用数组时我们就可以直接跳到那里,但是用链表的话就必须要一级一级的往下走,且必须设置头指针,否则会出现头指针缺失,回不去。但是链表相对于数组的优点在于,数组的大小是有限度的,而链表是无限的,它可以无限扩大,也就是我们说的能够构成一个无限的栈。
    • 学习了如何使用链表和数组来实现栈,同时,用来实现栈的功能,删除节点,添加节点等等。
    • 队列与栈不同,它是从头出,尾进,所以需要两个指针来实现它的功能
    • 队列也可以用数组和链表来实现
    • 使用数组时我们可以考虑使用“环形”数组,虽然其本质上还是线性构成的,但是可以实现了环形的效果。
    • 分析算法有两种:时间复杂度和空间复杂度
    • 时间复杂度其实说白了,就是看代码运行了多少次,代码运行的次数决定着这个程序运行的时间,我们会用大O法来说明时间复杂度的情况
    • 空间复杂度是说程序用了多少空间。

    教材中遇到的问题和解决过程

    • 问题1:链表和数组之间的优缺点在哪
    • 问题1解决方法:数组就是一枚可以精准打击的导弹,而链表就是一列不会回头的列车,使用数组时,我们像访问某个地方,数组就会帮我们直接跳转到那里,但是局限性是,数组是有范围的,正如导弹也是有其攻击范围一样;而链表则是所向披靡,永远也没有终点,当你想无限扩大你的栈时,链表也可以无限扩展(只要你的内存够多),但是问题是链表是个没有回头路的火车(除非用头指针将其返回从头开始),而且其无法精准定位,必须要一个一个的访问遍历才能到达目的地。总的来说,在查找这方面数组确实可控性更强,但是在构建这一方面链表的稳定性更棒,解决代码问题还是要具体问题具体分析,究竟哪一个更好还是要弄清双方的优缺点再加以利用。
    • 问题2:当队列的头指针和尾指针都不断向上走时(即不断出元素和不断入元素),我们如何在不增加队列空间内存的情况下,使其正常运行呢?
    • 问题2解决方法:可以将其转换成一个“环形”的数组,虽然这里说是环形,但是我还是想说清楚这个数组本质上还是线性的,只是在当尾指针指向数组的底部的时候,我们必须将其再重新放回到数组的头部罢了,这样就不会浪费过多的空间,让我们可以随意的出入队。
      下面为代码
    package zhan;
    
    
    
    public class CircularArrayQueue<T> implements Queue<T> {
        private final int DEFAULT_CAPACITY = 10;
        private int front, rear, cout;
        private T[] queue;
    
        public CircularArrayQueue() {
            front = rear = cout = 0;
            queue = (T[]) (new Object[DEFAULT_CAPACITY]);
        }
    
        @Override
        public void enqueue(T element) {
            if (cout == queue.length)
    
                expandCapacity();
            queue[rear] = element;
            rear = (rear+1)%queue.length;
            cout++;
        }
    
        private void expandCapacity() {
            T[] larger = (T[]) (new Object[queue.length * 2]);
            for (int index = 0; index < cout; index++)
                larger[index] = queue[(front + index) % queue.length];
                front = 0;
                rear = cout;
                queue = larger;
    
        }
    
    
    
    
        @Override
        public T dequeue() throws  EmptyCollectionException{
            T s;
            if(front == rear&&queue[front]==null)
            throw new EmptyCollectionException("队列已空");
            else {
                s = queue[front];
                queue[front]=null;
                front=(front+1)%queue.length;
                cout--;
            }
            return s;
        }
    
        @Override
        public T first() throws EmptyCollectionException {
            if(front == rear&&queue[front]==null)
            {
                throw new EmptyCollectionException("数组已空");
            }
            else
                return  queue[front];
        }
    
        @Override
        public boolean isEmpty() {
            if(cout == 0)
                return false;
            else
                return true;
        }
    
        @Override
        public int size() {
            return cout;
        }
        public String toString()
        {
            String result="";
            for(int i = 0 ;i<queue.length;i++)
            {
                result += "   " + queue[i];
            }
            return result;
        }
    
    }
    
    

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

    • 问题1:后缀表达式如何转换成前缀表达式
    • 问题1解决方法:

    如上图所示,当我们要将一个后缀表达式abcd-*+ef/-转换成前缀表达式的时候,可以遵循以下几个步骤

    从左到右扫描后缀表达式:
    
    1.如果是操作数,则直接将其指针压入栈中
    2.如果是操作符,则依次弹出两个栈顶指针进行字符串拼接后,再和操作符进行拼接,返回新的指针压入栈中(先弹出的拼接在后弹出的后面,操作符拼接在最前面)
    3.直到扫描结束,将栈顶指针弹出,即为前缀表达式字符串的指针
    

    于是,我们可以得到以下结果

    下面为我们的核心代码

    
    import java.util.Scanner;
    import java.util.Stack;
    public class change {
        private Stack<String> stack;
        private final  char add = '+';
        private final char subtract='-';
        private final char multiply='*';
        private final char divide='/';
        public change()
        {
            stack = new Stack<String>();
        }
        public String qianzhui(String s)
        {
            String op1="";
            String op2="";
            String result="";
            String token ;
            Scanner tokenizer = new Scanner(s);
            while (tokenizer.hasNext()) {
                token = tokenizer.next();
                if (isOpearator(token)) {
                    op1 = (stack.pop());
                    op2 = (stack.pop());
                    result = evalSingleop(token.charAt(0),op2,op1);
                    stack.push(result);
    
                }
                else
                    stack.push(token);
            }
            return  result;
        }
        public boolean isOpearator(String token)
        {
            return (token.equals("+")||token.equals("-")||token.equals("/")||token.equals("*"));
        }
        public String evalSingleop(char opeation,String op1,String op2)
        {
            String result = "";
            switch (opeation)
            {
                case add:
                    result="+"+op1+" "+op2;
                    break;
                case subtract:
                    result = "-"+op1+" "+op2;
                    break;
                case multiply:
                    result =("*"+op1)+" "+op2;
                    break;
                case divide:
                    result = ("/"+op1)+" "+op2;
    
            }
            return result;
    
        }
    }
    
    
    • 问题2:使用数组时java空指针异常:java.lang.NullPointException
    • 问题2解决方法:这个是在编写java的时候最常遇到的问题之一,我们创建一个关于我们自己编写的类的数组的时候,虽然用到了new,但是其实往往只是声明了一个空间位置而已,但是并没与实例化它。
      Scanner scan = new Scanner(System.in);
            Comparebale[] data = new Comparebale[12];
            Comparebale target ;
            Searching searching = new Searching();
            String s = "19 14 23 1 68 20 84 27 55 11 10 79";
            String[] n = s.split("\s");
    
            for(int i = 0;i<n.length;i++)
            {
                int num = Integer.parseInt(n[i]);
                data[i].geti = num;
            }
    

    以上就是一个错误的写法,因为虽然我们给datanew了一波,但是这也只是为他提供了12个位置的空间内存而已,而并没有实例化这个对象,正确写法是这样的

      Scanner scan = new Scanner(System.in);
            Comparebale[] data = new Comparebale[12];
            Comparebale target ;
            Searching searching = new Searching();
            String s = "19 14 23 1 68 20 84 27 55 11 10 79";
            String[] n = s.split("\s");
    
            for(int i = 0;i<n.length;i++)
            {
                int num = Integer.parseInt(n[i]);
                data[i]=new Comparebale(num);
            }
    

    即对每一个数组元素都要new一下,这样才能真正的实例化对象,不会造成空指针错误。

    • 问题3:出队时,队列总是为空
    • 问题3解决方法:
      以下为源代码
      public T dequeue() throws  EmptyCollectionException{
            T s;
            if(front == rear)
            throw new EmptyCollectionException("队列已空");
            else {
                s = queue[front];
                queue[front]=null;
                front=(front+1)%queue.length;
                cout--;
            }
            return s;
        }
    
    

    我原本想的是,如果头指针和尾指针指向同一个方向的时候,那么,这个队列就是一个空队列,但是运行程序的时候我发现队列总是为空,这又是怎么回事呢?debug发现,原来是我的入队代码的原因

     public void enqueue(T element) {
            if (cout == queue.length)
    
                expandCapacity();
            queue[rear] = element;
            rear = (rear+1)%queue.length;
            cout++;
        }
    
    

    每次新元素入队后,我的尾指针都会向后跳多一位,加入数组个数是10,当到了a[9]的时候,queue[9]=10,接着rear就会跳到开头头指针的位置rear=0,于是rear就和front相等了,从而程序就出现了问题,无论怎样都会显示队列已空。经过思考,我给这个出栈加多了一个限制条件

        public T dequeue() throws  EmptyCollectionException{
            T s;
            if(front == rear&&queue[front]==null)
            throw new EmptyCollectionException("队列已空");
            else {
                s = queue[front];
                queue[front]=null;
                front=(front+1)%queue.length;
                cout--;
            }
            return s;
        }
    

    这样,代码就可以照常运转了。

    代码托管

    (statistics.sh脚本的运行结果截图)

    上周考试错题总结

    上周无考试

    • 上周博客互评情况

    其他(感悟、思考等,可选)

    链与栈的内容感觉还好,队列也还没那么难,理解清楚它的意思就行了,就是在关于链如何回到链头的方法上有一定的问题,在类里运行可以成功,但是在驱动程序里运行失败了,还需要我下周继续研究研究。

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 200/200 2/2 20/20
    第二周 300/500 2/4 18/38
    第三周 623/1000 3/7 22/60
    第四周 600/1600 2/9 22/82
    第五周 1552/2987 2/11 22/94
    第六周 892/3879 2/11 22/114
    第七周 2284/6163 2/13 42/156
    参考:软件工程软件的估计为什么这么难软件工程 估计方法
    • 计划学习时间:10小时

    • 实际学习时间:42小时

    • 改进情况:我觉得我还可以顶一会,这周java的作业较为集中,花费的时间也变多

    (有空多看看现代软件工程 课件
    软件工程师能力自我评价表
    )

    参考资料

  • 相关阅读:
    疑难杂症--数据库触发器导致批处理中变量无效
    Transaction And Lock--锁相关基础
    INDEX--关于索引的琐碎
    INDEX--索引相关信息查看
    INDEX--索引页上存放那些数据
    Transaction And Lock--解决死锁/锁的几种有效方式
    Transaction And Lock--由Lookup导致的死锁情况
    Transaction And Lock--由外键导致的死锁
    oozie中时间EL表达式
    SpringBoot-hello world
  • 原文地址:https://www.cnblogs.com/yangkaihan/p/11781409.html
Copyright © 2011-2022 走看看