202204010 java笔试题

这篇博客涵盖了算法的时间复杂度分析,如O(nlogn)复杂度的典型问题;程序与进程的区别,强调动态特性的关键;猜数字游戏的最优策略分析;Java异常体系结构,包括Error和Exception及其实例;类加载器的工作原理与层次结构;有序集合交集的计算方法;以及多线程编程中奇偶数分组打印文件内容的实现。
摘要由CSDN通过智能技术生成

1.假设某算法的时间复杂度符合递推关系式T(n)=2T(n/2)+n,那么该算法的时间复杂度相当于(C )。

A.O(n^2)

 B.O(log n) 

C.O(nlog n) 

D.O(n)

2.程序和进程的本质区别是( D)。

A.独占使用和分时使用计算机资源 

B.非顺序和顺序执行机器指令

C.在外存和内存存储 

D.静态和动态特征

3.甲、乙两个人在玩猜数字游戏,甲随机写了一个数字,在[1,100]区间之内,将这个数字写在了一张纸上,然后乙来猜。如果乙猜的数字偏小,甲会提示:“数字偏小”。一旦乙猜的数字偏大,甲以后就再也不会提示了,只会回答“猜对或猜错”。请问:乙至少猜多少次才可以准确猜出这个数字?在这种策略下,乙猜的第一个数字是什么?

答案:乙至少猜14次才可以准确猜出这个数字,在这种策略下,乙猜的第一个数字是14。分析:数字所在区间为[1,100],乙在猜测数字时,存在以下三种可能性:

1)直接猜中。

2)猜测数字大于真实值。

3)猜测数字小于真实值。

以下将分别针对这三种不同的情况进行分析:

第1)种直接猜中的情况概率很低,只有百分之一,不具有代表意义。

第2)种情况,乙猜测的数字的值比真实值大,此时没有提示,假设待猜测的数字的值为N2,乙猜测的数字的值为N1,很显然,在本情况下,N1﹥N2,此时,为了找到N2,只能逐一在[1,N1-1]之间进行猜测,即1≤N2≤N1-1。

只有第3)种情况会存在提示,假设待猜测的数字的值为N2,乙猜测的数字的值为N1,很显然,在本情况下N1﹤N2,根据提示可知,可以继续在[N1+1,100]中选择另外的数N2,即N1+1≤N2≤100。所以,对于第2)种情况,一共需要猜测的次数为N1-1+1=N1次(其中,N1-1表示需要在[1,N1-1]之间逐一取值,1表示进行第一次测试)。对于第3)种情况,如果第一次猜的数字小于真实值,但第二次猜的数字大于真实值,此时需要尝试的总次数是[N1+1,N2-1]的元素个数加2(加2是N2和N1本身猜用掉一次),即为N2-N1+1次,根据“每次猜错后,尝试猜测的总次数相等”思想,有N1=N2-N1+1,可知N2=2N1-1,增量为N1-1。类似地,前两次猜得偏小,但第三次猜大,尝试总次数为[N2+1,N3-1]的元素个数加3,即N3-N2+2,那么有N3-N2+2=N1,N3=N2+N1-2,增量为N1-2……依次类推,增量是随着猜测次数的增加而逐1地减少。设最后一次猜测为k,则Nk=N1+(N1-1)+(N1-2)+…+1,Nk是等于或大于100的第一个数,根据等差数列求和公式可以算出N1=14,N2=27,N3=39…(14,27,39,50,60,69,77,84,90,95,99)。序列是14、27、39、50、60、69、77、84、90、95、99。因为无论第几次猜大了,

4.请给出Java异常类的继承体系结构,以及Java异常的分类,且为每种类型的异常各举几个例子。

Java语言提供了两种错误的处理类,分别为Error和Exception,且它们拥有共同的父类:Throwable。

Error表示程序在运行期间出现了非常严重的错误,并且该错误是不可恢复的。OutOfMemoryError、ThreadDeath和NoClassDefFoundError等都属于错误,当这些错误发生时,JVM一般会选择将线程终止。

Exception表示可恢复的异常,是编译器可以捕捉到的。它包含两种类型:运行时异常Runtime Exception)和检查异常(Checked Exception)。

1)检查异常是在程序中最经常碰到的异常,比如最常见的IO异常和SQL异常。对于这种异常,都发生在编译阶段,Java编译器强制程序去捕获此类型的异常,即把可能会出现这些异常的代码放到try块中,把对异常的处理的代码放到catch块中。这种异常一般在如下几种情况中使用:

①异常的发生并不会导致程序出错,进行处理后可以继续执行后续的操作。例如,当连接数据库失败后,可以重新连接后进行后续操作。

