1.2 数据抽象

练习

1.2.1 编写一个Point2D的用例,从命令行接受一个整数N.在单位正方形中生成N个随机点,然后计算两点之间的最近距离.       /*查阅Point2D的API*/

public class H1_2_01
{
    public static void main(String[] args)
    {
        int N=1000;
        Point2D[] arr=new Point2D[N];
        for(int i=0;i<N;i++)
            arr[i]=new Point2D(Math.random(), Math.random());

        double mindis=Double.POSITIVE_INFINITY;  //mindis--store the minimum distance
        for(int i=0;i<N-1;i++)
            for(int j=i+1;j<N;j++)
            {
                double dis=arr[i].distanceTo(arr[j]);
                if(mindis>dis)
                    mindis=dis;
            }
        StdOut.printf("the minimum distance is: %.5f\n",mindis);
    }
}

1.2.2 编写一个Interval1D的用例,从命令行接受一个整数N.从标准输入中读取N个间隔(每个间隔由一对double值定义)并打印出所有相交的间隔对

public class H1_2_02
{
    public static void main(String[] args)
    {
        int N=10;
        Interval1D[] arr=new Interval1D[N];
        for(int i=0;i<N;i++)
        {
            StdOut.println("please input an interval1D.");
            double lo=StdIn.readDouble();
            double hi=StdIn.readDouble();
            while(lo>hi)    //消除不合理的输入
            {
                StdOut.println("the input is illegal, please input again.");
                lo=StdIn.readDouble();
                hi=StdIn.readDouble();
            }
            arr[i]=new Interval1D(lo, hi);
        }
        for(int i=0;i<N-1;i++)
            for(int j=i+1;j<N;j++)
            {
                if(arr[i].intersects(arr[j]))
                    StdOut.printf("the interval %s is intersected to the interval %s\n",arr[i].toString(),arr[j].toString());
            }
    }
}

1.2.3 编写一个Interval2D的用例,从命令行接受参数N、min和max.生成N个随机的2D间隔,其宽和高均匀的分布在单位正方形中的min和max之间.用StdDraw画出他们并打印出橡胶的间隔对的数量以及包含关系的间隔对数量

public class H1_2_3
{
    public static void main(String[] args)
    {
        int N=20;
        double min=0.05;
        double max=0.1;
        int containCnt=0;int intersectCnt=0;
        Interval2D[] arr=new Interval2D[N];
        Interval1D[] arrx=new Interval1D[N];
        Interval1D[] arry=new Interval1D[N];
        for(int i=0;i<N;i++)
        {
            double x=StdRandom.uniform(0.0,1-max);
            arrx[i]=new Interval1D(x, x+StdRandom.uniform(min, max));
            double y=StdRandom.uniform(0.0,1-max);
            arry[i]=new Interval1D(y, y+StdRandom.uniform(min,max));
            arr[i]=new Interval2D(arrx[i], arry[i]);
            StdDraw.setPenColor(StdDraw.BOOK_BLUE);
            arr[i].draw();
        }
        for(int i=0;i<N-1;i++)
            for(int j=i+1;j<N;j++)
            {
                if(arr[i].intersects(arr[j]))
                {
                    intersectCnt++;
                    if(iscontains(arrx[i],arry[i], arrx[j], arry[j]))
                        containCnt++;
                }
            }
        StdOut.printf("the intersect interval2D=%d, the contain interval2D=%d\n", intersectCnt,containCnt);
    }
    public static boolean iscontains(Interval1D x1,Interval1D y1,Interval1D x2,Interval1D y2)  //判断是否存在包含关系
    {
        if(x1.left()<=x2.left()&&x1.right()>=x2.right()&&y1.left()<=y2.left()&&y1.right()>=y2.right())
            return true;
        if(x2.left()<=x1.left()&&x2.right()>=x1.right()&&y2.left()<=y1.left()&&y2.right()>=y1.right())
            return true;
        return false;
    }
}

