劍指OfferC++_31-40

31、整數中1出現的次數(從1到n整數中1出現的次數)

很快的求出任意非負整數區間中1出現的次數(從1 到 n 中1出現的次數)。

解題思路:

使用歸納的方法:
對於個位數,我們可以發現,每10個數就會在各位出現一個1.如0-22裏所有的數,只有1,11,21三個數的個位有1.所以對於22以內的數,,個位出現1一共3次。計算方法可以爲: n/10 * 1+(n%10!=0 ? 1 : 0)
對於十位數,我們發現,每100個數,出現10次,10-19.
設k = n % 100,即爲不完整階梯段的數字   歸納式爲:(n / 100) * 10 + (if(k > 19) 10 else if(k < 10) 0 else k - 10 + 1)  每100個數有10個數在十位上有1.餘數如果大於19.那麼又10個數,否則是k-10+1個(如:17-19+1=18)
對於百位數同理 設k = n % 1000 歸納式爲:(n / 1000) * 100 + (if(k >199) 100 else if(k < 100) 0 else k - 100 + 1
對於i位,我們設定i=1/10/100  設k = n % (i * 10) 有count(i) = (n / (i * 10)) * i + (if(k > i * 2 - 1) i else if(k < i) 0 else k - i + 1) ,將count求和就是結果sum1 = sum(count(i)),i = Math.pow(10, j), 0<=j<=log10(n)
把後半段簡化,我們不去計算i * 2 - 1了,我們只需保證k - i + 1在[0, i]區間內就行了,最後後半段可以寫成這樣  min(max((n mod (i*10))−i+1,0),i)

int NumberOf1Between1AndN_Solution(int n)
    { int sum=0;
        for(int i=1;i<=n;i=10*i)  //  0<=j<=log10(n)
            sum+=(n/(i*10))*i+min(max(0,(n%(i*10))-i+1),i);
        return sum;
    }

32、把數組排成最小的數

輸入一個正整數數組,把數組裏所有數字拼接起來排成一個數,打印能拼接出的所有數字中最小的一個。例如輸入數組{3,32,321},則打印出這三個數字能排成的最小數字爲321323。

解題思路:明顯是重載的題,難點在於如何重載,很可能覺得是return a<b 但不是 應該 return a+b<b+a

static bool cmp(int a,int b)//注意!一定要使用static變量!!!!!
{   string aa,bb;
    aa=to_string(a);
    bb=to_string(b);
    return (aa+bb)<(bb+aa);
}

string PrintMinNumber(vector<int> numbers) {
    string jj="";
    sort(numbers.begin(),numbers.end(),cmp);
    for(int i=0;i<numbers.size();i++)
        jj+=to_string(numbers[i]);
    return jj;
    }

33、醜數

把只包含質因子2、3和5的數稱作醜數(Ugly Number)。例如6、8都是醜數,但14不是,因爲它包含質因子7。 習慣上我們把1當做是第一個醜數。求按從小到大的順序的第N個醜數。

解題思路:

一個醜數一定由另一個醜數乘以2或者乘以3或者乘以5得到。那麼我們從1開始乘以2,3,5,就得到2,3,5三個醜數,在從這三個醜數出發乘以2,3,5就得到4,6,10,6,9,15,10,15,25九個醜數,我們發現這種方法會得到重複的醜數,而且我們題目要求第N個醜數,這樣的方法得到的醜數也是無序的。那麼我們可以維護4個隊列,其中3個序列是第一個序列的子序列,用索引來表示:
一個隊列存當前的醜數序列;另外三序列存的是分別是2的倍數的醜數,3的倍數的醜數,5的倍數的醜數。這三個序列之間存在重複。但是三個序列單獨是遞增的。x,y,z相當於存的是在2/3/5隊列裏,現在第N個醜數是乘了幾個。
而要判斷當前ret[i]是多少,其實就是找3個隊列裏的最小值。而最小值,其實就是ret[x]*2因爲上一個從2這裏面拿走的是ret[x],而再從2這裏面拿走一定是ret[x+1]=ret[x]*2.而當前ret[i]*2也在2這個隊列裏,可是不是最小的。也就是序列1是遞增順序的。每次Push進序列1裏的數我們會把他的2/3/5倍數分別放進對應序列裏。

1)醜數數組: 1

 乘以2的隊列:2
乘以3的隊列:3
乘以5的隊列:5
選擇三個隊列頭最小的數2加入醜數數組,同時將該最小的數乘以2,3,5放入三個隊列;
(2)醜數數組:1,2
乘以2的隊列:4
乘以3的隊列:3,6
乘以5的隊列:5,10
選擇三個隊列頭最小的數3加入醜數數組,同時將該最小的數乘以2,3,5放入三個隊列;
(3)醜數數組:1,2,3
乘以2的隊列:4,6
乘以3的隊列:6,9
乘以5的隊列:5,10,15
選擇三個隊列頭裏最小的數4加入醜數數組,同時將該最小的數乘以2,3,5放入三個隊列;
(4)醜數數組:1,2,3,4
乘以2的隊列:6,8
乘以3的隊列:6,9,12
乘以5的隊列:5,10,15,20
選擇三個隊列頭裏最小的數5加入醜數數組,同時將該最小的數乘以2,3,5放入三個隊列;
(5)醜數數組:1,2,3,4,5
乘以2的隊列:6,8,10,
乘以3的隊列:6,9,12,15
乘以5的隊列:10,15,20,25
選擇三個隊列頭裏最小的數6加入醜數數組,但我們發現,有兩個隊列頭都爲6,所以我們彈出兩個隊列頭,同時將12,18,30放入三個隊列;

    int GetUglyNumber_Solution(int index) {
        if(index<7)  return index; //1-6都是醜數
        vector<int> res;
        int x=0,y=0,z=0;
        res.push_back(1);
        for(int i=1;i<index;i++)
        {
            res.push_back(min(res[x]*2,min(res[y]*3,res[z]*5)));
            if(res[i]==res[x]*2) x++;
            if(res[i]==res[y]*3) y++;
            if(res[i]==res[z]*5) z++;
//這三行是爲了防止重複。這個數可能同時是,某另一個數的另一個倍數,如果不++,會重複。並且一定是下一個就是這個重複的數的時候才++
        }
        return res[index-1];
    }

 

31-25知識點總結:

1、vector<int> k(10); 開闢10個int的空間

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