编程珠玑第二版 ---- 第一章个人习题集(Java)(还未做完)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_24986539/article/details/81172185

第一题:如果不缺内存,如何使用一个具有库的语言来实现一种排序算法?

直接使用Collections.sort(list)排序

第二题:如何使用位逻辑运算来实现位向量?

package com.xck.util;

/**
 * 位向量
 *
 * 这里使用字节数组来实现位向量,主要目的是给文件中所保存的(无重复的)整数排序 --- 位排序。
 *
 * 原理:
 * 一个字节有8bit,相当于一个字节可以表示8个非负整数。
 * 在每个bit上用0/1来表示是否有这个整数存在,0-没有,1-有。
 * 存放完成之后,只需要从头开始遍历,跳过0的位,将有1的位所表示的整数放入文件中。
 *
 * 好处:在内存中只需要处理0/1即可,所有的实现均采用位运算
 */
public class BitVector {
    private byte[] bitVector; //位向量
    private int size; //位向量可以存放整数的数量,最多Integer.MAX_VALUE

    public BitVector(int size) {
        //size>>3=size/8
        this.bitVector = new byte[(size>>3)+1];
        this.size = size;
    }

    /**
     * 置位:1
     * index&7=index%8
     * bitVector[index>>3] = 00000000
     * 00000000 | (1<<5) = 00000000 | 00010000 = 00010000
     * @param index 需要存储的数字
     */
    public void set(int index){
        bitVector[index>>3] |= 1<<(index&7);
    }

    /**
     * 判断index是否存在
     * @param index
     * @return
     */
    public boolean isExist(int index){
        return (bitVector[index>>3] & (1<<(index&7)))!=0;
    }

    /**
     * 清0
     * @param index
     */
    public void clear(int index){
        if(!isExist(index)){
            return;
        }
        bitVector[index>>3] &= ~(1<<(index&7));
    }

    public int getSize(){
        return size;
    }
}

第三题:实现一个位图排序。给不重复的,最大为1000w的的,100w个整数排序。

这里面会涉及到如何生成这样的一个输入集合。(第四题),利用第四题生成的txt文件来排序。

