題目:把只包含質因子二、3和5的數稱做醜數(Ugly Number)。例如六、8都是醜數,但14不是,由於它包含質因子7。 習慣上咱們把1當作是第一個醜數。求按從小到大的順序的第N個醜數。c++
動態規劃方法,假設這個數爲 n
, 若是n
是醜數,只有三種可能:算法
n
是能整除2
,即 n % 2 == 0
,且 n/2
是醜數。n % 3 == 0
且 n/3
是醜數。n % 5 == 0
且 n / 5
是醜數。三種可能只要知足其中一種,就能夠確認是醜數了。測試
使用dp[i]
表示i
是否爲醜數。因爲不知道第index
個醜數究竟是第幾個數,因此使用vector
保存。優化
class Solution { public: int GetUglyNumber_Solution(int index) { if(index <= 6) // 1...6都是醜數 return index; vector<bool> dp(7,true); int count = 6; int i = 7; //從數字7開始判斷 while(1){ dp.push_back(false); // 默認第i個不是醜數 if(i%2 == 0 && dp[i/2]) { dp[i] = true; count++; } else if(i%3 == 0 && dp[i/3]) { dp[i] = true; count++; } else if(i%5 == 0 && dp[i/5]) { dp[i] = true; count ++; } if(count == index) break; i++; // 判斷下一個數是否是醜數 } return i; } };
用小數據測試了一下,看起來挺正確的,可是提交發現,過不了,緣由是內存超限:您的程序使用了超過限制的內存 。翻譯
反思一下,當求第1500個醜數的時候,確實內存太大了。因而想辦法優化了下內存。c++11
爲了優化內存,只能想辦法不存儲全部的數了,而是用一個集合存儲全部的醜數,若是要求第n
個醜數,也就最多使用n
個存儲空間。而判斷一個數是否是醜數的方法就是判斷該數在不在這個集合中。code
爲了查找的快速,選擇使用c++11
的unordered_set
容器。索引
class Solution { public: int GetUglyNumber_Solution(int index) { if( index == 1) return 1; unordered_set <int> mp; mp.insert(1); // 1是醜數 int count = 1; int i = 2; while(1){ // 能整除2, 且 i/2在醜數集合中 if(i%2 == 0 && mp.find(i/2)!=mp.end()) { mp.insert(i); //是醜數。添加到集合中 count++; } else if(i%3==0 && mp.find(i/3)!=mp.end()) { mp.insert(i); count++; } else if(i%5==0 && mp.find(i/5)!=mp.end()) { mp.insert(i); count ++; } if(count == index) break; i++; } return i; } };
內存確實優化了,卻仍是沒過!!! 此次是 運行超時:您的程序未能在規定時間內運行結束,請檢查是否循環有錯或算法複雜度過大。想了半天也沒能再優化下去,只得看看參考答案了。內存
其實仍是一樣的思路,不過換了個角度。若是已知了n
個醜數,第n+1
個醜數必然是前面的某個醜數乘以2,或者乘以3,或者乘以5。至因而誰,就是都嘗試一下,取最小。get
舉個栗子:
如今已知6個醜數 1 2 3 4 5 6, 求第7個醜數。
能夠翻譯成:假設dp[i]
表示第i
個醜數的數值,已知醜數的個數爲count=6
,且前6個醜數 dp[1]=1;dp[2]=2;dp[3]=3;dp[4]=4;dp[5]=5;dp[6]=6;
求dp[7]
。
則dp[7]
可能有三種狀況:
i=1
開始按順序求v = dp[i]*2
,當v>dp[6]
,能夠中止,則第4
個醜數乘2
獲得的8
多是第7
個醜數。i=1
開始按順序求v = dp[i]*3
,當v>dp[6]
,能夠中止,則第3
個醜數乘3
獲得的9
多是第7
個醜數。i=1
開始按順序求v = dp[i]*5
,當v>dp[6]
,能夠中止,則第3
個醜數乘5
獲得的10
多是第7
個醜數。取三種狀況的最小值,獲得8
,就是第7
個醜數,即dp[7] = 8
。
依此類推,能夠求得第8個醜數。
注意,這裏有個小優化,按順序搜索的時候並不須要每次都從1
開始,只須要從上次搜索的結束點繼續搜索就好了。
例如求dp[8]
,一樣有三種狀況:
i=4
開始按順序求v = dp[i]*2
,當v>dp[7]
,能夠中止,則第5
個醜數乘2
獲得的10
多是第8
個醜數。i=3
開始按順序求v = dp[i]*3
,當v>dp[7]
,能夠中止,則第3
個醜數乘3
獲得的9
多是第8
個醜數。i=2
開始按順序求v = dp[i]*5
,當v>dp[7]
,能夠中止,則第2
個醜數乘5
獲得的10
多是第8
個醜數。取三種狀況的最小值,獲得10
,即dp[8] = 9
。
class Solution { public: int GetUglyNumber_Solution(int index) { if(index <= 6) return index; int start1=1, start2=1, start3 = 1; //搜索起點 vector<int> dp(index+1, 0); for(int i=1;i<=6;i++) dp[i] = i; int count = 6; //當前醜數的最大索引 while(count < index){ //使用*2第一次超過已知的最大丑數 dp[count] 的 for(int i=start1;i<=count;i++){ if(2*dp[i] > dp[count]){ start1 = i; //記錄下這個索引,下次從這裏開始 break; // 找到第一個大於就中止搜索 } } for(int i=start2;i<=count;i++){ if(3*dp[i] > dp[count]){ start2 = i; break; } } for(int i=start3;i<=count;i++){ if(5*dp[i] > dp[count]){ start3 = i; break; } } count++; dp[count] = min(dp[start1]*2, dp[start2]*3); dp[count] = min(dp[count], dp[start3]*5); } return dp[index]; } };
本身想出來的方法比較複雜,第1500個醜數是859963392,那麼,個人方法就得判斷859963392前面的全部數究竟是不是醜數,時間複雜度很高。
而正確解法,只用遍歷前面的1499個醜數,就能夠計算出第1500個醜數的值!