时间和空间复杂度

1.时间复杂度和空间复杂度

空间复杂度和时间复杂度详解

时间复杂度

定义

时间复杂度 :描述算法执行时间随输入数据规模增长的变化趋势。

常用表示法(大O表示法)

常见时间复杂度(从小到大):

复杂度 名称 示例
O(1) 常数时间 数组索引访问
O(log n) 对数时间 二分查找
O(n) 线性时间 遍历数组
O(n log n) 线性对数时间 快速排序、归并排序
O(n²) 平方时间 冒泡排序、嵌套循环
O(2ⁿ) 指数时间 斐波那契数列(递归)
O(n!) 阶乘时间 旅行商问题

记住:O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(2ⁿ) < O(n!)

示例代码分析

javascript 复制代码
// O(1) 常数时间复杂度
//执行时间与输入规模无关,始终固定。
function getFirstElement(arr) {
    return arr[0];  // 无论数组多大,只执行一次操作
}
//分析:无论数组有多大,只需要访问第一个元素,操作次数恒定。

// O(n) 线性时间复杂度
function sumArray(arr) {
  let total = 0;
  for (let i = 0; i < arr.length; i++) {
    total += arr[i]; // 执行次数 = arr.length
  }
  return total;
}

console.log(sumArray([1, 2, 3, 4, 5])); // 输出 15
//分析:循环执行次数与数组长度成正比,输入规模n倍增,执行时间也约n倍增。

