題目簡析:題目要求找出第N個醜陋數,根據醜陋數的特點,醜陋數只有因數2 3 5,因此當N到達一定數字時,醜陋數的大小會非常大,因此這道題目需要思考一個時間複雜度比較低的算法。
我的第一個算法是,維護一個包含特殊數字的數組A,從num=1開始,假如這個num滿足以下條件,那麼就放到數組A裏,反之則放到醜陋數B中:
(1) num既不能被2,也不能被3、5整除
(2) 假如能被2、3、5三者其中之一整除,整除後的商的數字在數組A中
同時,爲了使得在數組A中的檢索時間複製度達到極限O(1),我令A中所有數都是0,但是檢測到滿足(1)(2)的數組時,採用A[num]=-1的方式儲存數組,即犧牲非常大的空間來換取時間。但是,最後提交時,估計空間過大,因此不能通過。
換言之,在已有的數組中進行檢索,要麼空間複雜度過高,要麼時間複雜度過高(即使是二分查找O(log) ),所以要拋棄檢索這個環節。因此第二個算法就是,讓數組ugly={1},每次選擇一個符合條件的最小丑陋數依次加入。
根據醜陋數的特點,我們可以歸納爲,一個新的醜陋數是從已有的醜陋數中之一與2,3,5相乘所得,因此我們每一輪從三者選擇一個最小值就行。
我們嘗試寫出三組醜陋數,分別是醜陋數序列(斜體數字) x 2 / 3 /5,如下
1*2 2*2 3*2 4*2 5*2……….
1*3 2*3 3*3 4*3 5*3……….
1*5 2*5 3*5 4*5 5*5……….
其中醜陋數序列爲 1 2 3 4 5 6 8 9 10 ……….
所以我們維護三個數組(或者隊列),每個數組分別有一個下標,每個下標表示這個數組的第幾個醜陋數,我們假設三個數組分別爲 g2,g3. g5 下標分別爲p2 p3 p5,以下我們模仿以下添加前幾個醜陋數,p2 p3 p5初始均爲0
第一輪:g2=0, g3=0, g5=0 àg2[p2]=2,g3[p3]=3, g5[P5]=5, 選取g2[p2]=2加入醜陋數,g2++
第二輪:g2=1, g3=0, g5=0 àg2[p2]=4,g3[p3]=3, g5[P5]=5, 選取g3[p3]=3加入醜陋數,g3++
第三輪:g2=1, g3=1, g5=0 àg2[p2]=4 g3[p3]=6,g5[P5]=5, 選取g2[p2]=4加入醜陋數,g2++
第四輪:g2=2, g3=1, g5=0 àg2[p2]=6, g3[p3]=6,g5[P5]=5, 選取g5[p5]=5加入醜陋數,g5++
第五輪:g2=2 g3=2, g5=21àg2[p2]=6, g3[p3]=6,g5[P5]=10, 選取g2[p2]=6加入醜陋數,g2++
補充的是,每組的醜陋數可以通過2*ugly[g2] 或者3*ugly[g3] 5*ugly[g5]獲取每組的下一個醜陋數,以及避免重複的醜陋數(比如2*3=6 和3*2=6),每一次添加醜陋數都要與上一個醜陋數對比,避免重複。
下面附上代碼
class Solution {
public:
int nthUglyNumber(int n) {
vector<int>group2={2};
vector<int>group3={3};
vector<int>group5={5};
int p2=0;
int p3=0;
int p5=0;
vector<int> ugly={1};
while(ugly.size()<=n){
int min_num=min_ele(group2[p2],min(group3[p3],group5[p5]));
if(ugly[ugly.size()-1]!=min_num)
ugly.push_back(min_num);
if(group2[p2]==min_num){
group2.push_back(2*ugly[++p2]);
}
else if(group3[p3]==min_num){
group3.push_back(3*ugly[++p3]);
}
else if(group5[p5]==min_num){
group5.push_back(5*ugly[++p5]);
}
}
return ugly[n-1];
}
int min_ele(int num1, int num2){
if(num1>num2)
return num2;
return num1;
}
};