第一題:如果不缺內存,如何使用一個具有庫的語言來實現一種排序算法?
直接使用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。