文章目录
数据结构与算法的详细介绍,涵盖核心概念、实现原理、应用场景及代码示例:
1. 数据结构详解
-
数据结构:存储和组织数据的方式,决定了数据的逻辑关系及操作效率(如增删查改)。
-
算法:解决问题的步骤,用于操作数据并得到结果。
-
关系:数据结构是算法的“工具”,算法通过高效使用数据结构完成任务。例如,图结构上的最短路径算法(如Dijkstra)依赖图的存储方式(邻接矩阵或邻接表)。
1.1 线性结构
-
数组 (Array)
- 实现:连续内存空间,存储相同类型元素。
- 操作:
- 随机访问:通过下标直接定位(
arr[i]
),时间复杂度 O(1)。 - 插入/删除:需移动后续元素,时间复杂度 O(n)。
- 随机访问:通过下标直接定位(
- 应用:高频随机访问场景(如缓存、矩阵运算)。
-
链表 (Linked List)
- 类型:
- 单链表:节点包含值和指向下一个节点的指针。
- 双链表:节点包含指向前后节点的指针,支持双向遍历。
- 操作:
- 插入/删除:修改指针指向即可,时间复杂度 O(1)(但需先遍历到目标位置)。
- 随机访问:需从头遍历,时间复杂度 O(n)。
- 应用:内存池管理、LRU缓存(结合哈希表)。
- 类型:
-
栈 (Stack)
- 实现:可通过数组或链表实现(数组更节省内存,链表动态扩容)。
- 核心操作:
push()
:入栈,时间复杂度 O(1)。pop()
:出栈,时间复杂度 O(1)。
- 应用:函数调用栈、表达式求值(中缀转后缀)、撤销操作。
-
队列 (Queue)
- 类型:
- 普通队列:FIFO,如任务队列。
- 循环队列:利用数组首尾相连,避免空间浪费。
- 优先队列:基于堆实现,按优先级出队(如 Dijkstra 算法)。
- 操作:
enqueue()
和dequeue()
,时间复杂度 O(1)。
- 应用:BFS 遍历、消息队列、滑动窗口算法。
- 类型:
1.2 非线性结构
-
树 (Tree)
-
二叉树 (Binary Tree)
- 性质:每个节点最多两个子节点。
- 遍历方式:
- 前序:根 → 左 → 右(用于复制树结构)。
- 中序:左 → 根 → 右(二叉搜索树有序输出)。
- 后序:左 → 右 → 根(计算目录大小)。
- 二叉搜索树 (BST):左子树值 < 根 < 右子树值,查找复杂度 O(log n)(退化为链表时 O(n))。
-
平衡树
- AVL 树:通过旋转(左旋/右旋)保持平衡,左右子树高度差 ≤ 1。
- 红黑树:近似平衡,通过颜色标记和旋转保证插入/删除高效(Java TreeMap、C++ map)。
-
堆 (Heap)
- 实现:完全二叉树,数组存储。
- 类型:
- 最大堆:父节点值 ≥ 子节点(用于求 Top K)。
- 最小堆:父节点值 ≤ 子节点(用于优先队列)。
- 操作:插入和删除的时间复杂度 O(log n)。
-
B树/B+树
- B树:多路平衡搜索树,每个节点存储多个键和子节点指针(适合磁盘存储,减少IO次数)。
- B+树:叶子节点通过指针连接形成有序链表(数据库索引如 MySQL)。
-
-
图 (Graph)
- 存储方式:
- 邻接矩阵:二维数组表示边,适合稠密图,空间复杂度 O(V²)。
- 邻接表:链表数组表示边,适合稀疏图,空间复杂度 O(V + E)。
- 算法:
- 最短路径:Dijkstra(单源无负权边)、Floyd-Warshall(多源)。
- 最小生成树:Prim(稠密图)、Kruskal(稀疏图)。
- 存储方式:
-
哈希表 (Hash Table)
- 原理:通过哈希函数将键映射到桶地址。
- 冲突解决:
- 开放寻址法:线性探测、二次探测。
- 链地址法:每个桶存储链表(Java HashMap)。
- 负载因子:哈希表填充程度,通常扩容阈值为 0.75。
2. 算法详解
2.1 排序算法
-
快速排序
def quicksort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr)//2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quicksort(left) + middle + quicksort(right)
- 核心思想:分治,选择一个基准值(pivot),将数组分为三部分。
- 时间复杂度:平均 O(n log n),最坏 O(n²)(可通过随机选择pivot优化)。
-
归并排序
- 核心思想:分治,将数组递归拆分为子数组,合并时按序合并。
- 稳定性:稳定排序,适合对象排序(如按多个字段排序)。
- 时间复杂度:始终 O(n log n)。
-
堆排序
- 步骤:
- 构建最大堆。
- 交换堆顶与末尾元素,堆大小减一,调整堆。
- 时间复杂度:O(n log n),适合大数据量但内存有限的情况。
- 步骤:
2.2 搜索算法
-
二分查找
def binary_search(arr, target): left, right = 0, len(arr)-1 while left <= right: mid = (left + right) // 2 if arr[mid] == target: return mid elif arr[mid] < target: left = mid + 1 else: right = mid - 1 return -1
- 前提:数组有序。
- 时间复杂度:O(log n)。
-
广度优先搜索 (BFS)
- 实现:队列辅助,逐层遍历。
- 应用:最短路径(无权图)、社交网络中的好友推荐。
-
深度优先搜索 (DFS)
- 实现:递归或栈辅助,回溯法常用。
- 应用:迷宫求解、拓扑排序。
2.3 动态规划 (DP)
- 核心思想:将问题分解为重叠子问题,通过记忆化(Memoization)或递推表格避免重复计算。
- 典型问题:
- 斐波那契数列:
dp[i] = dp[i-1] + dp[i-2]
。 - 背包问题:
- 0-1背包:
dp[i][w] = max(dp[i-1][w], dp[i-1][w - wt[i]] + val[i])
。 - 完全背包:允许重复选择物品。
- 0-1背包:
- 最长公共子序列 (LCS):二维DP表比较字符匹配。
- 斐波那契数列:
2.4 贪心算法
- 核心思想:每一步选择当前最优解,希望全局最优。
- 适用条件:问题具有贪心选择性质(局部最优能导向全局最优)。
- 典型问题:
- 霍夫曼编码:用最短编码表示高频字符。
- 区间调度:选择最多不重叠区间。
3. 复杂度分析
- 时间与空间复杂度权衡:
- 空间换时间:哈希表加速查询、动态规划中的备忘录。
- 时间换空间:某些递归算法(如尾递归优化)。
- 均摊分析:如动态数组(ArrayList)的扩容操作均摊时间复杂度为 O(1)。
4. 实际应用案例
- 数据库索引:B+树减少磁盘IO次数。
- 推荐系统:协同过滤算法(基于图或矩阵分解)。
- 编译器:语法分析树(AST)、符号表(哈希表)。
- 操作系统:进程调度(优先队列)、文件系统(B树)。
5. 学习路径与资源
- 入门:《大话数据结构》、《算法图解》。
- 进阶:《算法导论》、《编程珠玑》。
- 刷题平台:LeetCode(按标签分类练习)、HackerRank(侧重算法基础)。
- 开源项目:Redis(跳表实现有序集合)、Linux内核(红黑树管理内存)。
掌握数据结构与算法的核心在于理解其设计思想,并通过大量实践优化代码效率。例如,面对海量数据时,需权衡时间与空间复杂度;在系统设计中,需选择适合场景的数据结构(如 Redis 选择跳表而非平衡树)。