263. 丑数 & 264. 丑数 II & 313. 超级丑数

263. 丑数

丑数就是只包含质因数 2,3,5的正整数。

class Solution {
    public boolean isUgly(int num) {
        int temp;
        if(num < 0)
            return false;
    	while(num != 1){
        	temp = num;
        	num = num%2 == 0 ? num/2 : num;
            num = num%3 == 0 ? num/3 : num;
            num = num%5 == 0 ? num/5 : num;
            if(temp == num)
                return false;
        }
        return true;
    }
}

264. 丑数 II

方法一:

暴力超时,毕竟丑数还是少数,自己测了下,第1362个丑数就是424673280了。

class Solution {
    public int nthUglyNumber(int n) {
    	int count = 0;
    	int i = 0;
    	for (; i < Integer.MAX_VALUE; i++) {
    		if(isUgly(i)) {
    			count++;
    			if(count == n) {
    				break;
    			}
    		}
    	}
    	return i;
    }
    public boolean isUgly(int num) {
        int temp;
    	while(num != 1){
        	temp = num;
        	num = num%2 == 0 ? num/2 : num;
            num = num%3 == 0 ? num/3 : num;
            num = num%5 == 0 ? num/5 : num;
            if(temp == num)
                return false;
        }
        return true;
    }
}

方法二:

丑数既然是包含质因数 2,3,5的正整数,那么一个丑数乘以2,3,5应该还是丑数,我们从1开始构造丑数,往下找就行了,关键是如何构造的时候不重复。

可以使用PriorityQueue构造最小堆

class Solution {
    public int nthUglyNumber(int n) {
    	//优先队列默认排序规则是从小到大排序
    	//必须用Long,测过用Integer会溢出
    	PriorityQueue<Long> pq = new PriorityQueue<Long>();
    	int[] divisor = new int[] {2, 3, 5};
    	long[] res = new long[n];  //存丑数
    	res[0] = (long) 1;
    	for (int i = 0; i < n; i++) {
			for (int j = 0; j < divisor.length; j++) {
				Long temp = (Long) (res[i]*divisor[j]);
				if(!pq.contains(temp)) {
					pq.add(temp);
				}
			}
			//从队头取一个最小的丑数,并把它从队列删除
            if (i+1<res.length){
                res[i+1] = pq.poll();
            }
		}
    	return (int) res[n-1];
    }
}

实际时间复杂度为O(n^2*divisior.length),优先队列的方法contains时间复杂度为O(n),但是由于n的个数较小,还是可以通过的。

ps:

//再瞅一眼contains的源码
//优先队列继承的是object对象的方法,用的是顺序查找,优先队列排序的准则是不一定的,jdk没法用折半查找
    private int indexOf(Object o) {
        if (o != null) {
            for (int i = 0; i < size; i++)
                if (o.equals(queue[i]))
                    return i;
        }
        return -1;
    }

方法三:

动态规划+三指针:

没有想到,翻别人题解,学到了学到了orz

	public int nthUglyNumber(int n) {
        int[] dp = new int[n];	//dp保存按序排列的丑数
        dp[0] = 1;
        int i2 = 0, i3 = 0, i5 = 0;
        for (int i = 1; i < n; i++) {
            int min = Math.min(dp[i2] * 2, Math.min(dp[i3] * 3, dp[i5] * 5));
            if (min == dp[i2] * 2) i2++;
            if (min == dp[i3] * 3) i3++;
            if (min == dp[i5] * 5) i5++;
            dp[i] = min;
        }

        return dp[n - 1];
    }

这个思路跟之前的不同在于,之前是将最小的数分别乘以2,3,5都放进队列中判断,会有很大可能的重复,从而浪费时间,而DP的神仙之处在于可以同时选取多个数进行比较,并且乘因子的时候2,3,5是异步的,选取最小的,也不需要重新排序了。

时间复杂度为O(n)


313. 超级丑数

三指针变成了primes.length个指针。

class Solution {
    public int nthSuperUglyNumber(int n, int[] primes) {
    	int[] index = new int[primes.length];
    	int[] dp = new int[n];
    	dp[0] = 1;
    	for (int i = 1; i < n; i++) {
            dp[i] = Integer.MAX_VALUE;
			for (int j = 0; j < primes.length; j++) {
				while(dp[index[j]]*primes[j] <= dp[i-1]) index[j]++;
				dp[i] = Math.min(dp[i], dp[index[j]]*primes[j]);
			}
		}
    	return dp[n-1];
    }
}

发布了56 篇原创文章 · 获赞 4 · 访问量 1702

猜你喜欢

转载自blog.csdn.net/qq_41342326/article/details/104127897