算法效率的度量(上):时间复杂度中的对数思维

一、大O表示法:只看趋势

大O表示法描述的是算法执行时间随数据规模增长的变化趋势。推导时只需记住:保留最高阶项,去掉所有系数

例如:2N + 3logN + 10 最终记为 O(N)


二、O(log N):折半就是对数

对数复杂度的核心特征只有一个:每次循环或递归,问题的规模都缩小一半(或按固定比例缩小)

2.1 循环折半

复制代码
#include <stdio.h>
/*
 * 功能:演示对数复杂度 O(log N)
 * 关键特征:循环变量 i 每次乘以 2(翻倍)
 * 
 * 推导:设循环执行 x 次后结束
 * 第1次:i = 2
 * 第2次:i = 4
 * 第3次:i = 8
 * ...
 * 第x次:i = 2^x
 * 
 * 结束条件:2^x >= N  =>  x >= log₂N
 * 因此时间复杂度为 O(log N)
 */
void demoLogN(int N) {
    int count = 0;
    int i = 1;
while (i < N) {
    i = i * 2;      // 关键:每次翻倍
    count++;
}
printf("N=%d, 循环次数=%d\n", N, count);
}
int main() {
demoLogN(16);       // N=16, 循环4次 (log₂16 = 4)
demoLogN(1024);     // N=1024, 循环10次
return 0;
}

快速判断 :看到循环变量 i *= 2i /= 2,立刻想到 O(log N)。底数是 2 还是 3 不影响大O表示。

2.2 二分查找

复制代码
#include <stdio.h>
/*
 * 功能:二分查找,在有序数组中查找目标值
 * 时间复杂度:O(log N)
 * 
 * 核心思想:每次比较后,搜索范围缩小一半
 * 无论目标在哪,最多只需 log₂N 次比较
 */
int binarySearch(int arr[], int n, int target) {
    int left = 0;
    int right = n - 1;
while (left <= right) {
    int mid = left + (right - left) / 2;  // 防止溢出的写法
if (arr[mid] == target) {
    return mid;         // 找到目标
}
else if (arr[mid] < target) {
    left = mid + 1;     // 目标在右半部分,范围减半
}
else {
    right = mid - 1;    // 目标在左半部分,范围减半
}
}
return -1;  // 未找到
}
int main() {
int arr[] = {3, 8, 12, 25, 31, 47, 58, 69, 81, 95};
int n = 10;
int result = binarySearch(arr, n, 47);
printf("查找结果下标:%d\n", result);  // 输出 5
return 0;
}

2.3 递归折半

复制代码
#include <stdio.h>
/*
 * 功能:递归方式演示对数复杂度
 * 时间复杂度:O(log N)
 * 
 * 分析:递归深度决定了执行次数
 * 每次递归 N 变为 N/2,深度为 log₂N
 * 每层只执行常数操作(打印、判断)
 */
void recursiveHalve(int N) {
    if (N <= 1) {
        return;             // 递归终止条件
    }
printf("当前 N = %d\n", N);
recursiveHalve(N / 2);  // 关键:每次规模减半
}
int main() {
recursiveHalve(16);     // 会打印 16, 8, 4, 2,共 log₂16 = 4 层
return 0;
}

三、O(N log N):线性遍历套折半操作

O(N log N) 是高效排序算法(如归并排序、堆排序)的典型复杂度。它的结构很固定:外层是 N 次的线性遍历,内层是 O(log N) 的折半操作

复制代码
#include <stdio.h>
/*
 * 功能:演示线性对数复杂度 O(N log N)
 * 
 * 外层循环:i 从 1 到 N,执行 N 次
 * 内层循环:j 每次翻倍,执行 log N 次
 * 总操作次数 = N * log N
 */
