数据结构与算法:10种核心算法全解析

一、排序算法:高效数据整理的基石

1. 快速排序(Quick Sort)

作为分治思想的典范,快速排序通过选取基准值(pivot)将数组划分为左右两部分,递归处理子数组。其平均时间复杂度为O(n log n),但最坏情况下(如已排序数组)会退化至O(n²)。优化策略包括随机化基准值、三数取中法等。

  1. def quick_sort(arr):
  2. if len(arr) <= 1:
  3. return arr
  4. pivot = arr[len(arr)//2]
  5. left = [x for x in arr if x < pivot]
  6. middle = [x for x in arr if x == pivot]
  7. right = [x for x in arr if x > pivot]
  8. return quick_sort(left) + middle + quick_sort(right)

最佳实践:适用于大规模数据排序,需注意递归深度控制以避免栈溢出。

2. 归并排序(Merge Sort)

采用自底向上的分治策略,将数组拆分为最小单元后合并。时间复杂度稳定为O(n log n),但需要O(n)额外空间。

  1. def merge_sort(arr):
  2. if len(arr) <= 1:
  3. return arr
  4. mid = len(arr)//2
  5. left = merge_sort(arr[:mid])
  6. right = merge_sort(arr[mid:])
  7. return merge(left, right)
  8. def merge(left, right):
  9. result = []
  10. while left and right:
  11. if left[0] < right[0]:
  12. result.append(left.pop(0))
  13. else:
  14. result.append(right.pop(0))
  15. return result + left + right

适用场景:链表排序、外部排序(大数据量分块处理)。

3. 堆排序(Heap Sort)

基于二叉堆数据结构,通过构建最大堆实现升序排序。时间复杂度O(n log n),空间复杂度O(1)。

  1. def heapify(arr, n, i):
  2. largest = i
  3. l = 2 * i + 1
  4. r = 2 * i + 2
  5. if l < n and arr[i] < arr[l]:
  6. largest = l
  7. if r < n and arr[largest] < arr[r]:
  8. largest = r
  9. if largest != i:
  10. arr[i], arr[largest] = arr[largest], arr[i]
  11. heapify(arr, n, largest)
  12. def heap_sort(arr):
  13. n = len(arr)
  14. for i in range(n//2 - 1, -1, -1):
  15. heapify(arr, n, i)
  16. for i in range(n-1, 0, -1):
  17. arr[i], arr[0] = arr[0], arr[i]
  18. heapify(arr, i, 0)

性能优化:可结合优先队列实现动态数据排序。

二、搜索算法:精准定位目标

4. 二分查找(Binary Search)

针对有序数组,每次将搜索范围减半。时间复杂度O(log n),需注意边界条件处理。

  1. def binary_search(arr, target):
  2. left, right = 0, len(arr)-1
  3. while left <= right:
  4. mid = (left + right) // 2
  5. if arr[mid] == target:
  6. return mid
  7. elif arr[mid] < target:
  8. left = mid + 1
  9. else:
  10. right = mid - 1
  11. return -1

变种应用:查找第一个/最后一个等于目标的元素、旋转数组搜索。

5. 深度优先搜索(DFS)

通过递归或栈实现图的遍历,适用于连通性检测、拓扑排序等场景。

  1. def dfs(graph, start, visited=None):
  2. if visited is None:
  3. visited = set()
  4. visited.add(start)
  5. print(start)
  6. for neighbor in graph[start]:
  7. if neighbor not in visited:
  8. dfs(graph, neighbor, visited)

注意事项:需处理循环引用,避免无限递归。

6. 广度优先搜索(BFS)

使用队列实现层级遍历,常用于最短路径问题(无权图)。

  1. from collections import deque
  2. def bfs(graph, start):
  3. visited = set()
  4. queue = deque([start])
  5. visited.add(start)
  6. while queue:
  7. vertex = queue.popleft()
  8. print(vertex)
  9. for neighbor in graph[vertex]:
  10. if neighbor not in visited:
  11. visited.add(neighbor)
  12. queue.append(neighbor)

优化方向:双向BFS可显著提升搜索效率。

三、图论算法:复杂网络解析

7. Dijkstra算法

解决带权有向图的单源最短路径问题,使用优先队列优化后时间复杂度为O((V+E) log V)。

  1. import heapq
  2. def dijkstra(graph, start):
  3. distances = {node: float('infinity') for node in graph}
  4. distances[start] = 0
  5. heap = [(0, start)]
  6. while heap:
  7. current_dist, current_node = heapq.heappop(heap)
  8. if current_dist > distances[current_node]:
  9. continue
  10. for neighbor, weight in graph[current_node].items():
  11. distance = current_dist + weight
  12. if distance < distances[neighbor]:
  13. distances[neighbor] = distance
  14. heapq.heappush(heap, (distance, neighbor))
  15. return distances

限制条件:不适用于负权边图。

8. 最小生成树(Prim/Kruskal)

Prim算法通过贪心策略逐步扩展生成树,Kruskal算法则按边权排序后合并。

  1. # Kruskal算法示例
  2. def find(parent, i):
  3. if parent[i] == i:
  4. return i
  5. return find(parent, parent[i])
  6. def kruskal(graph):
  7. result = []
  8. i, e = 0, 0
  9. edges = []
  10. for u in graph:
  11. for v, weight in graph[u].items():
  12. edges.append((u, v, weight))
  13. edges.sort(key=lambda x: x[2])
  14. parent = {}
  15. for node in graph:
  16. parent[node] = node
  17. while e < len(graph)-1 and i < len(edges):
  18. u, v, w = edges[i]
  19. i += 1
  20. x = find(parent, u)
  21. y = find(parent, v)
  22. if x != y:
  23. e += 1
  24. result.append((u, v, w))
  25. parent[x] = y
  26. return result

应用场景:网络设计、集群通信优化。

四、动态规划与贪心策略

9. 动态规划(DP)

通过状态转移方程解决重叠子问题,如斐波那契数列计算:

  1. def fib_dp(n):
  2. dp = [0]*(n+1)
  3. dp[1] = 1
  4. for i in range(2, n+1):
  5. dp[i] = dp[i-1] + dp[i-2]
  6. return dp[n]

优化技巧:空间复杂度可从O(n)降至O(1)(仅保留前两个状态)。

10. 贪心算法

每一步选择局部最优解,如活动选择问题:

  1. def activity_selection(start, finish):
  2. activities = sorted(zip(start, finish), key=lambda x: x[1])
  3. selected = [activities[0]]
  4. last_finish = activities[0][1]
  5. for s, f in activities[1:]:
  6. if s >= last_finish:
  7. selected.append((s, f))
  8. last_finish = f
  9. return selected

适用条件:问题具有贪心选择性质(如最优子结构)。

五、算法选择与优化策略

  1. 数据规模分析:小规模数据优先选择简单算法(如插入排序),大规模数据需考虑时间复杂度。
  2. 空间权衡:归并排序需额外空间,堆排序则原地操作。
  3. 稳定性要求:归并排序稳定,快速排序不稳定。
  4. 并行化潜力:归并排序、矩阵乘法等可拆分为独立子任务。

开发者可通过LeetCode等平台系统练习算法思维,结合具体业务场景(如推荐系统中的相似度计算、日志分析中的Top K查询)灵活应用。掌握这些核心算法,将为解决复杂技术问题提供坚实的理论基础。