《編程珠璣》第二章

1、題目:                                                                  ios

A題:給定一個最多包含40億個隨機排列的32位整數的順序文件,找出一個不在文件中的32位整數。在文件中至少存在這樣一個數?ide

           一、若是有足夠的內存,如何處理?函數

           二、若是內存不足,僅能夠用文件來進行處理,如何處理?spa

答:.net

1 採用位圖,表示全部的32位整數,約要512M空間code

內存不足,能夠採用以下思想:blog

                      按最高位分爲兩段,沒有出現的那個數,確定在比較小的段裏面。排序

                      若是比較少的段最高位爲1,那麼缺乏的那個數的最高位也爲1.遞歸

                      若是比較少的段最高位爲0,那麼少的那個數的最高位也是0.內存

                      依次按以上方法去處理每一個位。

B題:字符串循環移位好比abcdef 左移三位,則變成defabc

答:

解法一:旋轉前i個字符,能夠預先將這i個字符拷貝到輔助空間,剩下的n-i向前移動i個位置,填充剩餘的i個空間。

解法二:用遞歸函數每次向前移位一個,遞歸i次。

解法三:

#include<stdlib.h>
#include<stdio.h>
#include<string.h>
static void _rever(char *strseq,int n)
{
    int i = 0,j = n-1;
    char t;
    while(i < j)
    {
        t = strseq[i]; strseq[i] = strseq[j];strseq[j] = t;
        i++;j--;
    }
}
char* rever(char *strseq,int i,int len)
{
    if(strseq == NULL && i > len )
        return NULL;
    if(i <= 0 || i == len) return strseq;
    _rever(strseq,len);
    _rever(strseq,i);
    _rever(strseq+i,len-i);
    return strseq;
}
void main()
{
    char strseq[100];
    scanf("%s",strseq);
    char *result = NULL;
    printf("%d \n",strlen(strseq));
    result = rever(strseq,3,strlen(strseq));
    if(result)
        printf("%s \n",result);
    
    system("pause");
}
View Code

 當i > n時,咱們還能夠的處理方式是 i = i % n;

 解法四:

#include <iostream>
using namespace std;

static void swap(char &a,char &b)
{
    char t = a;
    a = b;
    b = t;
}
void doRotate(string &str,int n,int m,int start,int end,bool flag)
{
    if(m == 0 || start == end)
        return;
    //標記爲true,表示向右旋轉前m個字符
    if(flag == true)
    {
        int i = start;
        int j = start + m;
        int step = n - (n%m + m);
        int k = 0;
        for( ;k < step;k++,i++,j++)
            swap(str[i],str[j]);
        doRotate(str,n-k,n%m,i,end,false);

    }else
    {
        int i = end;
        int j = end - m;
        int step = n - (n%m + m);
        int k = 0;
        for(;k < step;k++,i--,j--)
            swap(str[i],str[j]);
        doRotate(str,n-k,n%m,start,i,true);
    }
};

void main()
{
    
    string str = "abc12345";
    cout<<str<<endl;
    doRotate(str,str.length(),3,0,str.length()-1,true);
    cout<<str<<endl;
    system("pause");
}
View Code

 

C題:給定一個單詞集合,找出能夠相互轉換的集合。好比abc bca cba均可以相互轉換。(變位詞)

答:使每一個變位詞類有一個惟一的標識,而後對標識進行排序,即集中具備相同標識的單詞。

/*C++實現*/
#include <iostream>
#include <string>
#include <vector>
#include <map>
#include<algorithm>

using namespace std;

