算法題:尋找丑數
這是一道在訂閱的blog上看到的題目,覺得比較有意思,就動手做了一下。
何為丑數?
丑數(Ugly number)是指其因子中只包含2、3、5的整數,類如6、20、36都是丑數,而7、19、28則不是丑數。特別的:1是最小的丑數
題目:求從小大到的情況下第1500個丑數
解題過程:
- 原始解法:
從1開始,判斷當前數字是否丑數,不是則跳至下一個數字,是的話下標+1,如果下標增加到指定的數字,則此時對應的數字為要求的丑數。這個解法簡單粗暴,可以說毫無技術含量。因為每一個數都要與2、3、5輾轉相處,并且要遍歷答案之前的每一個自然數,時間復雜度太差。
- 改進解法:
實際上我們可以試圖跳過那些不是丑數的自然數,考慮一下丑數是怎么得來的:從1開始,12=2,13=3,1*5=5,然后從得到的2、3、5繼續乘以對應的2、3、5得到新的數字。這樣得到的數字都是丑數,因為不可能包含其他的因子。那么我們可以嘗試著來構造一個從小到大排列的數組,數組的長度就是我們需要的丑數序號。
接下來的問題是:如何保持數組在構造的過程中是有序的?
假設我們現在有了部分數組,要添加一個新的丑數。新的丑數必然是從已有的數中,分別乘2、3、5之后獲得,為保持有序我們只取其中最小的數字,因為這三個數字中間也許還有其他的丑數。實際上這樣存在不必要的計算:可能存在一些數字,乘以對應的2、3、5之后也比當前存在的最大丑數要小,而我們只需要乘以2、3、5之后比當前最大丑數大的那個數。所以,對應2、3、5這三個因子,我們應該保存三個位置,使得這三個位置上的數字乘以對應的2、3、5之后比當前最大丑數大。怎么找?很簡單,每次插入新的丑數之后,我們更新一下這三個數的位置就可以了。下一次就對這三個位置的數字分別乘以2、3、5,找出最小的插入數組。這樣我們就保證了數組有序,并盡量減少了不必要的計算。
C++代碼實現:
#include <iostream>
int get_min_number(int a, int b, int c)
{
int min_ab = std::min(a,b);
return std::min(min_ab, c);
}
int find_ugly_number(int target)
{
if (target < 1)
return 0;
int* array = new int[target];
array[0] = 1;
int *pos2 = array;
int *pos3 = array;
int *pos5 = array;
int index = 1;
while (index < target)
{
int cur_max = get_min_number(*pos2*2, *pos3*3,*pos5*5);
array[index++] = cur_max;
while (*pos2*2 <= cur_max)
pos2++;
while (*pos3*3 <= cur_max)
pos3++;
while (*pos5*5 <= cur_max)
pos5++;
}
int ret = array[target-1];
delete [] array;
return ret;
}
int main()
{
int index = 1500;
int result = find_ugly_number(index);
std::cout << result << std::endl;
return 0;
}
代碼也被我放在我Github的issues庫中,點擊查看