《編程珠璣》第2章三個問題

問題一:

給定一個最多包含40億個隨機排列的32位整數(shù)的順序文件,找出一個不在文件中的32位整數(shù)。在具有足夠內存的情況下,如何解決該問題皮钠?如果有幾個外部的“臨時”文件可用,但是僅有幾百字節(jié)的內存赠法,又該如何解決該問題麦轰?

先考慮有足夠的內存,我們可以采用位圖技術砖织,即使用536870912個8位字節(jié)形成的位圖來表示已看到的整數(shù)款侵。最后再對位圖遍歷一遍,找到某個位為0即可侧纯。實現(xiàn)代碼如下:

#define BITSPERWORD 32
#define SHIFT 32
#define MASK 0x1F
#define N 4000000000

int a[1 + N / BITSPERWORD];

void set(int i) {
    a[i >> SHIFT] |= (1 << (i & MASK));
}

void clr(int i) {
    a[i >> SHIFT] &= ~(1 << (i & MASK));
}

int test(int i) {
    return a[i >> SHIFT] & (i << (i &MASK));
}

int main(void) {
    int i;
    for (i = 0; i < N; i++) {
        clr(i);
    }
    while (scanf("%d", &i) != EOF) {
        set(i);
    }
    for (i = 0; i < N; i++) {
        if (test(i)) {
            printf("%d\n", i);
        }
    }
    return 0;
}

然而新锈,該問題還問到在僅有幾百個字節(jié)內存和幾個稀疏順序文件的情況下如何找到缺失的整數(shù)?我們從表示每個整數(shù)的32位的視角來考慮二分搜索眶熬。算法的第一趟(最多)讀取40億個輸入整數(shù)妹笆,并把起始位為0的整數(shù)寫入一個順序文件,并把起始位為1的整數(shù)寫入另一個順序文件為1寫入另一個順序文件娜氏。這兩個文件中拳缠,有一個文件最多包含20億個整數(shù),我么接下來將該文件用作當前輸入并重復探測過程贸弥,但這次探測的是第二個位窟坐。如果原始的輸入文件包含n個元素,那么第一趟將讀取n個整數(shù)绵疲,第二趟最多讀取n/2個整數(shù)哲鸳,以此類推。參考代碼如下:

int split(int* a, int* b, int*c, int alen, int bit) {
    int biter, citer, i;
    int v=0, re = 0, *t;

    while(bit--){ //bit從32開始
        v = (1 << bit);
        for(i=biter=citer=0; i < alen; i++) {
            if(a[i] & (1<<bit)) { //將當前位為0和1的整數(shù)分到不同的數(shù)組
                b[biter++] = a[i];
            } else {
                c[citer++] = a[i];
            }
        }
        if(biter <= citer) {
            re += v;
            t = a;
            a = b;
            b = t;
            alen = biter;
        } else {
            t = c;
            c = a;
            a = t;
            alen = citer;
        }
    }
    return re;
}

問題二

將一個n元一維向量向左旋轉i個位置盔憨。例如帕胆,當n=8且i=3時,向量abcdefgh旋轉為defghabc般渡。

方法一:
首先移動x[0]到臨時變量t懒豹,然后移動x[i]至x[0]芙盘,x[2i]至x[i],依次類推(x中的所有下標對n取模)脸秽,直至返回到取x[0]中的元素儒老,此時改為從t取值然后終止過程。如果該過程沒有移動全部元素记餐,就從x[1]開始再次進行移動驮樊,直到所有的元素都已經移動為止。參考代碼如下:

void rotate(int *nums, int len, int rotdist) {
    int i;
    for (i = 0; i < gcd(rotdist, len); i++) {
        int t = nums[i];
        int j = i;
        while (true) {
            int k = (j + rotdist) % len;
            if (k == i) {
                break;
            }
            nums[j] = nums[k];
            j = k;
        }
        nums[j] = t;
    }
}

方法二:
旋轉向量x其實就是交換向量ab的兩端片酝,得到向量ba囚衔。這里a表示x中的前i個元素。假設a比b短雕沿。將b分為bl和br练湿,使得br具有與a相同的長度。交換a和br审轮,也就是將ablbr轉換為brbla肥哎。序列a此時已經處于其最終的位置,因此現(xiàn)在的問題就集中到交換b的兩部分疾渣。由于新問題與原來的問題具有相同的形式篡诽,我們可以遞歸得解決之。參考代碼如下:

void swap(string &str, int leftBegin, int rightBegin, int count) {
    while (count--) {
        char temp = str[leftBegin];
        str[leftBegin] = str[rightBegin];
        str[rightBegin] = temp;
        
        leftBegin++;
        rightBegin++;
    }
}

void rotate(string &str, int rotdis) {
    int len = (int) str.size();
    int i = rotdis;
    int p = rotdis;
    int j = len - rotdis;
    
    while (i != j) {
        if (i > j) {
            swap(str, p - i, p, j);
            i -= j;
        } else {
            swap(str, p - i, p + j - i, i);
            j -= i;
        }
    }
    swap(str, p - i, p, i);
}

給定一個英語詞典榴捡,找出其中的所有變味詞集合杈女。例如,"pots"吊圾、"stop"碧信、"tops"互為變味詞,因為每一個單詞都可以通過改變其他單詞中字母的順序來得到街夭。

