1. 程式人生 > >劍指Offer(面試題33~34)

劍指Offer(面試題33~34)

面試題33:把陣列排成最小的數

題目:輸入一個正整數陣列,把數組裡所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個。例如輸入陣列{3,32,321},則打印出這3個數字能排成的最小數字321323。
分析:一個非常直觀的解決大數問題的方法就是把數字轉換成字串。另外,由於把數字m和n拼接起來得到mn和nm,它們的位數肯定是相同的,因此比較它們的大小隻需要按照字串大小的比較規則就可以了。

const int g_MaxNumberLength = 10;

char* g_StrCombine1 = new char[g_MaxNumberLength * 2 +1];
char
* g_StrCombine2 = new char[g_MaxNumberLength * 2 +1]; void PrintMinNumber(int* numbers, int length) { if(numbers == NULL || length <= 0) return; char** strNumbers = (char**)(new int[length]); for(int i = 0;i<length ;++i) { strNumbers[i] = new char[g_MaxNumberLength + 1
]; sprintf(strNumbers[i],"%d",numbers[i]); } qsort(strNumbers,length,sizeof(char*),compare); for(int i =0;i < length;++i) printf("%s",strNumbers[i]); printf("\n"); for(int i = 0;i < length;++i) delete[] strNumbers[i]; delete[] strNumbers; } int
compare(const void* strNumber1,const void* strNumber2) { strcpy(g_StrCombine1,*(const char**)strNumber1); strcat(g_StrCombine1,*(const char**)strNumber2); strcpy(g_StrCombine2,*(const char**)strNumber2); strcat(g_StrCombine2,*(const char**)strNumber1); return strcmp(g_StrCombine1,g_StrCombine2); }

在上述程式碼中,我們先把陣列中的整數轉換成字串,在函式compare中定義比較規則,並根據該規則用規則用庫函式qsort排序。最後把排好序的陣列中的數字依次打印出來,就是該陣列中數字能拼接出來的最小數字。這種思路的時間複雜度和qsort的時間複雜度相同,也就是O(nlogn),這比用n!的時間求出所有排列的思路要好很多。

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

逐個判斷每個整數是不是醜數的解法,直觀但不夠高效

//判斷一個數是不是醜數
bool IsUgly(int number)
{
    while(number % 2 == 0)
        number /= 2;
    while(number % 3 == 0)
        number /= 3;
    while(number % 5 == 0)
        number /= 5;
    return (number == 1)?true:false;
}

//判斷每一個整數是不是醜數
int GetUglyNumber(int index)
{
    if(index <= 0)
        return 0;

    int number = 0;
    int uglyFound = 0;
    while(uglyFound < index)
    {
        ++number;

        if(IsUgly(number))
        {
            ++uglyFound;
        }
    }
    return number;
}

方法二:建立陣列儲存已經找到的醜數,用空間換時間的解法
根據醜數的定義,醜數應該是另一個醜數乘以2、3或者5的結果(1除外)。因此我們可以建立一個數組,裡面的數字是排好序的醜數,每一個醜數都是前面的醜數乘以2,3或者5得到的。
這種思路的關鍵在於怎樣確保數組裡面的醜數是排好序的。假設陣列中已經有若干個醜數排好序後存放在陣列中,並且把已有最大的醜數記作M,我們接下來分析如何生成下一個醜數。該醜數肯定是前面某一個醜數乘以2、3或者5的結果,所以首先考慮把已有的每個醜數乘以2。在乘以2的時候,能得到若干個小於或等於M的結果。由於是按照順序生成的,小於或者等於M肯定已經在陣列中了,我們不需要再次考慮;還會得到若干個大於M的結果,但我們只需要第一個大於M的結果,因為我們希望醜數是從小到大的順序生成的,其他更大的結果以後再說。我們把得到的第一個乘以2後大於M的結果記為M2。同樣,我們把已有的每一個醜數乘以3或5,能得到第一個大於M的結果M3和M5。那麼下一個醜數應該是M2、M3和M5這3個數的最小者。