②程序依赖于不可靠的外部条件,例如系统IO。几个检查异常如下:SQLException、IOException和UnknownHostException。

2)对于运行时异常,编译器没有强制对其进行捕获并处理。如果不对这种异常进行处理,当出现这种异常时,会由JVM来处理。在Java语言中,最常见的运行时异常有如下几种:NullPointerException(空指针异常)、ArrayStoreException(数据存储异常)、ClassCastException(类型转换异常)、InexOutOfBoundException(数组越界异常)、BufferOverflowException(缓冲区溢出异常)及ArithmeticException(算术异常)等。

5.描述Java类加载器的原理及其组织结构。

 Java语言是一种具有动态性的解释型语言,类只有被加载到JVM中后才能运行。这个加载过程是由加载器来完成的,具体而言就是由ClassLoader和它的子类来实现的。类加载器本身也是一个类,其实质是将类文件从硬盘读取到内存中。

类的加载方式分为隐式装载与显式装载两种。隐式装载指的是程序在使用new等方式创建对象时,会隐式地调用类的加载器将对应的类加载到JVM中。显式装载指的是通过直接调用class.forName()方法将所需的类加载到JVM中。任何一个工程项目都是由许多个类组成的,当程序启动的时候,只把需要的类加载到JVM中,其他的类只有在被使用到的时候才会被加载,采用这种方法,一方面可以加快加载速度,另一方面可以节约程序运行过程中对内存的开销。此外,在Java语言中,每个类或接口都对应一个.class文件,这些文件可以被看成是一个个可以被动态加载的单元。在Java语言中,类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(例如基类)完全加载到JVM中,至于其他类,则在需要的时候才加载。

