终于把时间复杂度搞清楚了

一、时间复杂度的概念理解

时间复杂度指的是算法程序的语句的执行次数,也可以称为语句频度,一个程序的语句执行次数越多,则时间复杂度越大,则说明算法不合适。时间复杂度一般采用数学符号大O()表示,一般时间复杂度的计算中都会出现n,n表示规模,对于时间复杂度是表示算法的趋势

一般会把算法程序的语句的执行次数用T()表示,但是对于函数T()可能是一个多项式,而时间复杂度就是找出函数T()影响最大的项,所以时间复杂度是执行语句的估算值,使用数学符号大O()表示。O其实是order的缩写。大O的括号中写的值就是影响程序执行语句最大的那个项

二、时间复杂度计算

  • 案例一: O(n)
int main(void)
{
    for(int i = 0; i < n; i++);   // 分别执行1次,n+1次, n次
    {
        printf("hello");  // 执行n次
    }
}
// 执行次数总和为T(n)=3n+2, 但是语句执行的具体次数并不重要,我们只想知道语句的规模增长趋势
// 当n趋近于无穷时,T(n)≈n,即时间复杂度为O(n)
  • 案例二: O(√n)
int main(void)
{
    int i=0;  // 1次
    int s=0;  // 1次
    while(s<n)
    {
        ++i; 
        s+=i;  
    }
}
// s的加值为1,2,3,4,5...k次,因此判断条件时s的值为等差数列求和即(1+k)*k/2 < n
// k即语句循环次数,k²+k = 2n ,当n无穷大时,k²对规模增长起决定性作用,因而 k²≈n, k≈√n
// 语句循环次数T(n)≈2k=2√n,时间复杂度O(√n)

总结: 通过观察语句规律, 找出语句的循环次数T(n), 最后再取多项式的最高次幂即可

三、时间、空间互换的案例

计算32位系统中一个int型数据中二进制1的个数

在C语言中,给定一个整数,计算其二进制表示中1的个数是一个常见的问题。以下是几种高效的实现方法,适合不同场景的使用:

方法一:逐位检查法
通过循环检查整数的每一位是否为1,具体步骤如下:

  1. 使用 & 操作符与 1 相与,判断最低位是否为1。
  2. 将整数右移一位,继续检查下一位。
  3. 重复上述过程,直到整数变为0。

代码示例:

int count_one_bits(int n) {
    int count = 0;
    while (n) {
        count += n & 1;
        n >>= 1;
    }
    return count;
}

优点:简单易理解,适用于初学者。
缺点:效率较低,因为每个位都需要检查。


方法二:Brian Kernighan 算法
这是一种优化的位操作算法,利用 n & (n - 1) 的特性,每次消去二进制中最右边的1,具体步骤如下:

  1. 使用 n & (n - 1) 操作将最右边的1变为0。
  2. 统计操作的次数,即为1的个数。

代码示例:

int count_one_bits(int n) {
    int count = 0;
    while (n) {
        n = n & (n - 1);
        count++;
    }
    return count;
}

优点:高效,时间复杂度为 O(log n),适合处理大量数据。


方法三:查表法
通过预先生成一个大小为256的表,记录每个字节中1的个数,然后将整数拆分为多个字节进行查询。

代码示例:

int count_one_bits(int n) {
    static const int table[256] = {
        0, 1, 1, 2, 1, 2, /* ... */, 8 // 预先生成的表
    };
    int count = 0;
    unsigned char* p = (unsigned char*)&n;
    for (int i = 0; i < sizeof(n); i++) {
        count += table[p[i]];
    }
    return count;
}

优点:极高效,适合对性能要求极高的场景。



总结: 查表法是对空间换时间的极致体现

posted @ 2025-03-12 23:31  小镇青年达师傅  阅读(352)  评论(0)    收藏  举报