multimap<string,string> doAnagrams(const vector<string> &words)
{
    multimap<string,string>  keywords;
    string word;
    vector<string>::const_iterator viter;
    for(viter = words.begin();viter != words.end();viter++)
    {
        word = *viter;
        sort(word.begin(),word.end());
        keywords.insert(make_pair(word,*viter));
    }
    return keywords;
}
void main()
{
    vector<string> words;
    string word;
    while(cin>>word)
        words.push_back(word);
    
    multimap<string,string> multiwords;
    multiwords = doAnagrams(words);
    typedef multimap<string,string>::const_iterator multiiter_t;
    multiiter_t multiiter;
    //遍歷multimap,與map的遍歷有較大不一樣
    //注意multimap的底層實現,在鍵相同時,同樣在紅黑樹中進行插入操做
    for(multiiter = multiwords.begin();multiiter != multiwords.end();)
    {
        //equal_range返回一對迭代器,分別指向該鍵所對應的值區間[  )
        pair<multiiter_t,multiiter_t> pos = multiwords.equal_range(multiiter->first);
        while(pos.first != pos.second)
        {
            cout<<pos.first->second<<" ";
            ++pos.first;
            multiiter++;
        }
        cout<<endl;
    }
    system("pause");
}
View Code

不用multimap使用map<string,vector<string> >也是一個方案存儲相同的變位詞。

2、習題:                                                                  

習題2-1:

在能夠進行預處理的狀況下,計算出每一個單詞的標識,並排序。在查詢時用二分搜索

習題2-2:

方法一:二分搜索經過遞歸搜索包含半數以上整數的子區間來查找至少出現兩次的單詞。由於4300000000  > 2^32,因此一定存在重複的整數,搜索範圍從[0, 2^32)開始,中間值mid爲2^31,若區間[0, 2^31)內的整數個數大於2^31個,則調整搜索區間爲[0, 2^31),反之則調整搜索區間爲[2^31, 2^32),而後再對整個文件再遍歷一遍,直到獲得最後的結果。這樣一共會有log2(n)次的搜索,每次遍歷n個整數(每次都是徹底遍歷),整體的複雜度爲o(nlog2(n))。

http://blog.csdn.net/silenough/article/details/7040028

方法二:二叉搜索,每讀取必定數據後,先判斷首位數據加上數據長度是否等於尾部數據,若是相等,繼續讀取文件,若是不等,進行搜索,比較中位值與尾部數據+首位數據。若是中位值大,則繼續在尾部二分查找,若是中位值小,則繼續頭部二分查找,若是相等,那麼這個數就是重複的數。

http://blog.csdn.net/qjzl2008/article/details/7707598】答案來自該連接可是沒看懂,誰看懂了教我啊(*^__^*) 嘻嘻……

習題2-3:

海豚法(說明:http://blog.csdn.net/zhoushuai520/article/details/7703368):

#include <iostream>
#include <assert.h>
#include<string>
using namespace std;


static int gcd(int n,int m)
{
    assert(n > m);
    return m > 0 ? gcd(m,n%m) : n;    
}
static int gcd2(int i,int j)
{
    while(i != j)
    {
        if(i > j)
            i -= j;
        else
            j -= i;
    }
    return i;
}
void doRotate(string &str,int rotatedist,int len)
{
    int i,j;
    char t;
    int igcd = gcd(len,rotatedist);
    for(i = 0;i < igcd;i++)
    {
        t = str[i];
        j = i;
        for( ; ; )
        {
            int k = j + rotatedist;
            k %= len;
            if(k == i)
                break;
            str[j] = str[k];
            j = k;
        }
        str[j] = t;
    }
}

void main()
{
    
    string str = "abc12345";
    cout<<str<<endl;
    doRotate(str,3,8);
    cout<<str<<endl;

    cout<<gcd(8,3)<<endl;
    cout<<gcd2(8,3)<<endl;

    system("pause");
}
View Code

習題2-9:待解

 

3、總結:                                                                  

1 二分搜索

2 排序在很是多的問題中是必須作的前期準備工做,可是對於問題C,排序並不能合理的解決問題,可是給每條記錄添加一個額外的鍵(標識),並按照這些鍵進行排序,巧妙的解決了問題。

3 標識:當使用等價關係來定義類時,定義一種標識使得類中的每一項都具備相同的標識,而該類之外的其餘項則沒有該標識。

 

 參考:                                                                       

http://blog.csdn.net/zhoushuai520/article/details/7703368 

http://blog.csdn.net/tianshuai1111/article/details/7555563 

相關文章
相關標籤/搜索