在Java语言中,可以把类分为三类:系统类、扩展类和自定义类。Java语言针对这三种不同的类提供了三种类型的加载器,这三种加载器的关系如下:Bootstrap Loader-负责加载系统类(jre/lib/rt.jar的类)|--ExtClassLoader-负责加载扩展类(jar/lib/ext/*.jar的类)|--AppClassLoader-负责加载应用类(classpath指定的目录或jar中的类)

它们是通过委托的方式实现的具体而言,就是当有类需要被加载时,类装载器会请求父类来完成这个载入工作,父类会使用其自己的搜索路径来搜索需要被载入的类,如果搜索不到,才会由子类按照其搜索路径来搜索待加载的类。加载器的工作原理:TestLoader类是由AppClassLoader来加载的。另外需要说明的一点是由于Bootstrap Loader是使用C++语言来实现的。类加载的主要步骤分为以下三步:

1)装载:根据查找路径找到相对应的class文件,然后导入。

2)链接:链接又可以分为三个小的步骤,具体如下:

①检查:检查待加载的class文件的正确性。

②准备:给类中的静态变量分配存储空间。

③解析:将符号引用转换成直接引用(这一步是可选的)。

3)初始化:对静态变量和静态代码块执行初始化工作。

6.有两个有序的集合,集合中的每个元素都是一段范围,求其交集,例如集合{[4,8][9,13]}和{[6,12]}的交集为{[6,8],[9,12]}。 

import java.util.ArrayList;

/**
 * @author 龙御修
 * @create 2022-04-10 17:21
方法一
 */
public class Test {
    private static  MySet getIntersection(MySet s1,MySet s2){
        if(s1.getMin()< s2.getMin()){
            if(s1.getMax()<=s2.getMin())
                return null;
            else if(s1.getMax()<=s2.getMax())
                return new MySet(s2.getMin(),s1.getMax());
            else
                return new MySet(s2.getMin(),s2.getMax());
        }
        else if(s1.getMin()<= s2.getMax()){
            if(s1.getMax()<= s2.getMax())
                return new MySet(s1.getMin(),s1.getMax());
            else
                return new MySet(s1.getMin(), s2.getMax());
        }
        else {
            return null;
        }
    }
   public static ArrayList<MySet> getIntersection(ArrayList<MySet> l1,ArrayList<MySet>l2) {
       ArrayList<MySet> result = new ArrayList<MySet>();
       for (int i = 0; i < l1.size(); i++)
           for (int j = 0; j < l2.size(); j++) {
               MySet s = getIntersection(l1.get(i), l2.get(j));
               if (s != null)
                   result.add(s);
           }
           return result;
       }

    public static void main(String[] args) {
        ArrayList<MySet>l1=new ArrayList<MySet>();
        ArrayList<MySet>l2=new ArrayList<>();
        l1.add(new MySet(4,8));
        l1.add(new MySet(9,13));
        l2.add(new MySet(6,13));
        ArrayList<MySet> result=getIntersection(l1,l2);
        for(int i=0;i<result.size();i++){
            System.out.println("["+result.get(i).getMin()+","+result.get(i).getMax()+"]");
        }
    }

}
class MySet{
    private  int min;
    private  int max;

    public MySet(int min, int max) {
        this.min = min;
        this.max = max;
    }

    public int getMin() {
        return min;
    }

    public void setMin(int min) {
        this.min = min;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }
}
import java.util.ArrayList;

/**
 * @author 龙御修
 * @create 2022-04-10 17:51
 */
public class Test {
    public static ArrayList<MySet>getIntersection(ArrayList<MySet> l1,ArrayList<MySet>l2){
        ArrayList<MySet>result=new ArrayList<>();
        int j=0,i=0;
        while (i<l1.size()&&j<l2.size()){
            MySet s1=l1.get(i);
            MySet s2=l2.get(j);
            if(s1.getMin()<s2.getMin()){
                if(s1.getMax()<s2.getMin()){
                    i++;
                }
                else if(s1.getMax()<=s2.getMax()){
                    result.add(new MySet(s2.getMin(), s1.getMax()));
                    i++;
                }
                else {
                    result.add(new MySet(s2.getMin(), s2.getMax()));
                    j++;
                }
            }
            else  if(s1.getMin()<=s2.getMax()){
                if(s1.getMax()<=s2.getMax()){
                    result.add(new MySet(s1.getMin(),s1.getMax()));
                    i++;
                }
                else {
                    result.add(new MySet(s1.getMin(),s2.getMax()));
                    j++;
                }
            }
            else {
                j++;
            }
        }
        return result;
    }

    public static void main(String[] args) {
        ArrayList<MySet> l1=new ArrayList<>();
        ArrayList<MySet>l2=new ArrayList<>();
        l1.add(new MySet(4,8));
        l1.add(new MySet(9,13));
        l2.add(new MySet(6,13));
        ArrayList<MySet> result=getIntersection(l1,l2);
        for(int i=0;i<result.size();i++){
            System.out.println("["+result.get(i).getMin()+","+result.get(i).getMax()+"]");
        }

    }
}
class  MySet{
    private  int min;
    private  int max;

    public int getMin() {
        return min;
    }

    public void setMin(int min) {
        this.min = min;
    }

    public int getMax() {
        return max;
    }

    public void setMax(int max) {
        this.max = max;
    }

    public MySet(int min, int max) {
        this.min = min;
        this.max = max;
    }
}

 7.任意2n个整数,从其中选出n个整数,使得选出的n个整数之和与剩下的n个整数之和的差最小

/**
 * @author 龙御修
 * @create 2022-04-10 18:34
 */
public class Demo {
    private final int MAXSUM = 1000;

    private int min(int m, int n) {
        return m > n ? n : m;
    }

    public void getNearest(int[] arr) {
        int n = arr.length / 2;
        int sum = 0;
        for (int i = 0; i < 2 * n; i++)
            sum += arr[i];
        int s;
        boolean[][] dp = new boolean[2 * n][MAXSUM];
        dp[0][0] = true;
        for (int k1 = 0; k1 < 2 * n; k1++)
            for (int k2 = min(k1, n); k2 >= 1; k2--)
                for (s = 1; s < sum / 2; s++)//判断包含k2个数的子数组和为s且包括arr[k1]的情况是否存在
                    if (s >= arr[k1] && dp[k2 - 1][s - arr[k1]])
                        dp[k2][s] = true;
        //找出包含n个元素的子数组的和最接近sum/2的值
        for (s = sum / 2; s >= 1 && !dp[n][s]; s--) ;
        System.out.println("两个数组的差为:" + (sum - 2 * s));
    }

    public static void main(String[] args) {
        int[] arr = {1, 34, 22, 12, 23, 12};
        new Demo().getNearest(arr);
    }
}

8.一个文件中有10000个数,用Java语言实现一个多线程程序,将这10000个数输出到5个不同文件中(不要求输出到每个文件中的数量相同)。要求启动10个线程,两两一组分为5组。每组两个线程分别将文件中的奇数和偶数输出到该组对应的一个文件中,需要偶数线程每打印10个偶数以后,就将奇数线程打印10个奇数,如此交替进行。同时需要记录输出进度,每完成1000个数就在控制台中打印当前完成数量,并在所有线程结束后,在控制台输出“Done”。

import java.io.*;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author 龙御修
 * @create 2022-04-10 19:07
 */
public class Demo1 {
    private static final int count = 10000;
    private static final int threadGroupCont = 5;
    private static final String intputFile = "testInput.txt";

    public static void generateTestFile() throws IOException {
        //用随机数生成1000个测试数据放到文件中
        PrintWriter pw = new PrintWriter(new FileWriter(new File(intputFile)), true);
        Random random = new Random();
        for (int i = 0; i < count; i++) {
            pw.write(Math.abs(random.nextInt()) % count + ",");
        }
        pw.flush();
        pw.close();
    }

    public static void main(String[] args) {
        try {
            generateTestFile();
            BufferedReader reader = new BufferedReader(new FileReader(intputFile));
            String str = reader.readLine();
            reader.close();
            String[] strs = str.split(",");
            int index = 0;
            //为了简单,每个文件输出数字的个数相同
            int countForEachFile = count / threadGroupCont;
            for (int i = 0; i < threadGroupCont; i++) {
                int records[] = new int[countForEachFile];
                for (int j = 0; j < countForEachFile; j++) {
                    records[j] = Integer.parseInt(strs[index]);
                    index++;
                }
                PrintGroup group = new PrintGroup(records, i);
                group.startPrint();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

class PrintGroup {
    //这个线程组输出数字的个数
    private static volatile int count = 0;
    private Lock lock = new ReentrantLock();
    private Condition oddLock = lock.newCondition();
    private Condition evenLock = lock.newCondition();
    //这个线程组需要输出的数字数组
    private int records[];
    //这个线程需要把数字输出到同一个文件,因此需要共享一个writer,
    //由于任意时刻只会有一个线程写文件,因此,不需要同步
    private PrintWriter writer;
    //记录输出奇数所在的数组下标
    private volatile int oddIndex = 0;
    //记录偶数所在的数组
    private volatile int evenIndex = 0;
    //输出奇数的线程
    private OddPrintThread oddPrintThread;
    //输出偶数的线程
    private EvenPrintThread evenPrintThread;
    private volatile boolean first = true;
    private int[] result = new int[2000];
    private int index = 0;
    public PrintGroup(int[] records, int id) throws Exception {
        this.records = records;
        this.writer = new PrintWriter(new FileWriter(new File("output" + id + ".txt")), true);

    }
    public void startPrint() {
        oddPrintThread = new OddPrintThread();
        evenPrintThread = new EvenPrintThread();
        oddPrintThread.start();
        evenPrintThread.start();
    }
    private class OddPrintThread extends Thread {
        public void run() {
            while (true) {
                try {
                    lock.lock();
                    //第一次运行时,需要等待打印偶数的线程先执行
                    if (first) {
                        first = false;
                        evenLock.await();
                    }
                    for (int i = 0; i < 10; ) {
                        //素组中的偶数和奇数都打印完
                        if (oddIndex >= records.length && evenIndex >= records.length) {
                            writer.flush();
                            writer.close();
                            return;
                        }
                        //如果所有的奇数都打印完了,则不打印奇数,让打印偶数的线程有运行机会
                        if (oddIndex >= records.length) {
                            break;
                        }
                        //把奇数输出到文件,并记数
                        if (records[oddIndex] % 2 == 1) {
                            i++;
                            writer.println(records[oddIndex] + " ");
                            result[index++] = records[oddIndex];
                            writer.flush();
                            addCount();
                        }
                        oddIndex++;
                    }
                    //打印完10个奇数后,通知打印偶数的线程开始运行
                    oddLock.signal();
                    //接着等待打印偶数的线程结束
                    evenLock.await();
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    oddLock.signal();
                    lock.unlock();
                }
            }
        }

    }
    private class EvenPrintThread extends Thread {
        public void run(){
            while (true){
                try {
                    while (first){
                        Thread.sleep(1);

                    }
                    lock.lock();
                    for(int i=0;i<10;){
                        if(oddIndex>=records.length&&evenIndex>=records.length){
                            String s="";
                            for(int k=0;k<2000;k++){
                                s+=(result[k]+" ");

                            }
                            writer.flush();
                            return;
                        }
                        if(evenIndex>=records.length){
                            break;
                        }
                        if(records[evenIndex]%2==0){
                            i++;
                            writer.println(records[evenIndex]+" ");
                            result[index++]=records[evenIndex];
                            writer.flush();
                            addCount();
                        }
                        evenIndex++;
                    }
                    evenLock.signal();
                    oddLock.await();

                }catch (Exception e){

                }finally {
                   evenLock.signal();
                   lock.unlock();
                }
            }
        }

    }

    private synchronized static void addCount() {
        count++;
        if(count%1000==0){
            System.out.println("已完成:"+count);
            if(count==10000){
                System.out.println("Done");
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Royalreairman

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值