时间复杂度和空间复杂度的最小单位是什么

文章详细解释了时间复杂度和空间复杂度的概念,以求n个整数最大值为例,分析了如何量化时间和空间复杂度,指出常数项对复杂度的影响,并简化为O(n)。

C++数据结构与算法 目录

时间复杂度:CPU读写一次内存算作时间复杂度的最小单位。

读内存的场景:获取变量的值。

例如: if(x < 1000)

写内存的场景:给变量赋值。

例如:x = 1000

空间复杂度:内存占用一个字节作为空间复杂度的最小单位。

时间复杂度与空间复杂度的单位

如上图所示,由于CPU运算(加减乘除)特别快,而内存的速度与之相比慢了很多,但是CPU又只有有限的几个寄存器可以参与计算,所以,数据总是保存在内存中,通过内存数据反复的和寄存器交换数据,再由CPU加工寄存器数据来完成实际的运算,这就导致瓶颈发生在内存的读写上。

如果一个计算过程需要更多的内存字节数量,我们就说这个计算过程的空间复杂度比较高。

如果一个计算过程需要更多的内存读写次数,我们就说这个计算过程的时间复杂度比较高。

如何量化时间复杂度和空间复杂度呢?

我们用时间复杂度和空间复杂度的最小单位的函数来表示。

比如,我想求 n 个整数的最大值。

空间复杂度怎么量化?

那么,我就需要 n 个内存空间来存储这 n 个整数。这时候我们就说求 n 个整数最大值的过程(算法),需要的空间复杂度为 O(n)。

时间复杂度怎么量化?

假设,这 n 个整数 连续的存储在一个数组中,那么需要的时间复杂度就是需要遍历这个 n 个整数。代码如下:

int max_num = array[0];
for(int i = 0; i < n; ++i)
{
    max_num = max(max_num, array[i]);
}
return max_num;

从代码中可以看出来,我们需要读取内存 次数为 1 + n * 3。

其中, 1 对应: int max_num = array[0]; 也就是给 max_num 初始化。

其中, n * 3 对应: for(i : 0 ~ n-1)  max_num = max(max_num, array[i])。 循环执行了[0~n-1] 次,也就是 n 次。 

循环的每一次,访问了几次内存呢? 3 次。 

为何是 3 次呢? 其实是 max_num = max(max_num, array[i]); 这条语句访问了 3 次内存。

读取 max_num 原来的值 1 次(读内存), 读取 array[i] 的值 1 次(读内存), 最后把 新的 max 求出来(这个比较是在CPU内部寄存器完成的,没有再发生内存读取),求出了 新的 max 之后 写回到内存覆盖 max_num 之前的值。 这时候 是 1 次写内存。

所以,总共是 2 次读内存, 1 次 写内存。 总共 是 3 次。

综上,求 n 个数的最大值,时间复杂度为 1 + n * 3。

由于随着 n 的规模变大, 常数 1 可以忽略。 比如, 1000000000 个整数的最大值,和 1000000001个整数的最大值,我们认为时间复杂度是相同的。

这样时间复杂度 就变成了 O(n*3) 。

另外,常数项系数,并没有从本质上改变复杂度。我们通常把常数项都改写为 1 来简化问题复杂度的描述。

这样以来,时间复杂度就变成了 O(n)。

为了使**时间复杂度****空间复杂度**尽可能最小,我们需要对原始代码进行优化。主要优化点包括: - **优化素数判断**:使用**埃拉托斯特尼筛法(Sieve of Eratosthenes)**预处理所有可能的素数,避免重复计算。 - **预处理数字**:避免多次将数字转为字符串求,可以提前计算。 - **避免重复计算双重素数**,改用查表方式。 --- ### 修改后的优化代码 ```python import sys def sieve(max_limit): """埃拉托斯特尼筛法,返回最大到max_limit的素数表""" is_prime = [True] * (max_limit + 1) is_prime[0] = is_prime[1] = False for i in range(2, int(max_limit ** 0.5) + 1): if is_prime[i]: for j in range(i * i, max_limit + 1, i): is_prime[j] = False return is_prime def digit_sum(n): """计算数字各位之""" s = 0 while n > 0: s += n % 10 n //= 10 return s def main(): input = sys.stdin.read data = input().split() T = int(data[0]) queries = [] idx = 1 L_list = [] R_list = [] max_R = 0 for _ in range(T): L = int(data[idx]) R = int(data[idx + 1]) queries.append((L, R)) max_R = max(max_R, R) idx += 2 # 预处理最大可能的素数表数字素数表 is_prime = sieve(max_R) is_digit_prime = [False] * (max_R + 1) for i in range(max_R + 1): s = digit_sum(i) if is_prime[s]: is_digit_prime[i] = True # 前缀优化查询双重素数个数 prefix = [0] * (max_R + 2) for i in range(1, max_R + 1): prefix[i] = prefix[i - 1] + (is_prime[i] and is_digit_prime[i]) # 回答查询 results = [] for L, R in queries: count = prefix[R] - prefix[L - 1] results.append(str(count)) print('\n'.join(results)) if __name__ == "__main__": main() ``` --- ### 复杂度分析 #### 时间复杂度 - **筛法预处理素数**:$O(R \log \log R)$ - **前缀数组构造**:$O(R)$ - **每次查询时间**:$O(1)$ 总时间复杂度为: $$ O(R \log \log R + T) $$ #### 空间复杂度 - **素数标记数组**:$O(R)$ - **前缀数组**:$O(R)$ 总空间复杂度为: $$ O(R) $$ --- ### 优化点说明 1. **埃拉托斯特尼筛法**(Sieve of Eratosthenes):快速预处理所有可能的素数,避免在每次判断时重复计算。 2. **前缀数组优化**:利用前缀进行区间查询优化,将每次查询的时间从 $O(R - L + 1)$ 降低为 $O(1)$。 3. **使用 `sys.stdin.read` 加快输入处理**:减少输入 I/O 操作时间,适合大数据量时提升性能。 此代码适合大规模输入下的双重素数统计问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

C++程序员Carea

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值