void demoNLogN(int N) {
    int total = 0;
for (int i = 1; i <= N; i++) {      // 外层:线性 N 次
    int j = 1;
while (j < N) {                 // 内层:对数 log N 次
    j = j * 2;
    total++;
}
}
printf("N=%d, 总操作次数=%d\n", N, total);
}
int main() {
demoNLogN(8);   // 8 * 3 = 24
demoNLogN(16);  // 16 * 4 = 64
return 0;
}

实际场景 :对 N 个元素分别进行一次二分查找,整体就是 O(N log N)


四、快速判断口诀

表格

代码特征 时间复杂度 判断要点
循环变量每次乘2或除2 O(log N) 折半、翻倍就是对数
外层 N 次 + 内层折半 O(N log N) 线性套对数
递归每次规模减半 O(log N) 递归深度为 log N
递归先遍历 N 再折半递归(两次) O(N log N) 类似归并排序的递归树

五、练习题

题目1

复制代码
void funcA(int N) {
    int i = N;
    while (i > 1) {
        i = i / 3;      // 每次变为原来的 1/3
    }
}

时间复杂度是多少?

题目2

复制代码
void funcB(int N) {
    for (int i = 0; i < N; i++) {
        int j = 1;
        while (j < N) {
            j = j * 2;
        }
    }
}

时间复杂度是多少?

题目3(递归)

复制代码
void funcC(int N) {
    if (N <= 1) return;
    funcC(N / 2);
}

时间复杂度是多少?

题目4(递归)

复制代码
void funcD(int N) {
    if (N <= 1) return;
    for (int i = 0; i < N; i++) {
        printf("%d ", i);
    }
    printf("\n");
    funcD(N / 2);
    funcD(N / 2);
}

时间复杂度是多少?

题目5(双参数)

复制代码
void funcE(int N, int M) {
    for (int i = 0; i < N; i++) printf("1");
    for (int j = 0; j < M; j++) printf("2");
}

时间复杂度是多少?(提示:O(M+N)O(max(M,N)) 在渐进意义下等价,因为 max(M,N) ≤ M+N ≤ 2×max(M,N),两者只相差常数倍。通常写 O(M+N) 更直观。)

答案与解析

表格

题号 时间复杂度 解析
1 O(log N) 循环变量每次除以3,底数不影响大O
2 O(N log N) 外层N次 × 内层log N次
3 O(log N) 递归深度为 log N,每层常数操作
4 O(N log N) 递归树深度为 log N,每层总工作量约为 N(N + N/2 + N/2 = 2N),总复杂度为 O(N log N)
5 O(M+N) 两个独立循环,复杂度相加。也可写作 O(max(M,N))
相关推荐
派葛穆6 小时前
Lerobot -飞特舵机ST-3215双舵机联动(ESP_NOW)
c语言
零依赖极客7 小时前
Day 6·1 ARMv8.2 dotprod——vdotq_s32 一条指令做 4 个点积
c语言·开发语言·人工智能·矩阵·arm·vllm
stolentime7 小时前
洛谷P10515 转圈题解
c++·算法·数学建模·贪心算法
和裕8 小时前
蜂窝板 vs 七层瓦楞重型纸箱:大件工业设备运输性能与成本全对比
大数据·运维·网络·人工智能·算法
衡石科技8 小时前
Data_Agent记忆机制与经验复用衡石分析智能体会话记忆与长期学习技术解析
人工智能·科技·学习·算法·企业级bi
Msshu1239 小时前
2~5串锂电快充优选|XSP36 Type‑C升压/升降压快充管理芯片
数据结构·随机森林·贪心算法·排序算法·线性回归·启发式算法
shirsl10 小时前
算法 Day 2 滑动窗口 + 栈 / 单调栈
开发语言·python·算法
老王爱玩车10 小时前
深入理解指针2
c语言·开发语言·学习
土司大王11 小时前
LeetCode hot100 回溯专题总结:Java 通用模板、决策树模型
java·算法·leetcode·决策树
2601_9622186111 小时前
万象生鲜系统订单全生命周期状态同步技术实现业务可视
大数据·数据库·人工智能·python·算法