【数学】C022_丑数II(暴力枚举 | 优化 | 三重循环 |dp 思想)

一、题目描述

Write a program to find the n-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. 

Example:

Input: n = 10
Output: 12
Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers.
Note:  

1 is typically treated as an ugly number.
n does not exceed 1690.

二、题解

(1) 超级暴力

private boolean isUgly(int num) {
  while (num % 2 == 0)
    num >>= 1;
  while (num % 3 == 0)
    num /= 3;
  while (num % 5 == 0)
    num /= 5;
  return num == 1;
}

/**
 * 暴力求解 超时
 * @param n
 * @return
 */
public int nthUglyNumber1(int n) {
  int ans=1;
  for(int cnt = 1; cnt < n; cnt++) {
    ans++;
    if(isUgly(ans)) ++cnt;
  }
  return ans;
}

(2) 暴力优化

/**
 * 暴力优化
 * 为什么用TreeSet会超时??
 * 执行用时 353 ms 击败了 5% 的java用户
 * 内存消耗 36 MB 击败了 22.5% 的java用户
 * @param n
 * @return
 */
public int nthUglyNumber2(int n) {
  ArrayList<Integer> uglyNumList = new ArrayList<>();
  int[] factors = {2,3,5};
  uglyNumList.add(1);
  while (uglyNumList.size() < n) {
    int min = Integer.MAX_VALUE;
    for(int num : uglyNumList) {
      for(int f : factors) {
        int t = num * f;
        // 丑数 1074954240 与 5 相乘得到1079803904 会溢出,所以要增加一层判断t / num == f
        if(t < min && t > uglyNumList.get(uglyNumList.size()-1) && t / num == f)
          min = t;
      }
    }
    uglyNumList.add(min);
  }
  return uglyNumList.get(n-1);
}

(3) 三重循环暴力

/**
 * 优化暴力:去重,不难发现如果将一个丑数乘以2/3/5,得到的还是丑数,
 * 但这样计算可能会出现重复的结果。比如2*3=6,3*2=6
 *
 * 我们将丑数的每个循环都乘以2/3/5,这样得到的
 *
 * @param n
 * @return
 */
public int nthUglyNumber3(int n) {
  ArrayList<Integer> uglyNumList = new ArrayList<>(n);
  for (long i = 1; i < Integer.MAX_VALUE; i<<=1)
  for (long j = i; j < Integer.MAX_VALUE; j*=3)
  for (long k = j; k < Integer.MAX_VALUE; k*=5)
   uglyNumList.add((int)k);
  Collections.sort(uglyNumList);
  return uglyNumList.get(n-1);
}

(4) 动态规划思想

/**
 * @thought:利用动态规划思想
 * @date: 1/19/2020 9:38 PM
 * @Execution info:3ms 击败 98.57% 的j,MB 击败 % 的j
 * @Asymptotic Time Complexity:O()
 */
static int[] dp;  // 对于测试用例来说,它只会new 一个 Solution()才测试,静态变量的好处就是不用每次调用方法测试都初始化。
public int nthUglyNumber4(int n) {
  dp = new int[n+1];
  dp[0]=1;
  int i=0;
  int i2=0, i3=0, i5=0;
  while(i++ < n) {
    int next2 = dp[i2] * 2; // 得到下一个2的倍数的丑数
    int next3 = dp[i3] * 3; // 得到下一个3的倍数的丑数
    int next5 = dp[i5] * 5; // 得到下一个5的倍数的丑数
    int nextUN = Math.min(next2, Math.min(next3, next5)); // 取最小的丑数,是为了按序得出丑数。
    if(nextUN == next2) i2++; // 如果相等,则证明,i2指针指向的丑数已经被遍历过,则i2指针要向前走一步
    if(nextUN == next3) i3++; // i3也要比较的原因是:一开始虽然是i2先自增,到了后面会出现相同的丑数,可能i2在前面,i3在后面,如果i3走了一步,dp[i3]*3就有可能超过dp[i2]了
    if(nextUN == next5) i5++;
    dp[i]=nextUN;
  }
  return dp[n-1];
}
  • 时间复杂度: O ( n ) O(n)
发布了300 篇原创文章 · 获赞 48 · 访问量 8075

猜你喜欢

转载自blog.csdn.net/qq_43539599/article/details/103980506
今日推荐