算法第四版学习(chapter1.2)

       话不多说,开始今天的学习,今天来到数据抽象这一章节,果然算法这本书,真的非常适合算法初学者,在java语言中,数据抽象即将数据视为某种对象,能很好的,更直观的的处理自己的数据。

       首先来整理一下核心知识点:

       1.2.1:

       ①为什么我们要使用抽象数据类型呢:这有一部分就是java封装的思想,你不需要知道我内部是怎么实现的,我给你相应的API,你直接调用就好,能输入你想存放的数据,返回你想要的数据。

       ②抽象数据类型在java继承下的得益:通过继承Object,数据类型会带有toString(),equals(),compareTo(),hashcode()等初始方法,我们通常会对其进行重载,转化为适合实际情况的方法。

         ③如何去使用ADT(abstract data type)呢:本质上来讲,ADT还是一种java类,我们要使用它,我们就必须先对他进行实例化(实例过程中,我么可以通过构造器对它进行成员属性初始化),然后就是调用实例化方法去操作成员属性了。

        ④实际应用场合:我们会先实例化一个或多个ADT,在程序逻辑中需要对数据进行修改时,调用实例化对象的方法,对对象的成员变量进行修改,当将ADT当成方法参数时,跟普通数据一样,只是取值的时候用下方法就好了。

      1.2.2-12.4:

     下面就讲述一些ADT的实际例子,以及具体如何实现,如果有一定java基础的同学可以大概看一下,没有的话还是仔细看看吧。

      1.2.5:

     ADT在设计时会用到的的一些原则:

    ①设计API时,我们应该考虑其实现难度,尽量别设计过于困难的功能

    ②注意使用范围,针对某一固定范畴进行设计

    ③要将算法实现和用例代码进行隔离,解耦合

    ④ADT的两种生成方式,实现接口,继承父类

    ⑤等价性原则:判断变量是否等价时会考虑到以下性质:自反性 自己要等于自己;对称性 x==y,则y==x;传递性 x==y,y==z,则x==z;一致性 当x和y没有变化时,x==y的结果不会变 ;非空性x==null一定要返回false

    ⑥不可变性:有部分ADT是不容许改变的,当一开始实例化调用构造器时,确认了某一层成员变量后是不会再发生改变的(for example:String类型,在实例化String的时候,后面的字符串将会保留在常量池,是没办法再改变的,而当我们改变String时,其实我们是在更改它的引用对象,又例如Vector,Vector生成之后数组成员的引用地址都是不能够变的,但是我们还得考虑一个问题,虽然引用地址不变,但是他们所指向的对象不一定是不能改变的,基本数据类型的一般不变,他们没有引用,只有数值摆在那块,而其他数据类型就不保准了)

    ⑦小知识点:断言,用来检测某一boolean数值是否为true,若不是则报错。

    好了,又到了我最喜欢的练手时间了

   1.2.1:初略说下思路,先定义ADT点,然后就是生成一个框,随机在框中生成点,放入Arraylist中,在逐一计算比较,其实我一开始也在想有没有什么更加优化的算法,我这设计的算法在点比较少的时候是没有问题的,当点多了,这样比较起来就有点蠢了,应该可一采用分割,将不同板块进行分割,再算最短,虽然这样只能算出局部最优,因为板块分割部位可能出现把最近的两个点切开了,以后再思考这个吧,先给出一种想法。

   1.2.2:这个问题就比较简单了:先是标准输入(x1,x2),再按x1排序,当后面组的x1小于当前组的x2时,重合,继续向下比对,好像说的有点不太清楚,画个图吧

图好丑,各位不要介意,这个意思到了就好

1.2.3:2D间隔的重合,覆盖问题,,我们先根据p47,编写相应的ADT,然后就是上代码,让小伙伴自己看吧

public static void get2DN(int N,double min,double max) {

        StdDraw.setXscale(min, max);
        StdDraw.setYscale(min, max);
        Point2D[] leftTopPoints = new Point2D[N];
        Point2D[] rightBottomPoints = new Point2D[N];
        Interval2D[] intervals = new Interval2D[N];
        for (int i = 0; i < N; i++) {
            //生成长x的过程
            double a = StdRandom.uniform(min, max);
            double b = StdRandom.uniform(min, max);
            double left, right, top, bottom;
            Interval1D x, y;
            if (a < b) {
                left = a;
                right = b;
            } else {
                left = b;
                right = a;
            }
            x = new Interval1D(left, right);
            //生成宽y的过程
            a = StdRandom.uniform(min, max);
            b = StdRandom.uniform(min, max);
            if (a < b) {
                top = a;
                bottom = b;
            } else {
                top = b;
                bottom = a;
            }
            y = new Interval1D(top, bottom);
            //将2d间隔的左上角和右下角这两个点保存下来,用作后面的包含判断
            leftTopPoints[i] = new Point2D(left, top);
            rightBottomPoints[i] = new Point2D(right, bottom);
            intervals[i] = new Interval2D(x, y);
            intervals[i].draw();
        }
        int containNum = 0, intervalNum = 0;
        //开始判断重合和包含关系
        for (int i = 0; i < N - 1; i++) {
            for (int j = 0; j < N; j++) {
                if (j > i && intervals[i].intersect(intervals[j])) {
                    intervalNum++;
                }
                if (j != i && intervals[i].contains(leftTopPoints[j]) && intervals[i].contains(rightBottomPoints[j])) {
                    containNum++;
                }
            }
        }
        System.out.println("Interval count: " + intervalNum);
        System.out.println("Contain count: " + containNum);

    }