注:另一种解决思路是在Interval2D类中添加判断是否存在包含关系的实例函数

1.2.4 主要考察字符串不可变---因此对字符串重新"赋值"其实是指向了新的对象而不是改变原有值

1.2.6 编写一个程序检查两个给定的字符串s和t是否互为回环变位(如果字符串s中的字符循环移动任意位置之后能够得到另一个字符串t,那么s就被称为t的回环变位)    /*掌握字符串的灵活用法*/

public class H1_2_06
{
    public static void main(String[] args)
    {
        StdOut.println(cirRotation("ACTGACG", "TGACGAC"));
    }

    public static boolean cirRotation(String s,String t)
    {
        String temp=s+s;  //该语句起到很大的简便判断作用
        if(temp.indexOf(t)!=-1&&s.length()==t.length())
            return true;
        return false;
    }
}

作者网站上给出的答案:Solution: (s.length() == t.length()) && (s.concat(s).indexOf(t) >= 0)

1.2.9 修改BinarySearch,使用Counter统计在查找中被检查的键的总数并在查找结束后打印该值.

public class H1_2_09
{
    public static void main(String[] args)
    {
        Counter cnt=new Counter("count");
        int[] arr={1,4,5,6,7,8,9};
        Arrays.sort(arr);
        int key=9;
        rank(key,arr,cnt);
        StdOut.println(cnt.toString());
    }

    public static int rank(int key,int[] a,Counter cnt)
    {
        int lo=0;
        int hi=a.length-1;
        while(lo<=hi)
        {
            cnt.increment();  //每次进入循环代表一次检查
            int mid=lo+(hi-lo)/2;
            if(key<a[mid])
                hi=mid-1;
            else if(key>a[mid])
                lo=mid+1;
            else
                return mid;
        }
        return -1;
    }
}

1.2.10 编写一个类VisualCounter,支持加一和减一操作.它的构造函数接受两个参数N和max,其中N指定了操作的最大次数,max指定了计数器的最大绝对值.作为副作用,用图形显示每次计数器变化后的值.

先根据要求列出"需求"API     /*明确类所需要的数据,方法等*/

VisualCounter(int N,int max) 构造函数
increment() 加一操作
decrement() 减一操作
public class VisualCounter
{
    private int cnt=0;
    private int opN=0;
    private int max;
    private int N;
    public VisualCounter(int N,int max)
    {
        this.N=N;
        this.max=max;
        StdDraw.setXscale(0,N);
        StdDraw.setYscale(-max-0.1,max+0.1);
        StdDraw.setPenColor(StdDraw.BOOK_BLUE);
    }

    public void increment()
    {
        if(cnt<max&&opN<N)
        {
            cnt++;
            opN++;
            StdDraw.filledRectangle(opN-0.5, cnt/2.0, 0.9/2, Math.abs(cnt)/2.0); //采用柱状图体现变化后的值
        }
    }
    public void decrement()
    {
        if(-cnt<max&&opN<N)
        {
            cnt--;
            opN++;
            StdDraw.filledRectangle(opN-0.5, cnt/2.0, 0.9/2, Math.abs(cnt)/2.0);
        }
    }

    public static void main(String[] args)
    {
        int N=10;int max=5;
        VisualCounter vc=new VisualCounter(N, max);
        int i=0;
        while(i<N)
        {
            if(StdRandom.bernoulli(0.5))  //采用等可能出现进行测试
                vc.increment();
            else
                vc.decrement();
            i++;
        }
    }
}

1.2.11&1.2.12 根据Date的API实现一个SmartDate类型,在日期非法时抛出一个异常,并为SmartDate添加一个方法dayOfTheWeek(),为日期中每周的日返回Monday到Sunday中的适当值.(可以假定时间是21世纪)         /*此题可了解构造函数直接如何调用,闰年的判断,蔡勒公式判断星期以及抛出异常的使用~ 此题重要!*/

