第一题:如果不缺内存,如何使用一个具有库的语言来实现一种排序算法?
直接使用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。