1.2.6:回环变位,上代码吧

这是比较笨的版本

public static void circleString(String s1,String s2){
        //先获取s1的第一个字符
        char first=s1.charAt(0);
        int find=0;
        String s;
        int fromindex=1;
        //先看看人家是不是啥都没变,直接塞给我
        if(s1.equals(s2)){
            System.out.println("true");
            return;
        }
        //大概讲讲思路,先获取s1第一个字符,然后从s2中找位置,拆串重新组装,比较,不行就往后继续找位置,以此类推到找不到就结束
        while(find>=0){
            find=s2.indexOf(first,fromindex);
            if (find > 0) {
                s=s2.substring(find);
                s=s+s2.substring(0,find);
                if(s.equals(s1)){
                    System.out.println("true"+find);
                    return;
                }
            }
            if(fromindex<s1.length()){
                fromindex=find+1;
            }else{
                System.out.println("false");
            }

        }

    }

聪明版

public static void circleString2(String s1,String s2) {
        int index=0;
        //思路:将两个s1拼在一起,若果s2是s1的回环变位,则s1能包含s2
        if(s1.length()==s2.length()&&(index=s1.concat(s1).indexOf(s2))>=0){
            System.out.println("找到了"+index);
        }
    }

1.2.10:

public class VisualCounter {
    int N;
    int max;
    int now=0;

    public VisualCounter(int n, int max) {
        this.N = n;
        this.max = max;
    }
    //增长过程要通过判断,才能进行增长
    public void increasement(int i){
        if(now+i<=max&&N>0){
            now+=i;
            show();
        }else if(Math.abs(now+i)>max){
            System.out.println("超出额定数字");
        }else if(N==0){
            System.out.println("操作次数已用完");
        }
    }
    //在画面上显示现在的now值
    public void show(){
        StdDraw.clear();
        StdDraw.text(0.5,0.5,((Integer)now).toString());
    }


}

1.2.16和1.2.17:

这个问题我贴部分代码吧,整体代码有点长,因为用到了断言,所以不支持在编译器下直接运行,需要在cmd 上用java -ea执行

public class Rational {
    public int numerater;
    public int denominator;

    public Rational(int numerater, int denominator) {
        int c=0;
        if((c=day02.gcd(numerater,denominator))>1){
            numerater/=c;
            denominator/=c;
        }

        this.numerater = numerater;
        this.denominator = denominator;

    }

    public Rational plus(Rational r){
        Rational result;
        long resultN=0;
        long resultD=0;
        int c=0;
        resultN=this.numerater*r.denominator+r.numerater*this.denominator;
        resultD=(long)this.denominator*(long)r.denominator;
        boolean flag=resultD<2147483647;
        try{
            assert  flag: "程序错误";
            System.out.println("程序正常");
        }catch(AssertionError err){
            System.out.println(err.getMessage());
        }

        if((c=day02.gcd((int)resultN,(int)resultD))>1){
            resultN/=c;
            resultD/=c;
        }
        result=new Rational((int)resultN,(int)resultD);
        return result;
    }
}

1.2.18:

关于这个算法,首先我能告诉大家,它是错的,下面我们来分析一下其中的原因(看代码)

public class Accumulator {
    private double m;
    private double s;
    private int N;
    public void addDataValue(double x){
        //每次添加新的数值,N+1
        N++;
        //这是整个算法错误的问题所在,方差运算需要的是所有数值的平均数,而不能这样逐个获得平均值去计算
        s = s+1.0*(N-1)/N*(x-m)*(x-m);
        m=m+(x-m)/N;
    }
    public double mean(){
        return m;
    }
    public double var(){
        return s/(N-1);
    }
    public double stddev(){
        return Math.sqrt(this.var());
    }
}

1.2.19:唉。。。基础题,不写了

发布了8 篇原创文章 · 获赞 2 · 访问量 1629

猜你喜欢

转载自blog.csdn.net/z1261203654/article/details/81355805