Date的API如下

SmartDate(int month,int day,int year) 创建一个日期
SmartDate(String str) 创建一个日期
int day()
int month()
int year()
String dayOfTheWeek() 当日为星期几
public class SmartDate
{
    private int year;
    private int month;
    private int day;
    public SmartDate(int y,int m,int d)
    {
        if(!isIllegal(y,m,d))
            throw new IllegalArgumentException("illegal date!!");
        else
        {
            year=y;
            month=m;
            day=d;
        }
    }
    public SmartDate(String date)
    {
        String[] fields=date.split("/");
        int m=Integer.parseInt(fields[0]);
        int d=Integer.parseInt(fields[1]);
        int y=Integer.parseInt(fields[2]);
//        this(y,m,d); ---不可行,必须要写在第一行,因此此处不可用
        if(!isIllegal(y,m,d))
            throw new IllegalArgumentException("illegal date!!");
        else
        {
            year=y;
            month=m;
            day=d;
        }
    }

    public int year()
    {
        return year;
    }
    public int month()
    {
        return month;
    }
    public int day()
    {
        return day;
    }
    public String toString()
    {
        return month()+"/"+day()+"/"+year();
    }
    //运用蔡勒公式---判断改日的星期
    public String dayOfWeek()
    {
        int w=(year/100/4-2*(year/100)+year%100+year%100/4+26*(month+1)/10+day-1)%7;
        switch(w)
        {
        case 1:
            return "Monday";
        case 2:
            return "Tuesday";
        case 3:
            return "Wednesday";
        case 4:
            return "Thursday";
        case 5:
            return "Friday";
        case 6:
            return "Saturday";
        default:
            return "Sunday";
        }
    }

    //判断是否合法
    private boolean isIllegal(int y,int m,int d)  //判断日期是否合法:三方面--年月日
    {
        if(y<1||m<1||m>12||d<0||d>31)
            return false;
        int[] monthOfDay={0,31,-1,31,30,31,30,31,31,30,31,30,31};
        if(isLeapYear(y))
            monthOfDay[2]=29;
        else
            monthOfDay[2]=28;
        if(monthOfDay[m]<d)
            return false;
        return true;
    }

    private boolean isLeapYear(int year)  //判断是否为闰年
    {
        if(year%100!=0&&year%4==0)
            return true;
        else if(year%100==0&&year%400==0)
            return true;
        else
            return false;
    }

    public static void main(String[] args)
    {
        SmartDate sd=new SmartDate(2015,9,1);
        StdOut.println(sd.toString()+"  "+sd.dayOfWeek());
    }
}

1.2.13&1.2.14 用我们对Date的实现作为模板实现Transaction类型           /*euqal()重载---一般分为①是否同引用②是否为空③类型是否相同④3同强转⑤内容是否同*/

根据表1.2.6给出的TransactionAPI如下(去掉compareTo)

Transaction(String who, SmartDate when, double amount) 创建一笔交易
Transaction(String transaction) 创建一笔交易
String  who() 客户名
Date  when() 交易日期
double  amount() 交易金额
String  toString() 对象的字符串表示
boolean  equals(Object that) 该笔交易和that是否相同
public class Transaction
{
    private String name;
    private SmartDate date;
    private double num;
    public Transaction(String who,SmartDate when,double amount)
    {
        name=who.toString();
        date=new SmartDate(when.year(), when.month(), when.day());
        num=amount;
    }
    public Transaction(String trans)
    {
        String[] fields=trans.split("\\s+");
        name=fields[0];
        date=new SmartDate(fields[1]);
        num=Double.parseDouble(fields[2]);
    }
    public String who()
    {
        return name;
    }
    public SmartDate when()
    {
        return date;
    }
    public double amount()
    {
        return num;
    }

