python数据结构与算法(基础)
先补充一些基础概念,下一篇再讲数据结构和算法
数据结构与算法
一、基础概念
1.1 数据结构(Data Structure)
定义:
数据结构是数据元素之间的逻辑关系以及在计算机中的存储表示方式,是组织、管理和高效访问数据的方式。
核心三要素:
- 逻辑结构:数据元素之间的抽象关系(如线性、树形、图状、集合)
- 存储结构:数据在计算机中的物理存储方式(如顺序、链式、索引、散列)
- 运算集合:可在数据结构上执行的操作(如插入、删除、查找、遍历)
一句话总结:数据结构 = 逻辑结构 + 存储结构 + 操作集合
常见数据类型分类:
类型 | 示例 |
---|---|
基本类型 | 整数、浮点数、布尔值、字符 |
复合类型 | 列表、元组、集合、字典 |
抽象数据类型(ADT) | 栈、队列、树、图等 |
1.2 算法(Algorithm)
定义:
算法是解决特定问题的一组明确、有限的步骤序列,是对解决问题方法的精确描述。
核心关系:
程序 = 数据结构 + 算法
这是著名计算机科学家 Niklaus Wirth 提出的经典公式,强调了程序设计的两大基石。
影响效率的关键因素:
- 相同数据 + 不同算法 → 执行效率差异显著
- 相同问题 + 不同数据结构 → 可能导致算法选择不同
- 合理搭配数据结构与算法 → 显著提升程序性能
二、算法特性与独立性
2.1 算法的独立性
算法是一种独立于编程语言和具体实现的思维模式。其本质是“解决问题的策略”,而非代码本身。
示例说明:
以“穷举法”为例:
- 思想:枚举所有可能解并验证是否满足条件
- 实现:可用 C、Python、Java 等任意语言实现
- 结论:算法思想具有普适性,语言只是表达工具
启示:学习算法重在理解其设计思想,而非死记代码。
2.2 算法的五大基本特性
特性 | 说明 | 示例/反例 |
---|---|---|
输入性 | 有零个或多个输入 | 如排序算法需输入数组 |
输出性 | 至少有一个输出 | 必须返回结果或状态 |
有穷性 | 在有限步骤内结束 | 死循环违反此特性 |
确定性 | 每步操作无歧义 | 相同输入始终得相同输出 |
可行性 | 每一步都可执行且在合理时间内完成 | “瞬间传送”不可行 |
注意:违反任一特性,则不能称为有效算法。
三、时间复杂度深度解析
3.1 时间效率的本质
执行时间模型:
总执行时间 T(n) = Σ(每步操作耗时 × 执行次数)
分析原则:
- 假设每条基本操作(如赋值、比较、算术运算)耗时为常量
- 关注操作次数随输入规模 n 的增长趋势,而非绝对运行时间
- 忽略硬件、语言等外部因素干扰
3.2 时间复杂度概念
定义:
时间复杂度描述算法执行时间随输入规模 n 增长的渐进行为,反映算法的“量级”或“阶”。
经典示例:
求解满足 a + b + c = 1000 且 a^2 + b^2 = c^2 的整数解(a,b,c ≥ 0)
若采用三重嵌套循环遍历:
for a in range(1001):for b in range(1001):for c in range(1001):if a + b + c == 1000 and a*a + b*b == c*c:print(a, b, c)
- 操作次数 ≈ 1001^3
- 设 n = 1000 ,则 T(n) \approx n^3
- 时间复杂度为 O(n^3)
3.3 大O表示法(Big-O Notation)
定义:
大O表示法用于描述函数增长的上界,即最坏情况下的增长速率。
数学定义:
若存在正常数a和b,使得当n>=b时,有T(n)<=a*f(n),则称T(n)= O(f(n))。
简化规则:
- 忽略常数项(如 O(3n^2) \rightarrow O(n^2) )
- 忽略低阶项(如 O(n^2 + n) \rightarrow O(n^2) )
- 保留最高次项
3.4 时间复杂度计算规则
结构类型 | 计算方法 | 示例 |
---|---|---|
基本操作 | O(1) | 赋值、访问数组元素 |
顺序结构 | 各部分相加,取最大值 | O(n) + O(1) = O(n) |
循环结构 | 循环次数 × 单次循环复杂度 | for i in range(n): 内部 O(1) → O(n) |
嵌套循环 | 各层复杂度相乘 | 两层 n 次循环 → O(n²) |
分支结构 | 取各分支中最复杂者 | if A: O(n); else: O(1) → O(n) |
递归调用 | 使用递推公式或主定理分析 | 归并排序: T(n) = 2T(n/2) + O(n) → O(n log n) |
3.5 最优、最坏与平均时间复杂度
类型 | 定义 | 特点 | 实际意义 |
---|---|---|---|
最优时间复杂度 | 最理想情况下执行时间 | 过于乐观,参考价值低 | 一般不作为评估依据 |
最坏时间复杂度 | 最恶劣情况下执行时间 | 提供性能保障 | 重点评估指标 |
平均时间复杂度 | 所有可能输入下的期望时间 | 更全面但难计算 | 常用于概率分析 |
实践建议:优先关注最坏时间复杂度,确保系统在极端情况下的稳定性。
四、常见时间复杂度对比
渐进增长率排序(由优到劣)
时间复杂度 | 名称 | 增长速度 | 典型算法示例 |
---|---|---|---|
O(1) | 常数阶 | 恒定 | 数组随机访问、哈希表查找 |
O(\log n) | 对数阶 | 极慢 | 二分查找、平衡二叉树操作 |
O(\sqrt{n}) | 平方根阶 | 较慢 | 素数判断(试除法) |
O(n) | 线性阶 | 线性增长 | 线性搜索、数组遍历 |
O(n \log n) | 线性对数阶 | 略快于线性 | 快速排序、归并排序、堆排序 |
O(n^2) | 平方阶 | 明显变慢 | 冒泡排序、选择排序、简单动态规划 |
O(n^3) | 立方阶 | 大数据不可行 | Floyd算法、三重循环 |
O(2^n) | 指数阶 | 急剧爆炸 | 子集枚举、暴力破解 |
O(n!) | 阶乘阶 | 极端缓慢 | 旅行商问题(暴力法) |
趋势图示意:
执行时间 ↑|| O(n!)| O(2ⁿ)| O(n³)| O(n²)| O(n log n)| O(n)| O(√n)| O(log n)|O(1)+----------------------------------→ 数据规模 n
观察结论:当 n > 1000 时,O(n^2) 以上算法往往难以接受; O(n \log n) 是大规模数据处理的“分水岭”。
五、空间复杂度分析
5.1 核心概念
定义:
空间复杂度指算法在运行过程中临时占用的存储空间大小,通常也用大O表示法描述。
关键点:
- 不包括输入数据本身所占空间
- 主要考虑辅助变量、递归调用栈、临时数据结构等
- 与时间复杂度类似,关注增长趋势
5.2 常见空间复杂度
空间复杂度 | 描述 | 典型场景 |
---|---|---|
O(1) | 常量空间 | 原地排序(如冒泡排序)、简单变量计算 |
O(\log n) | 对数空间 | 递归深度为 \log n (如快速排序平均情况) |
O(n) | 线性空间 | 复制数组、哈希表、BFS队列 |
O(n \log n) | 线性对数空间 | 某些分治算法的中间结构 |
O(n^2) | 平方空间 | 邻接矩阵存储图、DP二维表 |
5.3 时间与空间的权衡(Trade-off)
策略 | 说明 | 示例 |
---|---|---|
以空间换时间 | 增加内存使用,减少计算时间 | 哈希表加速查找、动态规划缓存结果 |
以时间换空间 | 减少内存占用,增加计算时间 | 不使用缓存的递归、流式处理大数据 |
💡 设计原则:根据应用场景权衡。实时系统重时间,嵌入式系统重空间。
六、实际应用建议
场景 | 推荐策略 |
---|---|
小规模数据(n < 100) | 优先选择实现简单的算法(如冒泡排序),即使复杂度为 O(n^2) |
中等规模数据(n ~ 10⁴) | 选择 O(n^2) 以下算法,避免嵌套循环 |
大规模数据(n > 10⁵) | 必须使用 O(n \log n) 或更优算法(如快排、堆排) |
内存受限环境 | 优先考虑空间复杂度,避免递归过深或大数组 |
实时系统(如高频交易) | 严格控制最坏时间复杂度,避免不可预测延迟 |
频繁查询场景 | 考虑预处理+索引(如哈希、树结构)提升查询效率 |
关键结论:
没有“最好”的算法,只有“最合适”的算法。
实际开发中应结合:数据规模、性能要求、资源限制、实现成本 综合决策。
七、附录:常见操作复杂度速查表
操作 | 数组 | 链表 | 哈希表 | 二叉搜索树(平衡) |
---|---|---|---|---|
查找 | O(n) / O(1)* | O(n) | O(1) 平均 | O(log n) |
插入(头) | O(n) | O(1) | - | - |
插入(任意) | O(n) | O(n) | O(1) | O(log n) |
删除 | O(n) | O(n) | O(1) | O(log n) |
遍历 | O(n) | O(n) | O(n) | O(n) |
注:数组按索引访问为 O(1),按值查找为 O(n)