// O(n²) 平方时间复杂度
function bubbleSort(arr) {
    for (let i = 0; i < arr.length; i++) {          // 外层循环 n 次
        for (let j = 0; j < arr.length - 1; j++) {  // 内层循环 n-1 次
            if (arr[j] > arr[j + 1]) {
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;  // 总操作次数 ≈ n * (n-1) ≈ O(n²)
}
//分析:两层嵌套循环,最坏情况下需要比较n*(n-1)次,时间复杂度≈为O(n²)。

// O(log n) 对数时间复杂度
function binarySearch(arr, target) {
    let left = 0, right = arr.length - 1;
    while (left <= right) {
        const mid = Math.floor((left + right) / 2);
        if (arr[mid] === target) return mid;
        if (arr[mid] < target) left = mid + 1;  // 每次循环数据规模减半
        else right = mid - 1;  // 每次循环数据规模减半
    }
    return -1;  // 总操作次数 ≈ log₂n
}
//分析:每次循环将搜索范围减半,执行次数约为log₂n,时间复杂度为O(log n)。

时间复杂度计算规则

  1. 忽略常数项:O(2n) = O(n)
  2. 保留最高阶项:O(n² + n) = O(n²)
  3. 循环嵌套相乘顺序执行相加
javascript 复制代码
// O(n + m) 时间复杂度
function twoLoops(n, m) {
    for (let i = 0; i < n; i++) {}    // O(n)
    for (let j = 0; j < m; j++) {}    // O(m)
    // 总复杂度: O(n + m)
}

// O(n × m) 时间复杂度
function nestedLoops(n, m) {
    for (let i = 0; i < n; i++) {       // O(n)
        for (let j = 0; j < m; j++) {   // O(m)
            // 操作
        }
    }
    // 总复杂度: O(n × m)
}

空间复杂度

定义

空间复杂度 :描述算法占用内存空间随输入数据规模增长的变化趋势。

常见空间复杂度

复杂度 描述 示例
O(1) 常数空间 使用固定数量的变量
O(n) 线性空间 创建与输入等长的数组
O(n²) 平方空间 创建 n×n 的二维数组
O(log n) 对数空间 递归调用栈深度

示例代码分析

javascript 复制代码
// O(1) 常数空间复杂度
function sum(arr) {
    let total = 0;          // 1个变量
    for (let i = 0; i < arr.length; i++) {
        total += arr[i];
    }
    return total;  // 只用了2个变量,与输入规模无关
}
//分析:只使用了total和i两个变量,空间占用不随输入数组大小变化。

// O(n) 线性空间复杂度
function copyArray(arr) {
    const copy = [];                     // 创建新数组
    for (let i = 0; i < arr.length; i++) {
        copy.push(arr[i]);               // 占用空间与输入大小成正比
    }
    return copy;  // 空间复杂度 O(n)
}
//分析:创建了一个与输入数组等长的新数组,空间复杂度为O(n)。

// O(n²) 平方空间复杂度
function createMatrix(n) {
    const matrix = [];
    for (let i = 0; i < n; i++) {
        matrix[i] = [];
        for (let j = 0; j < n; j++) {
            matrix[i][j] = i + j;        // 创建 n×n 的二维数组
        }
    }
    return matrix;  // 空间复杂度 O(n²)
}
//分析:创建了一个n×n的二维数组,空间复杂度为O(n²)。

// O(log n) 空间复杂度(递归深度)
function binarySearchRecursive(arr, target, left = 0, right = arr.length - 1) {
    if (left > right) return -1;
    
    const mid = Math.floor((left + right) / 2);
    if (arr[mid] === target) return mid;
    
    if (arr[mid] < target) {
        return binarySearchRecursive(arr, target, mid + 1, right);
    } else {
        return binarySearchRecursive(arr, target, left, mid - 1);
    }
    // 递归深度 = 调用栈深度 = log₂n
}

时间与空间复杂度权衡

javascript 复制代码
// 示例:查找数组中的重复元素

// 方法1:时间复杂度 O(n²),空间复杂度 O(1)
function findDuplicateBruteForce(arr) {
    for (let i = 0; i < arr.length; i++) {
        for (let j = i + 1; j < arr.length; j++) {
            if (arr[i] === arr[j]) return arr[i];
        }
    }
    return null;
    // 时间: O(n²) - 嵌套循环
    // 空间: O(1) - 只用常数个变量
}

// 方法2:时间复杂度 O(n log n),空间复杂度 O(1)
function findDuplicateSort(arr) {
    arr.sort((a, b) => a - b);  // O(n log n)
    for (let i = 1; i < arr.length; i++) {  // O(n)
        if (arr[i] === arr[i - 1]) return arr[i];
    }
    return null;
    // 时间: O(n log n + n) ≈ O(n log n)
    // 空间: O(1) - 原地排序
}

// 方法3:时间复杂度 O(n),空间复杂度 O(n)
function findDuplicateHash(arr) {
    const seen = new Set();  // O(n) 空间
    for (let i = 0; i < arr.length; i++) {  // O(n) 时间
        if (seen.has(arr[i])) return arr[i];
        seen.add(arr[i]);
    }
    return null;
    // 时间: O(n)
    // 空间: O(n)
}

// 方法4:时间复杂度 O(n),空间复杂度 O(1) - 但有限制
function findDuplicateFloyd(arr) {
    // 使用弗洛伊德循环检测算法
    // 要求:数组元素在 1..n 范围内,且只有一个重复
    let slow = arr[0];
    let fast = arr[0];
    
    do {
        slow = arr[slow];
        fast = arr[arr[fast]];
    } while (slow !== fast);
    
    slow = arr[0];
    while (slow !== fast) {
        slow = arr[slow];
        fast = arr[fast];
    }
    return slow;
    // 时间: O(n)
    // 空间: O(1) - 只用几个变量
}

实际应用中的复杂度分析

React/Vue 组件

javascript 复制代码
// O(n) 时间,O(1) 空间
function ListComponent({ items }) {
    return (
        <ul>
            {items.map(item => (  // O(n) 时间
                <li key={item.id}>{item.name}</li>
            ))}
        </ul>
    );
}

// O(n²) 时间(潜在性能问题)
function NestedListComponent({ data }) {
    return (
        <div>
            {data.map(item => (  // O(n)
                <div key={item.id}>
                    {item.children.map(child => (  // O(m) - 嵌套
                        <span key={child.id}>{child.name}</span>
                    ))}
                </div>
            ))}
        </div>
    );
    // 总复杂度: O(n × m)
}

API 请求处理

javascript 复制代码
// 低效:O(n²) 时间
async function getUsersWithPosts(users) {
    const result = [];
    for (const user of users) {  // O(n)
        const posts = await fetchPosts(user.id);  // O(m) API调用
        result.push({ ...user, posts });
    }
    return result;  // 总: O(n × m) 时间
}

// 高效:O(n + m) 时间
async function getUsersWithPostsOptimized(users) {
    // 批量获取所有用户的帖子
    const postPromises = users.map(user => 
        fetchPosts(user.id)  // 并行请求
    );
    const allPosts = await Promise.all(postPromises);  // O(n) 并行
    
    return users.map((user, index) => ({  // O(n)
        ...user,
        posts: allPosts[index]
    }));
}

性能优化建议

时间优化技巧

  1. 缓存计算结果(记忆化)
  2. 使用哈希表代替线性查找
  3. 排序后使用二分查找
  4. 避免嵌套循环

空间优化技巧

  1. 原地操作(修改原数组而非创建新数组)
  2. 流式处理大数据集
  3. 及时释放引用(设置为 null)
  4. 使用位运算存储状态

复杂度分析工具

javascript 复制代码
// 性能测试示例
function measurePerformance(fn, input) {
    const startTime = performance.now();
    const result = fn(input);
    const endTime = performance.now();
    
    console.log(`执行时间: ${endTime - startTime}ms`);
    console.log(`输入规模: ${input.length}`);
    console.log(`结果: ${result}`);
    
    return result;
}

总结

  • 时间复杂度 关注速度空间复杂度 关注内存
  • 一般优先优化时间复杂度,除非内存受限
  • 根据实际场景选择最优方案(时间 vs 空间权衡)
  • 理解复杂度有助于写出更高效、可扩展的代码

2.

相关推荐
七夜zippoe4 天前
Python算法优化实战:时间与空间复杂度的艺术平衡
开发语言·python·算法·贪心算法·动态规划·复杂度
聆风吟º2 个月前
【数据结构入门手札】算法核心概念与复杂度入门
数据结构·算法·复杂度·算法的特性·算法设计要求·事后统计方法·事前分析估算方法
egoist20231 年前
数据结构之堆排序
c语言·开发语言·数据结构·算法·学习方法·堆排序·复杂度
egoist20231 年前
数据结构之算法复杂度(超详解)
c语言·开发语言·数据结构·学习·算法·leetcode·复杂度
老肖相当外语大佬1 年前
DDD之理解复杂度、尊重复杂度、掌控复杂度
ddd·领域驱动设计·复杂度·软件成本
Jamo@2 年前
复杂度分析
c语言·数据结构·复杂度