    public String toString()
    {
        return "name:"+name+", date:"+date.toString()+", amount:"+num;
    }

    public boolean equals(Object x)  //判断是否相等
    {
        if(this==x)
            return true;
        if(x==null)
            return false;
        if(this.getClass()!=x.getClass())
            return false;
        Transaction that=(Transaction) x;
        return that.date.equals(date)&&that.amount()==num&&that.who()==name;
    }

    public static void main(String[] args)
    {
        SmartDate date=new SmartDate(1996,12,5);
        Transaction tran=new Transaction("Bob",date,1000);
        StdOut.println(tran.toString());
    }
}


提高题

1.2.15 文件输入.基于String的split()方法实现In中的静态方法readInts().

public class H1_2_15
{
    public static int[] readInts(String name)
    {
        In in=new In(name);
        String input=in.readAll();
        String[] words=input.split("\\s+");
        int[] ints=new int[words.length];
        for(int i=0;i<words.length;i++)
            ints[i]=Integer.parseInt(words[i]);
        return ints;
    }

    public static void main(String[] args)
    {
        int[] ints=readInts("h1_2_15.txt");
        for(int a:ints)
            StdOut.printf("%d ",a);
    }
}

1.2.16 有理数.为有理数实现一个不可变数据类型Rational,支持加减乘除操作       /*从中思考如何自己来写出一个API大纲然后实现之*/

Rational(int numerator, int denominator)  
Rational  plus(Rational b) 该数与b之和
Rational  minus(Rational b) 该数与b之差
Rational  times(Rational b) 该数与b之积
Ratioanl  divides(Rational b) 该数与b之商
boolean  equals(Rational that) 该数与that相等吗
String  toString() 对象的字符串表示
public class Rational
{
    private long numerator;
    private long denominator;

    public Rational(int numerator,int denominator)
    {
        if(denominator<0)     //为后续方便,始终将分母变为正数
        {
            numerator=-1*numerator;
            denominator=-1*denominator;
        }
        long cd=gcd(Math.abs(numerator),Math.abs(denominator));  //化为分数的最简形式
        this.numerator=numerator/cd;
        this.denominator=denominator/cd;
    }

    public long getnumerator()
    {
        return numerator;
    }

    public long getdenominator()
    {
        return denominator;
    }

    public Rational plus(Rational b)
    {
        long num=b.getnumerator();
        long den=b.getdenominator();
        long tempnum=num*denominator+numerator*den;
        long tempden=den*denominator;
        long cd=gcd(Math.abs(tempnum),Math.abs(tempden));
        this.numerator=tempnum/cd;
        this.denominator=tempden/cd;
        return this;
    }

    public Rational minus(Rational b)
    {
        long num=b.getnumerator();
        long den=b.getdenominator();
        long tempnum=numerator*den-num*denominator;
        long tempden=den*denominator;
        long cd=gcd(Math.abs(tempnum),Math.abs(tempden));
        this.numerator=tempnum/cd;
        this.denominator=tempden/cd;
        return this;
    }

    public Rational times(Rational b)
    {
        long num=b.getnumerator();
        long den=b.getdenominator();
        long tempnum=numerator*num;
        long tempden=den*denominator;
        long cd=gcd(Math.abs(tempnum),Math.abs(tempden));
        this.numerator=tempnum/cd;
        this.denominator=tempden/cd;
        return this;
    }

    public Rational divides(Rational b)
    {
        long num=b.getnumerator();
        long den=b.getdenominator();
        long tempnum=numerator*den;
        long tempden=num*denominator;
        long cd=gcd(Math.abs(tempnum),Math.abs(tempden));
        this.numerator=tempnum/cd;
        this.denominator=tempden/cd;
        return this;
    }

    public boolean equals(Rational that)
    {
        if(this==that)
            return true;
        if(that==null)
            return false;
        return this.numerator==that.getnumerator()&&this.denominator==that.getdenominator();
    }

