# 1. 概述与复杂度分析 > 本文是整个「数据结构与算法」系列的总纲。后续所有章节(顺序表、链表、栈、队列、树、图、哈希表、排序、查找)都建立在本章概念之上。 ## 数据结构三要素 数据结构 = **逻辑结构** + **存储结构** + **数据的运算**。三者缺一不可: | 要素 | 含义 | 例子 | | ---------- | ------------------------------------------------------ | ------------------------ | | 逻辑结构 | 数据元素之间的逻辑关系(与存储无关,是抽象出来的关系) | 线性结构、树形结构 | | 存储结构 | 数据在计算机中的物理存储方式(如何把逻辑关系落地) | 顺序存储、链式存储 | | 数据的运算 | 施加在数据上的操作,如插入、删除、查找 | 在顺序表中按下标插入元素 | - 逻辑结构是「用什么关系组织数据」,存储结构是「怎么在内存里摆」,运算是「对这个结构能做什么」。 - 同一逻辑结构可以用不同存储结构实现,运算的实现效率随之不同。 ## 四种逻辑结构 | 逻辑结构 | 特征 | 典型代表 | | -------- | -------------------------------------------------- | ---------------------- | | 集合结构 | 元素之间除了「同属于一个集合」外无其他关系 | 并查集、哈希集合 | | 线性结构 | 元素之间一一对应,有唯一前驱和唯一后继(除了首尾) | 顺序表、链表、栈、队列 | | 树形结构 | 一对多,一个根、多个分支,分层 | 二叉树、堆、B 树 | | 图状结构 | 多对多,任意两点之间都可能有关系 | 有向图、无向图、网络 | ## 四种存储结构 | 存储结构 | 说明 | 优点 | 缺点 | | -------- | -------------------------------------------------------- | ---------------------------- | ---------------------------------------- | | 顺序存储 | 用一组连续的存储单元依次存放数据元素,逻辑相邻即物理相邻 | 随机存取、实现简单 | 插入删除需移动大量元素;需要大片连续空间 | | 链式存储 | 用一组任意(不连续)的存储单元存放,借助指针表示逻辑关系 | 插入删除灵活、空间可动态分配 | 只能顺序存取;额外存储指针、空间开销大 | | 索引存储 | 建立索引表,索引项指向数据元素 | 查找速度快,便于按关键字检索 | 索引表本身占用空间,增删要维护索引 | | 散列存储 | 根据元素的关键字直接计算出存储地址 | 查找平均时间复杂度 O(1) | 存在冲突(碰撞),需要处理冲突的策略 | ## 时间复杂度 - **定义**:算法执行时间随问题规模 n 增长的变化趋势,记为 **T(n)**。用**大 O 表示法**(算法的渐进时间复杂度)描述:`T(n) = O(f(n))`,表示 T(n) 的增长不慢于 f(n)。 - 只保留**最高阶项**,忽略常数系数与低阶项:例如 `T(n) = 3n² + 2n + 100` → `O(n²)`。 - **最坏 / 平均 / 最好** 三种情况:通常以**最坏时间复杂度**作为算法性能的保证。 ### 常见复杂度量级排序(从小到大) | 量级 | 名称 | 规模 10 时约执行 | 规模 100 时约执行 | | ---------- | ---------- | ---------------- | ----------------- | | O(1) | 常数阶 | 1 | 1 | | O(log₂n) | 对数阶 | 4 | 7 | | O(n) | 线性阶 | 10 | 100 | | O(n log₂n) | 线性对数阶 | 34 | 664 | | O(n²) | 平方阶 | 100 | 10 000 | | O(n³) | 立方阶 | 1 000 | 1 000 000 | | O(2ⁿ) | 指数阶 | 1 024 | 巨大 | | O(n!) | 阶乘阶 | 3 628 800 | 天文数字 | > 排序关系:`O(1) < O(log n) < O(n) < O(n log n) < O(n²) < O(n³) < O(2ⁿ) < O(n!)` ### 计算技巧 - 循环体执行次数 = 复杂度。`for(i=0;i // 计算 1*1 + 1*2 + ... + n*n 的累加 // 内层循环执行次数:n*(n+1)/2 次 -> 时间复杂度 O(n^2) int sum(int n) { int total = 0; // O(1) 额外空间,空间复杂度 O(1) for (int i = 1; i <= n; i++) // 外层 n 次 for (int j = 1; j <= n; j++) // 内层 n 次 total += i * j; return total; } int main(void) { int n = 5; printf("sum(%d) = %d\n", n, sum(n)); return 0; } ``` ### Python 示例:折半(对数阶) ```python # 每次循环 i 翻倍,只需 log2(n) 次 -> 时间复杂度 O(log n) def count_steps(n: int) -> int: steps = 0 i = 1 while i < n: i = i * 2 steps += 1 return steps if __name__ == "__main__": n = 1000 print(f"n={n} 时循环次数: {count_steps(n)}") # 约 10 次 ``` ## 系列导航 | 章节 | 主题 | 复杂度主线 | | ---- | ---------------- | ------------------------------------ | | 本页 | 概述与复杂度分析 | O(1) ~ O(n!) 量级 | | 2 | [[2.顺序表]] | 随机存取 O(1),插入删除 O(n) | | 3 | [[3.单链表]] | 插入删除 O(1)(已知结点),查找 O(n) | | 4 | [[4.双链表]] | 可双向遍历,前插 O(1) | | 5 | [[5.循环链表]] | 从尾部到头 O(1),遍历终止条件特殊 | > 阅读建议:先掌握本页的复杂度分析方法,再按 2 → 5 的顺序学习线性表,重点对比「顺序存储 vs 链式存储」的取舍。