给你两个正整数 left
和 right
,请你找到两个整数 num1
和 num2
,它们满足:
left <= nums1 < nums2 <= right
。
nums1
和 nums2
都是 质数 。
nums2 - nums1
是满足上述条件的质数对中的 最小值 。
请你返回正整数数组 ans = [nums1, nums2]
。如果有多个整数对满足上述条件,请你返回 nums1
最小的质数对。如果不存在符合题意的质数对,请你返回 [-1, -1]
。
如果一个整数大于 1
,且只能被 1
和它自己整除,那么它是一个质数。
示例 1:
**输入:** left = 10, right = 19
**输出:** [11,13]
**解释:** 10 到 19 之间的质数为 11 ,13 ,17 和 19 。
质数对的最小差值是 2 ,[11,13] 和 [17,19] 都可以得到最小差值。
由于 11 比 17 小,我们返回第一个质数对。
示例 2:
**输入:** left = 4, right = 6
**输出:** [-1,-1]
**解释:** 给定范围内只有一个质数,所以题目条件无法被满足。
提示:
1 <= left <= right <= 106
分两步:
筛质数,做法见 204. 计数质数 。
找 [\textit{left},\textit{right}] 范围内的最小质数间隙(prime gap),暴力枚举范围内的所有相邻质数即可。
附:视频讲解
优化
找范围内的第一个质数可以用二分查找。
可以往质数表末尾额外插入 2 个 10^6+1,这样无需判断下标是否越界。
[sol1-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 MX = 10 ** 6 + 1 primes = [] is_prime = [True ] * MX for i in range (2 , MX): if is_prime[i]: primes.append(i) for j in range (i * i, MX, i): is_prime[j] = False primes.extend((MX, MX)) class Solution : def closestPrimes (self, left: int , right: int ) -> List [int ]: p = q = -1 i = bisect_left(primes, left) while primes[i + 1 ] <= right: if p < 0 or primes[i + 1 ] - primes[i] < q - p: p, q = primes[i], primes[i + 1 ] i += 1 return [p, q]
[sol1-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 class Solution { private final static int MX = (int ) 1e6 ; private final static int [] primes = new int [78500 ]; static { var np = new boolean [MX + 1 ]; var pi = 0 ; for (var i = 2 ; i <= MX; ++i) if (!np[i]) { primes[pi++] = i; for (var j = i; j <= MX / i; ++j) np[i * j] = true ; } primes[pi++] = MX + 1 ; primes[pi++] = MX + 1 ; } public int [] closestPrimes(int left, int right) { int p = -1 , q = -1 ; for (var i = lowerBound(primes, left); primes[i + 1 ] <= right; ++i) if (p < 0 || primes[i + 1 ] - primes[i] < q - p) { p = primes[i]; q = primes[i + 1 ]; } return new int []{p, q}; } private int lowerBound (int [] nums, int target) { int left = -1 , right = nums.length; while (left + 1 < right) { int mid = left + (right - left) / 2 ; if (nums[mid] < target) left = mid; else right = mid; } return right; } }
[sol1-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 const int MX = 1e6 ;vector<int > primes; int init = []() { bool np[MX + 1 ]{}; for (int i = 2 ; i <= MX; ++i) if (!np[i]) { primes.push_back (i); for (int j = i; j <= MX / i; ++j) np[i * j] = true ; } primes.push_back (MX + 1 ); primes.push_back (MX + 1 ); return 0 ; }(); class Solution {public : vector<int > closestPrimes (int left, int right) { int p = -1 , q = -1 ; int i = lower_bound (primes.begin (), primes.end (), left) - primes.begin (); for (; primes[i + 1 ] <= right; ++i) if (p < 0 || primes[i + 1 ] - primes[i] < q - p) { p = primes[i]; q = primes[i + 1 ]; } return {p, q}; } };
[sol1-Go] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 const mx = 1e6 + 1 var primes = make ([]int , 0 , 78500 )func init () { np := [mx]bool {} for i := 2 ; i < mx; i++ { if !np[i] { primes = append (primes, i) for j := i * i; j < mx; j += i { np[j] = true } } } primes = append (primes, mx, mx) } func closestPrimes (left, right int ) []int { p, q := -1 , -1 for i := sort.SearchInts(primes, left); primes[i+1 ] <= right; i++ { if p < 0 || primes[i+1 ]-primes[i] < q-p { p, q = primes[i], primes[i+1 ] } } return []int {p, q} }
也可以用线性筛(欧拉筛)做,具体原理见 视频讲解 。
[sol2-Python3] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 MX = 10 ** 6 + 1 primes = [] is_prime = [True ] * MX for i in range (2 , MX): if is_prime[i]: primes.append(i) for p in primes: if i * p >= MX: break is_prime[i * p] = False if i % p == 0 : break primes.extend((MX, MX)) class Solution : def closestPrimes (self, left: int , right: int ) -> List [int ]: p = q = -1 i = bisect_left(primes, left) while primes[i + 1 ] <= right: if p < 0 or primes[i + 1 ] - primes[i] < q - p: p, q = primes[i], primes[i + 1 ] i += 1 return [p, q]
[sol2-Java] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 class Solution { private final static int MX = (int ) 1e6 ; private final static int [] primes = new int [78500 ]; static { var np = new boolean [MX + 1 ]; var pi = 0 ; for (var i = 2 ; i <= MX; ++i) { if (!np[i]) primes[pi++] = i; for (var j = 0 ; j < pi; ++j) { var p = primes[j]; if (i * p > MX) break ; np[i * p] = true ; if (i % p == 0 ) break ; } } primes[pi++] = MX + 1 ; primes[pi++] = MX + 1 ; } public int [] closestPrimes(int left, int right) { int p = -1 , q = -1 ; for (var i = lowerBound(primes, left); primes[i + 1 ] <= right; ++i) if (p < 0 || primes[i + 1 ] - primes[i] < q - p) { p = primes[i]; q = primes[i + 1 ]; } return new int []{p, q}; } private int lowerBound (int [] nums, int target) { int left = -1 , right = nums.length; while (left + 1 < right) { int mid = left + (right - left) / 2 ; if (nums[mid] < target) left = mid; else right = mid; } return right; } }
[sol2-C++] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const int MX = 1e6 ;vector<int > primes; int init = []() { bool np[MX + 1 ]{}; for (int i = 2 ; i <= MX; ++i) { if (!np[i]) primes.push_back (i); for (int p: primes) { if (i * p > MX) break ; np[i * p] = true ; if (i % p == 0 ) break ; } } primes.push_back (MX + 1 ); primes.push_back (MX + 1 ); return 0 ; }(); class Solution {public : vector<int > closestPrimes (int left, int right) { int p = -1 , q = -1 ; int i = lower_bound (primes.begin (), primes.end (), left) - primes.begin (); for (; primes[i + 1 ] <= right; ++i) if (p < 0 || primes[i + 1 ] - primes[i] < q - p) { p = primes[i]; q = primes[i + 1 ]; } return {p, q}; } };
[sol2-Go] 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 const mx = 1e6 + 1 var primes = make ([]int , 0 , 78500 )func init () { np := [mx]bool {} for i := 2 ; i < mx; i++ { if !np[i] { primes = append (primes, i) } for _, p := range primes { if i*p >= mx { break } np[i*p] = true if i%p == 0 { break } } } primes = append (primes, mx, mx) } func closestPrimes (left, right int ) []int { p, q := -1 , -1 for i := sort.SearchInts(primes, left); primes[i+1 ] <= right; i++ { if p < 0 || primes[i+1 ]-primes[i] < q-p { p, q = primes[i], primes[i+1 ] } } return []int {p, q} }
复杂度分析
时间复杂度:O(\textit{right}),忽略预处理的时间复杂度。严谨地说,由于范围内有 O\left(\dfrac{\textit{right} }{\log\textit{right} }-\dfrac{\textit{left} }{\log\textit{left} }\right) 个质数(根据质数密度),所以遍历的时间复杂度为 O\left(\dfrac{\textit{right} }{\log\textit{right} }-\dfrac{\textit{left} }{\log\textit{left} }\right),再算上二分质数的时间 O(\log\pi(U))(\pi(U) 表示 U=10^6 内的质数个数,这有 78498 个),总的时间复杂度为 O\left(\log\pi(U) + \dfrac{\textit{right} }{\log\textit{right} }-\dfrac{\textit{left} }{\log\textit{left} }\right)。
空间复杂度:O(1),忽略预处理的空间复杂度。仅用到若干变量。