    public String toString()
    {
        if(denominator==1)
            return numerator+"";
        else
            return numerator+"/"+denominator;
    }
    private long gcd(long x,long y)
    {
        if(y==0)
            return x;
        long temp=x%y;
        return gcd(y,temp);
    }

    public static void main(String[] args)
    {
        Rational rat1=new Rational(10, 30);
        Rational rat2=new Rational(2, 5);
        StdOut.printf("plus: %s\n",rat1.plus(rat2).toString());
        StdOut.printf("minus: %s\n",rat1.minus(rat2).toString());  //ps:此处rat1已经为+rat2后的值
        StdOut.printf("times: %s\n",rat1.times(rat2).toString());
        StdOut.printf("divides: %s\n",rat1.divides(rat2).toString());
        StdOut.printf("equals?: %s\n",rat1.equals(rat2));
    }
}

1.2.18 累加器的方差. var()---计算方差,  stddev()---计算标准差      /*回顾1.1.34中内容---此处采用的求均值和方差的方式很好,记忆之*/

public class Accumulator
{
    private double m;  //m---均值
    private double s;  //s---总和
    private int N;     //N---个数
    public void addDataValue(double x)
    {
        N++;
        s=s+1.0*(N-1)/N*(x-m)*(x-m);  //见后续推导
        m=m+(x-m)/N;    //即(x-m)/N可以理解为将x与m的偏离程度平均分散给N个
    }
    public double mean()
    {
        return m;
    }
    public double var()
    {
        return s/(N-1);
    }
    public double stddev()
    {
        return Math.sqrt(this.var());
    }
}



以上答案中错误和不合理之处希望大家指出O(∩_∩)O~

注:里面很多来源于《算法》作者给的库

时间: 2024-12-31 16:37:33

1.2 数据抽象的相关文章

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员)、拷贝构造函数

C++ Primer 学习笔记_19_类与数据抽象(5)_初始化列表(const和引用成员).拷贝构造函数  从概念上将,可以认为构造函数分为两个阶段执行: 1)初始化阶段: 2)普通的计算阶段.计算阶段由构造函数函数体中的所有语句组成. 一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 1.对象成员及其初始化 <span style="font-size:14px;">#include <iostream> using namespace std;

C++ Primer 学习笔记_56_类与数据抽象 --消息处理示例

复制控制 --消息处理示例 说明: 有些类为了做一些工作需要对复制进行控制.为了给出这样的例子,我们将概略定义两个类,这两个类可用于邮件处理应用程序.Message类和 Folder类分别表示电子邮件(或其他)消息和消息所出现的目录,一个给定消息可以出现在多个目录中.Message上有 save和 remove操作,用于在指定Folder中保存或删除该消息. 数据结构: 对每个Message,我们并不是在每个Folder中都存放一个副本,而是使每个Message保存一个指针集(set),set中

C++ Primer 学习笔记_57_类与数据抽象 --管理指针成员

复制控制 --管理指针成员 引言: 包含指针的类需要特别注意复制控制,原因是复制指针时只是复制了指针中的地址,而不会复制指针指向的对象! 将一个指针复制到另一个指针时,两个指针指向同一对象.当两个指针指向同一对象时,可能使用任一指针改变基础对象.类似地,很可能一个指针删除了一对象时,另一指针的用户还认为基础对象仍然存在.指针成员默认具有与指针对象同样的行为. 大多数C++类采用以下三种方法之一管理指针成员: 1)指针成员采取常规指针型行为:这样的类具有指针的所有缺陷但无需特殊的复制控制! 2)类

C++ Primer 学习笔记_55_类与数据抽象 --析构函数