int GetUglyNumber_Solution2(int index)
{
    if(index <= 0)
        return 0;

    int *pUglyNumbers = new int[index];
    pUglyNumbers[0]=1;
    int nextUglyIndex = 1;

    int *pMultiply2 = pUglyNumbers;
    int *pMultiply3 = pUglyNumbers;
    int *pMultiply5 = pUglyNumbers;

    while(nextUglyIndex < index)
    {
        int min =Min(*pMultiply2 * 2, *pMultiply3 * 3,*pMultiply5 * 5);
        pUglyNumbers[nextUglyIndex] = min;

        while(*pMultiply2 * 2 <= pUglyNumbers[nextUglyIndex])
            ++pMultiply2;
        while(*pMultiply3 * 3 <= pUglyNumbers[nextUglyIndex])
            ++pMultiply3;
        while(*pMultiply5 * 5 <= pUglyNumbers[nextUglyIndex])
            ++pMultiply5;

        ++nextUglyIndex;
    }

    int ugly = pUglyNumbers[nextUglyIndex - 1];
    delete[] pUglyNumbers;
    return ugly;
}

int Min(int number1,int number2,int number3)
{
    int min = (number1 < number2) ? number1 :number2;
    min = (min < number3) ? min:number3;
    return min;
}

和第一種思路相比,第二種思路不需要在非醜數的整數上做任何計算,因此時間效率有明顯提升。但也需要指出,第二種演算法由於需要儲存已經生成的醜數,因此需要一個數組,從而增加了空間消耗。如果是求第1500個醜數,將建立一個容納1500個醜數的陣列,這個陣列佔記憶體6KB。而第一種思路沒有這樣的記憶體開銷。總的來說,第二種思路相當於用較小的空間消耗換取了時間效率的提升。

參考資料《劍指Offer》

相關推薦

Offer試題33~34

面試題33:把陣列排成最小的數 題目:輸入一個正整數陣列,把數組裡所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個。例如輸入陣列{3,32,321},則打印出這3個數字能排成的最小數字321323。 分析:一個非常直觀的解決大數問題的方法就是把

Offer試題43~45

面試題43:n個骰子的點數 題目:把n個骰子仍在地上,所有骰子朝上一面的點數之和為s。輸入n,打印出s的所有可能的值出現的概念。 解法一:基於遞迴求骰子點數,時間效率不夠高 要想求出n個骰子的點數和,可以先把n個骰子分成兩堆:第一堆只有一個,另一堆有n-1

Offer試題29~30

面試題29:陣列中出現次數超過一半的數字 題目:陣列中有一個數字出現的次數超過陣列長度的一半,請找出這個數字。例如輸入一個長度為9的陣列{1,2,3,2,2,2,5,4,2}。由於數字2在陣列中出現了5次,超過陣列長度的一半,因此輸出2。 bool g_

offer試題--從尾到頭列印單鏈表

程式碼如下(遞迴實現): /** * public class ListNode { * int val; * ListNode next = null; * * ListNode(int val) { *

offer試題33:二叉搜尋樹的後序遍歷序列

題目:輸入一個整數陣列,判斷該陣列是不是某二叉搜尋樹的後序遍歷的結果。如果是則返回 true,否則返回 false。假設輸入的陣列的任意兩個數字都互不相同。 牛客網連結: https://blog.csdn.net/jsqfengbao/article/details/4

offer試題29:順時針列印矩陣

/* * 順時針列印矩陣 * 注意矩陣的維度 */ #include<iostream> using namespace std; //只要當前的起始行號小於終止行號或者起始列號小於終止列號,就可以繼續順時針列印 // 但是隨著順勢怎列印,剩下

offer試題39:陣列中出現次數超過陣列長度一半的數字