方法一
我們可以計算每個單詞的hash值,如果是變味詞躏筏,可以保證hash值肯定相同板丽。但并不能保證相同的hash值就一定是變味詞,有可能兩個單詞不是變味詞趁尼,但恰好具有相同的hash值埃碱,這個時候就需要解決沖突,類似于散列表中的散列沖突酥泞。我們可以用一個map的key來保存單詞的hash值砚殿,value保存該hash值的單詞保存的位置。因為某個hash值可能存在多種變味詞芝囤,因此value本身是一個列表似炎。比如有個單詞A辛萍,首先計算A的hash值,然后用hash值從map中獲取對應的存放位置羡藐。因為存放位置可能有多個贩毕,我們需要每個都去判斷是不是屬于它的存放位置。參考代碼如下:

class Solution {
private:
    int prime[26] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101};
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<int, vector<int>> mapper;
        vector<vector<string>> result;
        for (string &str : strs) {
            int hashVal = caculateHashVal(str); //計算對應的hash值
            unordered_map<int, vector<int>>::iterator pos;
            unordered_map<int, vector<int>>::iterator end = mapper.end();
            //若沒有找到仆嗦,則將其放在列表的最后一個位置
            if ((pos = mapper.find(hashVal)) == end) {
                putInEnd(result, mapper, hashVal, str);
            } else {
                //找到后需要逐個判斷是否屬于它的存放位置
                vector<int> &v = pos->second;
                bool isExist = false;
                for (int index : v) {
                    string &str1 = result[index][0];
                    if (isSameGroup(str1, str)) {
                        result[index].push_back(str);
                        isExist = true;
                        break;
                    }
                }
                if (!isExist) {
                    putInEnd(result, mapper, hashVal, str);
                }

            }

        }
        for (vector<string> &v : result) {
            sort(v.begin(), v.end());
        }
        return result;

    }

    int caculateHashVal(string &str) {
        int result = 0;
        for (char c : str) {
            int num = c - 'a';
            result += num * prime[num];
        }
        return result;
    }

    void putInEnd(vector<vector<string>> &result, unordered_map<int, vector<int>> &mapper, int hashVal, string &str) {
        int len = result.size();
        result.resize(len + 1);
        mapper[hashVal].push_back(len);
        result[len].push_back(str);
    }

    bool isSameGroup(string &str1, string &str2) {
        int len = str1.size();
        if (str2.size() == len) {
            int flags[26];
            memset(flags, 0, sizeof(int) * 26);
            for (char c : str1) {
                flags[c - 'a']++;
            }
            for (char c : str2) {
                flags[c - 'a']--;
            }
            for (int i = 0; i < 26; i++) {
                if (flags[i] != 0) {
                    return false;
                }
            }
            return true;
        }
        return false;
    }

};

方法二
我們可以標識字典里的每一個詞辉阶,使得在相同變味詞類中的單詞具有相同的標識。然后瘩扼,將具有相同標識的單詞集中在一起谆甜。這將原始的變味詞問題簡化為兩個子問題:選擇標識和集中具有相同的單詞。
對于第一個問題集绰,我們可以使用基于排序的標識:將單詞中的字母表順序排列规辱。"deposit"的標識就是"deiopst",這也是"dopiest"和其他任何該類單詞的標識倒慧。要解決第二個問題按摘,我們將所有的單詞按照其標識的順序排序。

public List<List<String>> groupAnagrams(String[] strs) {
    HashMap<String, List<String>> map = new HashMap<>();
    for (String s : strs) {
        char[] ar = s.toCharArray();
        Arrays.sort(ar);
        String sorted = String.valueOf(ar);
        List<String> list = map.get(sorted);
        if (list == null) list = new ArrayList<String>();
        list.add(s);
        map.put(sorted, list);
    }
    List<List<String>> res = new ArrayList<>();
    for (List<String> l : map.values()) {
        Collections.sort(l);
        res.add(l);
    }
    return res;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末纫谅,一起剝皮案震驚了整個濱河市炫贤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌付秕,老刑警劉巖兰珍,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異询吴,居然都是意外死亡掠河,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門猛计,熙熙樓的掌柜王于貴愁眉苦臉地迎上來唠摹,“玉大人,你說我怎么就攤上這事奉瘤」蠢” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵盗温,是天一觀的道長藕赞。 經常有香客問我,道長卖局,這世上最難降的妖魔是什么斧蜕? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮砚偶,結果婚禮上批销,老公的妹妹穿的比我還像新娘洒闸。我一直安慰自己,他們只是感情好风钻,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布顷蟀。 她就那樣靜靜地躺著,像睡著了一般骡技。 火紅的嫁衣襯著肌膚如雪鸣个。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天布朦,我揣著相機與錄音囤萤,去河邊找鬼。 笑死是趴,一個胖子當著我的面吹牛涛舍,可吹牛的內容都是我干的。 我是一名探鬼主播唆途,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼富雅,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了肛搬?” 一聲冷哼從身側響起没佑,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎温赔,沒想到半個月后蛤奢,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡陶贼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年啤贩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拜秧。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡痹屹,死狀恐怖,靈堂內的尸體忽然破棺而出枉氮,到底是詐尸還是另有隱情志衍,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布嘲恍,位于F島的核電站,受9級特大地震影響雄驹,放射性物質發(fā)生泄漏佃牛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一医舆、第九天 我趴在偏房一處隱蔽的房頂上張望俘侠。 院中可真熱鬧象缀,春花似錦、人聲如沸爷速。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽惫东。三九已至莉给,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間廉沮,已是汗流浹背颓遏。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滞时,地道東北人叁幢。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像坪稽,于是被迫代替她去往敵國和親曼玩。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容