复制控制 --析构函数 引言: 在构造函数中分配了资源之后,需要一个对应操作自动回收或释放资源.析构函数就是这样的一个特殊函数,它可以完成所需的资源回收,作为类构造函数的补充. 1.何时调用析构函数 撤销类对象时会自动调用析构函数: Sales_item *p = new Sales_item; { Sales_item item(*p); //调用复制构造函数 delete p; //调用指针p的析构函数 } //调用对象item的析构函数 动态分配的对象只有在指向该对象的指针被删除时才撤销,

C++ Primer 学习笔记_53_类与数据抽象 --友元、static成员

类 --友元.static成员 一.友元 友元机制允许一个类将对其非公有成员的访问权授予指定的函数或类(对未被授权的函数或类,则阻止其访问):友元的声明以关键字friend开始,但是它只能出现在类定义的内部.友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的访问控制影响. [最佳实践] 通常,将友元声明成组的放在类定义的开始或结尾是个好主意! 1.友元关系:一个例子 假设一个窗口管理类Window_Mgr可能需要访问由其管理的Screen对象的内部

系统构架风格---数据抽象与面向对象风格

抽象数据类型概念对软件系统有着重要作用,目前软件界已普遍转向使用面向对象系统.这种风格建立在数据抽象和面向对象的基础上,数据的表示方法和它们的相应操作封装在一个抽象数据类型或对象中.这种风格的构件是对象,或者说是抽象数据类型的实例.对象是一种被称作管理者的构件,因为它负责保持资源的完整性.对象是通过函数和过程的调用来交互的. 图 数据抽象和面向对象风格的体系结构 面向对象的系统有许多的优点,并早已为人所知: (1)      因为对象对其它对象隐藏它的表示,所以可以改变一个对象的表示,而不影响其

对数据类型封装和数据抽象的简单理解

请特别关注程序设计技术,而不是各种语言特征. --<C++程序设计语言> Bjarne Stroustrup 本文是<C++程序设计语言>(Bjarne Stroustrup )的第二章的读书笔记,例子来源于这本书的第二章. 在程序设计之中,我们倾向于将数据结构(也可以说是数据类型)以及一组对其操作的相关过程组织在一起,在逻辑上可以称将其为模块.此时程序分为一些模块,模块包括一组对数据的操作,数据隐藏于模块之中.以下以栈的设计为例,使用C和C++进行设计,简单理解模块化设计中的数据

C++ Primer 学习笔记_54_类与数据抽象 --复制构造函数、赋值操作符

复制控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,需要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用!      复制构造函数:具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式的使用复制构造函数:当将该类型的对象传递给函数或者从函数返回该类型的对象时,将隐式使用复制构造函数.     析构函数:作为构造函数的互补,当对象超出作用域或动态分配的对象被删除

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式、auto_ptr与单例模式、const成员函数、const 对象、mutable修饰符

C++ Primer 学习笔记_24_类与数据抽象(10)--static 与单例模式.auto_ptr与单例模式.const成员函数.const 对象.mutable修饰符 前言 [例]写出面向对象的五个基本原则? 解答:单一职责原则,开放封闭原则,依赖倒置原则,接口隔离原则和里氏替换原则 里氏替换原则:子类型必须能够替换他们的基类型. 设计模式分为三种类型:创建型模式.结构型模式和行为型模式 一.static 与单例模式 1.单例模式 单例模式的意图:保证一个类仅有一个实例,并提供一个访问它

C++ Primer 学习笔记_16_类与数据抽象(2)_隐含的this指针

C++ Primer 学习笔记_16_类与数据抽象(2)_隐含的this指针 1.引言 在前面提到过,成员函数具有一个附加的隐含形参,即指向该类对象的一个指针.这个隐含形参命名为this. 2.返回*this 成员函数有一个隐含的附加形参,即指向该对象的指针,这个隐含的形参叫做this指针(编译器自动传递)使用this指针保证了每个对象可以拥有不同数值的数据成员,但处理这些成员的代码可以被所有对象共享.成员函数是只读的代码,由所有对象共享,并不占对象的存储空间,因为this指针指向当前对象,所以