diff --git a/solutions/313. Super Ugly Number.md b/solutions/313. Super Ugly Number.md index 590dcab..9d8045e 100644 --- a/solutions/313. Super Ugly Number.md +++ b/solutions/313. Super Ugly Number.md @@ -11,10 +11,20 @@ 当k比较大时候思路一就显得可优化了, 我们可以维护一个大小为k的候选值最小堆, 每个元素包含两个域: value和idx, 分别代表值和由哪一个质数得来, 可以用pair实现. 每当我们从堆顶pop出一个元素, 这个元素的value就是下一个丑陋的数(注意可能重复), 这个元素的idx就代表从哪一个质数得来, 应该将该质数的idx加一, 再向堆中插入下一个候选值. -由于向堆中插入和pop都是对数级别的, 所以总的时间复杂度就是O(nlogk). (但是实测比方法一慢, 猜想可能是由于测试样例k不是很大) +由于向堆中插入和pop都是对数级别的, 所以总的时间复杂度就是O(nlogk). (但亲测慢很多) > 注意学习`pair`以及`priority_queue`的用法. +更新: +## 思路二改进 +思路二会比思路一慢很多, 应该有两个原因: +1. 测试样例的k不是很大; +2. 因为堆里面包含大量重复元素, 所以while循环不止执行n次, 所以复杂度应该不止O(nlogk). + +所以我们考虑让重复值不要进入堆, 网上只找到了代码没有解释, 代码我也没咋看懂, 这里只给出代码, 有空了再好好想想. + +亲测会比思路二快很多. + # C++ ## 思路一 ``` C++ @@ -68,3 +78,35 @@ public: }; ``` +## 思路二改进 +``` C++ +class Solution { +public: + int nthSuperUglyNumber(int n, vector& primes) { + int prime_n = primes.size(); + vectornums(n, 0), idx(prime_n, 0), last_factor(n, 0); + nums[0] = 1; + + // pair: UglyNumber, produce_by_which_prime + priority_queue, vector>, greater>> minheap; + for(int i = 0; i < prime_n; i++) + minheap.push({primes[i], i}); + + for(int i = 1; i < n; i++){ + pairnext = minheap.top(); minheap.pop(); + nums[i] = next.first; + last_factor[i] = next.second; + + idx[next.second]++; + // skip duplicates + while(last_factor[idx[next.second]] > next.second) + idx[next.second]++; + + minheap.push({nums[idx[next.second]] * primes[next.second], next.second}); + } + for(int a: last_factor) cout << a << " "; + return nums.back(); + } +}; +``` +