个性化阅读
专注于IT技术分析

C++找出第n个丑数两种方式:简单方式和动态规划

丑数是只有2、3或5是质因数的数字,序列1、2、3、4、5、6、8、9、10、12、15……显示前11个丑数,按照惯例,包含1。

给定一个数字n,任务是找出第n个丑数。

例子:

输入  : n = 7
输出 : 8

输入  : n = 10
输出 : 12

输入  : n = 15
输出 : 24

输入  : n = 150
输出 : 5832

方法1(简单的)

循环所有正整数,直到丑数计数小于n,如果一个整数是丑数,则增加丑数计数。

要检查一个数是不是丑的,把这个数除以2、3和5的最大可除幂,如果这个数变成1,那么它就是丑数,否则不是。

例如,让我们看看如何检查300是否是丑数,2的最大可除指数是4,300除以4等于75。3的最大可除数是3,75除以3等于25。5的最大可除指数是25,25除以25等于1。因为最后得到1,所以300是个丑数。

C++实现:

// CPP程序找出第n个丑数 
# include<stdio.h> 
# include<stdlib.h> 
  
/*这个函数把a除以b的最大可除幂*/
int maxDivide(int a, int b) 
{ 
  while (a%b == 0) 
   a = a/b;  
  return a; 
}     
  
/* 函数的作用是检查一个数字是否是丑数 */
int isUgly(int no) 
{ 
  no = maxDivide(no, 2); 
  no = maxDivide(no, 3); 
  no = maxDivide(no, 5); 
    
  return (no == 1)? 1 : 0; 
}     
  
/* 函数可以得到第n个丑数*/
int getNthUglyNo(int n) 
{ 
  int i = 1;  
  int count = 1;   /* 丑数总数 */ 
  
  /*检查所有整数,直到丑数总数变成n */ 
  while (n > count) 
  { 
    i++;       
    if (isUgly(i)) 
      count++;  
  } 
  return i; 
} 
  
/* 测试程序 */
int main() 
{ 
    unsigned no = getNthUglyNo(150); 
    printf("第150个丑数是 %d ",  no); 
    getchar(); 
    return 0; 
} 

这种方法的时间效率不高,因为它检查所有整数,直到丑数计数变成n,但是这种方法的空间复杂度是O(1)。

方法2(使用动态规划)

这是一个有额外空间O(n)的高效时间解决方案,假设丑数序列是1、2、3、4、5、6、8、9、10、12、15……

因为每个数只能被2、3、5整除,所以看序列的一种方法是把序列分成以下三组:

(1) 1×2,2×2,3×2,4×2,5×2,…

(2) 1×3,2×3,3×3,4×3,5×3,…

(3) 1×5,2×5,3×5,4×5,5×5,…

我们可以发现每个子序列都是丑序列本身(1,2,3,4,5,…)乘以2,3,5。然后利用类似于归并排序的方法,求出三个子序列中所有的丑数。我们每走一步都选择最小的一步,然后再往前走一步。

1、为一个丑数声明一个数组:ugly[n]
2、初始化第一个丑数: ugly[0] = 1
3、初始化三个数组索引变量i2, i3, i5指向
丑数组的第一个元素:
i2 = i3 = i5 =0;
4、初始化3个选项为下一个丑数:
next_mulitple_of_2 =ugly(i2) * 2;
next_mulitple_of_3 =ugly(i3) * 3
next_mulitple_of_5 =ugly(i5) * 5;
5、现在循环填入所有丑数直到150:
For (i = 1; i < 150; i++ ) 
{
    /* 这些小步骤没有经过优化以获得良好的可读性,可在实现中进行优化 */
    next_ugly_no  = Min(next_mulitple_of_2,
                        next_mulitple_of_3,
                        next_mulitple_of_5); 

    ugly[i] =  next_ugly_no       

    if (next_ugly_no  == next_mulitple_of_2) 
    {             
        i2 = i2 + 1;        
        next_mulitple_of_2 = ugly[i2]*2;
    } 
    if (next_ugly_no  == next_mulitple_of_3) 
    {             
        i3 = i3 + 1;        
        next_mulitple_of_3 = ugly[i3]*3;
     }            
     if (next_ugly_no  == next_mulitple_of_5)
     {    
        i5 = i5 + 1;        
        next_mulitple_of_5 = ugly[i5]*5;
     } 
     
}/* 循环结束 */ 
6.返回下一个丑数

例子:

让我们看看它是如何工作的

初始化
   ugly[] =  | 1 |
   i2 =  i3 = i5 = 0;

第1次迭代
   ugly[1] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
            = Min(2, 3, 5)
            = 2
   ugly[] =  | 1 | 2 |
   i2 = 1,  i3 = i5 = 0  (i2增加了) 

第二次迭代
    ugly[2] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
             = Min(4, 3, 5)
             = 3
    ugly[] =  | 1 | 2 | 3 |
    i2 = 1,  i3 =  1, i5 = 0  (i3增加了 ) 

第三次迭代
    ugly[3] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
             = Min(4, 6, 5)
             = 4
    ugly[] =  | 1 | 2 | 3 |  4 |
    i2 = 2,  i3 =  1, i5 = 0  (i2增加了)

第四次迭代
    ugly[4] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
              = Min(6, 6, 5)
              = 5
    ugly[] =  | 1 | 2 | 3 |  4 | 5 |
    i2 = 2,  i3 =  1, i5 = 1  (i5增加了 )

第五次迭代
    ugly[4] = Min(ugly[i2]*2, ugly[i3]*3, ugly[i5]*5)
              = Min(6, 6, 10)
              = 6
    ugly[] =  | 1 | 2 | 3 |  4 | 5 | 6 |
    i2 = 3,  i3 =  2, i5 = 1  (i2和i3增加了)

继续同样的方式直到 I < 150

动态规划C++实现:

// c++程序找出第n个丑数 
# include<bits/stdc++.h> 
using namespace std; 
  
/* 函数可以得到第n个丑数*/
unsigned getNthUglyNo(unsigned n) 
{ 
    unsigned ugly[n]; // 存储丑数 
    unsigned i2 = 0, i3 = 0, i5 = 0; 
    unsigned next_multiple_of_2 = 2; 
    unsigned next_multiple_of_3 = 3; 
    unsigned next_multiple_of_5 = 5; 
    unsigned next_ugly_no = 1; 
  
    ugly[0] = 1; 
    for (int i=1; i<n; i++) 
    { 
       next_ugly_no = min(next_multiple_of_2, 
                           min(next_multiple_of_3, 
                               next_multiple_of_5)); 
       ugly[i] = next_ugly_no; 
       if (next_ugly_no == next_multiple_of_2) 
       { 
           i2 = i2+1; 
           next_multiple_of_2 = ugly[i2]*2; 
       } 
       if (next_ugly_no == next_multiple_of_3) 
       { 
           i3 = i3+1; 
           next_multiple_of_3 = ugly[i3]*3; 
       } 
       if (next_ugly_no == next_multiple_of_5) 
       { 
           i5 = i5+1; 
           next_multiple_of_5 = ugly[i5]*5; 
       } 
    } /*循环结束 (i=1; i<n; i++) */
    return next_ugly_no; 
} 
  
/* 驱动程序测试以上功能 */
int main() 
{ 
    int n = 150; 
    cout << getNthUglyNo(n); 
    return 0; 
} 

时间复杂度:O(n)

辅助空间:O(n)

赞(0)
未经允许不得转载:srcmini » C++找出第n个丑数两种方式:简单方式和动态规划

评论 抢沙发

评论前必须登录!