//将随机数从文件中读取到位向量中 
public static BitVector readFileToVector(String pathName){
        File file = null;
        FileReader fr = null;
        BufferedReader br = null;
        BitVector bitVector = new BitVector(10000000);
        try {
            file = new File(pathName);
            if(file.exists()){
                fr = new FileReader(file);
                br = new BufferedReader(fr);

                int count = 0;
                String result;
                while((result = br.readLine())!=null){
                    bitVector.set(Integer.parseInt(result));
                    count++;
                }
                System.out.println(count);
            }
            return bitVector;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
                if(fr != null){
                    fr.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        return bitVector;

第四题:如果你看了第三题,你将会面对生成小于n且没有重复的k个整数的问题。那么如何生成位于0至n-1之间的k个不同的随机顺序的随机整数?

看到这题,我的第一个思路就是:随机数一个个生成,然后用Set集合去重,若重复则重新生成随机数,否则直接写入文件中。这里需要注意一个问题,虽然Set是无序的,但是最后用迭代器遍历出来的并不是真正意义上的无序,大致上还是有序的。

public static int getRandomNumInRange(int minBound, int maxBound){
    Random random = new Random();
    return random.nextInt(maxBound)%(maxBound-minBound+1)+minBound;
}

/**
 * 用于测试,生成无重复的,最大为1000w的,100w个非负整数,并写入文件中.
 * 利用Set集合不允许重复的特性
 * @param pathName
 */
public static void writeRandomNum_NoRepeate(String pathName){
        File file = null;
        FileWriter fw = null;
        BufferedWriter bw = null;
        try {
            file = new File(pathName);
            if(!file.exists()){
                file.createNewFile();
                fw = new FileWriter(file);
                bw = new BufferedWriter(fw);
                Set<Integer> set = new HashSet<Integer>();

                int oldSize = set.size();
                int count = 0;
                while(set.size()<1000000){
                    int random = getRandomNumInRange(0, 10000000);
                    set.add(random);
                    int newSize = set.size();
                    if (newSize > oldSize) {
                        count++;
                        bw.write(random + "\r\n");
                    }
                    oldSize = newSize;
                }
                System.out.println(count);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
                if(fw != null){
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

经过网上搜索资料,我看到这位博主的思路,我觉得很不错(但是我用java写运行太慢了,我等了好久啊,不知道是不是哪里写的有问题)。他的思想说起来也很简单,就是创建一个可以存放从1到1000w的数组,然后按序放进去,然后取前面100w的长度遍历,对于每个遍历到的index都随机一个index与之交换,目的是打乱顺序,这样就生成了一组随机数。

List<Integer> list = new ArrayList<Integer>();
for(int i=0; i<10000000; i++){
    list.add(i);
}
               
int temp;
for(int i=0; i<1000000; i++){
    int randomIndex = getRandomNumInRange(0, 10000000);
    if(randomIndex!=i){
        temp = list.get(i);
        list.add(i, list.get(randomIndex));
        list.add(randomIndex, temp);
    }
}
file.createNewFile();
fw = new FileWriter(file);
bw = new BufferedWriter(fw);
for(int i=0; i<1000000; i++){
    bw.write(list.get(i) + "\r\n");
}

第五题:那个程序员说他有1MB的可用存储空间,但是我们概要描述的代码需要1.25MB的空间。他可以不费力气的索取到额外的空间。如果1MB空间是严格的边界,你会推荐如何处理呢?你的算法运行时间又是多少呢?

第一种方案:

根据书前面所述,可以采用多趟读取的方式,每次读取一定范围内的数据放入内存中排序,然后输出到文件中,循环往复。一个int是4个字节,1M大约可以存储100w个字节,也就是25w个int类型的整数。

所以实现方式就比较简单了:扫描txt文件,读取小于25w的全部数据,排序然后输出到文件中;再读取小于50w的全部数据,排序追加到文件末尾,以此类推,需要40趟。

第二种方案:采用两趟算法,为了节约内存,可以使用位向量,500w个位,也就是625000B来表示500w个数,第一次读取0~500w放入位向量中排序,然后输出到文件中,第二次全部读完。

第三种方案:1000w个位可以表示1000w个整数,而1000wbit占了125w个字节。这里因为电话号码位数是固定的7位,所以0开头的电话号码可以不予考虑(100w),答案说没有以1开头的(不知道为啥,姑且信了),然后这样空间需求可以降低到105wB<1MB。

其实我一直没搞明白在java中怎么测试class文件运行的所需内存,我尝试修改jvm虚拟机参数我发现1m以内老是出错,说初始化空间不够,java自带的监控工具没成都是还没有打开那个进程就已经结束了。所以上一切还都是猜想,还没有真正证明在1MB内。

第六题:如果那个程序员说的不是每个整数最多出现一次,而是每个整数最多出现10次,你又如何建议他呢?你的解决方案如何随着可用存储空间总量的变化而变化呢?

这个可以根据之前第三题不重复的思路进行延伸。之前因为是不重复所以可以用1bit表示是否存在,现在变成10次,10可以用4bit表示,所以这次可以变成4位表示一个整数,若是0则表示不存在,最多到10(这里生成的时候要进行限制,计数的时候也要)。

第七题:本书的第1.4节描述的程序存在一些缺陷。首先是假定在输入中没有出现重复的整数。那么如果某个数出现超过一次的话,会发生什么?在这种情况下,如何修改程序来调用错误处理函数?当输入的整数小于0或大于等于n时,又会发生什么?如果某个输入不是数值又如何?在这些情况下,程序该如何处理?程序还应该包含哪些明智的检查?描述一些用以测试程序的小型数据集合,并说明如何正确处理上述以及其他的不良情况。

第一个问题:某个数出现超过一次,其实并不影响,因为|(按位或)运算1 | 1=1,所以不会影响。这样看来这个数据结构去重也挺好用。

第二个问题:输入整数小于0或大于等于n,在从文件读取数据的时候,可以从配置文件中读取预先设定好的范围

第三个问题:若输入不是数值会报错,这里考虑可以加个小的try-catch,若Integer转换报错则continue。

第八题:当那个程序员解决该问题的时候,美国所有免费电话的区号都是800.现在免费电话的区号包括800,877和888,而且还在增多。如何在1MB空间内完成对所有这些免费电话号码的排序?如何将免费电话号码储存在一个集合中,要求可以实现非常快速的查找以判定一个给定的免费电话号码是否可用或者已经存在?

猜你喜欢

转载自blog.csdn.net/qq_24986539/article/details/81172185