方法1: 從數學角度看,所求的數一定的原陣列(有序化)的中位數,那麼,我們可以通過對陣列排序,這樣得到的時間複雜度是O(nlogn)O(nlogn)。而如果能夠優化到在O(n)O(n)的時間內找到,就更好了。基於快速排序的partition階段,如果我們可以

offer試題35:複雜連結串列的複製

/* * 複雜連結串列的複製 * 複雜連結串列是指,結點的指標可能不規則地指向另一個結點 */ #include<iostream> using namespace std; struct ComplexListNode { int v

offer試題38:字串的排列

分析: 求字串中所有字元的全排列,實際上按照全排列的計算公式來理解,第一步是求出所有可能出現在第一個位置的字元,即用首個字元分別後餘下的字元交換;第二步是固定首個字元,餘下字元組成的子串進行第一步的

offer試題21:根據給定條件劃分陣列

/* * 題目 * 輸入一個數組,實現一個函式來調整該陣列中數字的順序,使得所有奇數位於陣列的前半部分 * 偶數位於陣列的後半部分 * 同時考慮程式碼的可擴充套件行 */ #include <i

offer試題31:棧的壓入和彈出序列

/* * 給定兩個序列,一個是棧的壓入序列,一個是彈出序列,判斷彈出序列能否匹配壓入序列 */ #include<iostream> #include<stack> usi

offer試題39 二叉樹的深度java

設計模式 博客 rgs 歷史 存在 復制 pri 取值 今天 摘要: 今天翻到了《劍指offer》面試題39,題目二中的解法二是在函數的參數列表中通過指針的方式進行傳值,而java是沒有指針的,所以函數要進行改造。然而我翻了下別人的java版本(我就想看看有什麽高大上的改造

offer試題43 n個骰子的點數 java

r+ nal ret 次循環 分而治之 源碼 ava 面試 ble 引言:寫這篇文章的初衷只是想做個筆記,因為這道題代碼量有點大,有點抽象,而書上並沒有詳細的註釋。為了加深印象和便於下次復習,做個記錄。 原題:把n個骰子扔到地上,所有骰子朝上一面的點數之後為s. 輸入n,打

offer試題571:和為S的數字

題目 輸入一個遞增排序的陣列和一個數字S,在陣列中查詢兩個數,是的他們的和正好是S,如果有多對數字的和等於S,輸出兩個數的乘積最小的。 ps: 對應每個測試案例,輸出兩個數,小的先輸出。 思路

offer試題34:二叉樹中和為某一值的路徑

題目:輸入一顆二叉樹的跟節點和一個整數,打印出二叉樹中結點值的和為輸入整數的所有路徑。路徑定義為從樹的根結點開始往下一直到葉結點所經過的結點形成一條路徑。(注意: 在返回值的list中,陣列長度大的陣列靠前)  連結:https://www.nowcoder.com/qu

offer試題答案彙總Java版

面試題2:實現Singleton模式 (1)餓漢模式 public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){}

offer》- 試題3:陣列中重複的數字java實現

題目一:         在一個長度為n的數組裡的所有數字都在0到n-1的範圍內。 陣列中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出陣列中任意一個重複的數字。 例如,如果輸入長度為7的陣列{2,3,1,0,2,5,3},那麼對應的輸出是重複

offer試題322:分行從上到下列印二叉樹

完整程式碼地址 題目 從上到下按層列印二叉樹,同一層結點從左至右輸出。每一層輸出一行。 思路 用佇列來儲存要列印的節點。 同時我們需要兩個變數:一個變量表示在當前層中還沒有列印的節點數

offer試題二:單例模式的實現使用C++語言

#include <iostream> #include <iomanip> #include <cstdio> #include <cstdlib>

offer試題50:字元流中第一個只出現一次的字元【C++版本】

題目:字串中第一個只出現一次的字元。 在字串中找出第一個只出現一次的字元。如輸入"abaccdeff" "abaccdeff",則輸出′ b ′  ′b′。 解題思路: 1.使用雜湊表來記錄每個字元出現的次數,因為字元char char為8位,