編程珠璣第二版 ---- 第一章個人習題集(Java)(還未做完)

第一題:如果不缺內存,如何使用一個具有庫的語言來實現一種排序算法?

直接使用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空間內完成對所有這些免費電話號碼的排序?如何將免費電話號碼儲存在一個集合中,要求可以實現非常快速的查找以判定一個給定的免費電話號碼是否可用或者已經存在?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章