第三部分:算法与例题——解决问题的艺术


文档摘要

第三部分:算法与例题——解决问题的艺术 词条7:排序与搜索算法 官方解释 排序算法将数据按特定顺序排列,搜索算法在数据集中查找特定元素。这是计算机科学中最基础、最重要的算法类别。 常见排序算法: 冒泡排序:简单但低效,O(n²) 选择排序:每次选择最小元素,O(n²) 插入排序:像整理扑克牌,O(n²) 归并排序:分治策略,O(n log n) 快速排序:分治策略,平均O(n log n) 常见搜索算法: 线性搜索:逐个检查,O(n) 二分搜索:要求有序,O(log n) 兔狲老师解释 排序就像'整理书架',搜索就像'在书架上找书'。

第三部分:算法与例题——解决问题的艺术

词条7:排序与搜索算法

官方解释

排序算法将数据按特定顺序排列,搜索算法在数据集中查找特定元素。这是计算机科学中最基础、最重要的算法类别。

常见排序算法:

  • 冒泡排序:简单但低效,O(n²)
  • 选择排序:每次选择最小元素,O(n²)
  • 插入排序:像整理扑克牌,O(n²)
  • 归并排序:分治策略,O(n log n)
  • 快速排序:分治策略,平均O(n log n)

常见搜索算法:

  • 线性搜索:逐个检查,O(n)
  • 二分搜索:要求有序,O(log n)

兔狲老师解释

排序就像'整理书架',搜索就像'在书架上找书'。

小小猪的比喻:

  • 冒泡排序:像气泡上浮,小的往上冒
  • 快速排序:选一个'基准',小的放左边,大的放右边
  • 二分搜索:每次排除一半,快速缩小范围

算法复杂度:

  • 时间复杂度:算法执行时间随输入规模的增长
  • 空间复杂度:算法需要的内存空间

思考题13:动手题

问题:实现几种排序和搜索算法:

# 1. 排序算法实现 def bubble_sort(arr): """冒泡排序""" n = len(arr) for i in range(n): # 最后i个元素已经排好序 for j in range(0, n - i - 1): if arr[j] > arr[j + 1]: arr[j], arr[j + 1] = arr[j + 1], arr[j] return arr def selection_sort(arr): """选择排序""" n = len(arr) for i in range(n): # 找到最小元素的索引 min_idx = i for j in range(i + 1, n): if arr[j] < arr[min_idx]: min_idx = j # 交换 arr[i], arr[min_idx] = arr[min_idx], arr[i] return arr def insertion_sort(arr): """插入排序""" for i in range(1, len(arr)): key = arr[i] j = i - 1 # 将比key大的元素向右移动 while j >= 0 and key < arr[j]: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr def merge_sort(arr): """归并排序""" if len(arr) <= 1: return arr # 分割 mid = len(arr) // 2 left = merge_sort(arr[:mid]) right = merge_sort(arr[mid:]) # 合并 return merge(left, right) def merge(left, right): """合并两个有序数组""" result = [] i = j = 0 while i < len(left) and j < len(right): if left[i] < right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result def quick_sort(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 quick_sort(left) + middle + quick_sort(right) # 2. 搜索算法实现 def linear_search(arr, target): """线性搜索""" for i, value in enumerate(arr): if value == target: return i return -1 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 # 测试排序算法 print("排序算法测试:") test_array = [64, 34, 25, 12, 22, 11, 90] print("原始数组:", test_array) print("\n冒泡排序:", bubble_sort(test_array.copy())) print("选择排序:", selection_sort(test_array.copy())) print("插入排序:", insertion_sort(test_array.copy())) print("归并排序:", merge_sort(test_array.copy())) print("快速排序:", quick_sort(test_array.copy())) print("\n" + "="*50) # 测试搜索算法 print("搜索算法测试:") sorted_array = [11, 12, 22, 25, 34, 64, 90] print("有序数组:", sorted_array) target = 25 print(f"\n线性搜索 {target}: 索引 {linear_search(sorted_array, target)}") print(f"二分搜索 {target}: 索引 {binary_search(sorted_array, target)}") target = 100 print(f"\n线性搜索 {target}: 索引 {linear_search(sorted_array, target)}") print(f"二分搜索 {target}: 索引 {binary_search(sorted_array, target)}")

思考题14:动脑题

问题:如何选择合适的排序算法?

思考方向:

  • 数据规模小时,为什么插入排序可能比快速排序快?
  • 什么时候应该用稳定排序?(稳定排序:相等元素的相对顺序不变)
  • 内存受限时应该选择什么排序算法?
  • 数据几乎有序时,什么排序算法最快?
  • 在实际应用中,Python的sorted()函数用什么算法?

词条8:动态规划与贪心算法

官方解释

动态规划(Dynamic Programming)通过将复杂问题分解为重叠子问题来求解,保存子问题的解避免重复计算。贪心算法(Greedy Algorithm)每一步都选择当前最优解,希望最终得到全局最优解。

动态规划特点:

  • 最优子结构:问题的最优解包含子问题的最优解
  • 重叠子问题:子问题会被重复计算
  • 记忆化:保存已计算子问题的结果

贪心算法特点:

  • 局部最优选择
  • 不保证全局最优
  • 通常更简单高效

兔狲老师解释

动态规划就像'建备忘录',贪心算法就像'眼前利益最大化'。

小小猪的比喻:

  • 动态规划:爬楼梯问题,记住每步的结果
  • 贪心算法:找零钱问题,每次选最大面额

适用场景:

  • 动态规划:背包问题、最长公共子序列、最短路径
  • 贪心算法:霍夫曼编码、最小生成树、活动选择

思考题15:动手题

问题:实现动态规划和贪心算法解决经典问题:

# 1. 动态规划:斐波那契数列 def fibonacci_dp(n): """动态规划求斐波那契数列""" if n <= 1: return n dp = [0] * (n + 1) dp[1] = 1 for i in range(2, n + 1): dp[i] = dp[i - 1] + dp[i - 2] return dp[n] def fibonacci_memo(n, memo=None): """记忆化递归求斐波那契数列""" if memo is None: memo = {} if n in memo: return memo[n] if n <= 1: return n memo[n] = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo) return memo[n] # 2. 动态规划:0-1背包问题 def knapsack_01(weights, values, capacity): """0-1背包问题动态规划""" n = len(weights) # dp[i][w] 表示前i个物品,容量为w时的最大价值 dp = [[0] * (capacity + 1) for _ in range(n + 1)] for i in range(1, n + 1): for w in range(1, capacity + 1): if weights[i - 1] <= w: # 选择:放入或不放入 dp[i][w] = max( dp[i - 1][w], # 不放入 dp[i - 1][w - weights[i - 1]] + values[i - 1] # 放入 ) else: dp[i][w] = dp[i - 1][w] # 放不下 # 回溯找出选择的物品 selected = [] w = capacity for i in range(n, 0, -1): if dp[i][w] != dp[i - 1][w]: selected.append(i - 1) w -= weights[i - 1] selected.reverse() return dp[n][capacity], selected # 3. 贪心算法:找零钱问题 def coin_change_greedy(coins, amount): """贪心算法找零钱(硬币面额递减)""" coins.sort(reverse=True) # 从大到小排序 result = [] for coin in coins: while amount >= coin: amount -= coin result.append(coin) return result if amount == 0 else None # 4. 贪心算法:活动选择问题 def activity_selection(start_times, finish_times): """贪心算法选择最多互不冲突的活动""" # 按结束时间排序 activities = sorted(zip(start_times, finish_times), key=lambda x: x[1]) selected = [] last_finish = 0 for start, finish in activities: if start >= last_finish: selected.append((start, finish)) last_finish = finish return selected # 测试动态规划 print("动态规划测试:") print("斐波那契数列(动态规划):") for i in range(10): print(f"F({i}) = {fibonacci_dp(i)}", end=" ") print() print("\n斐波那契数列(记忆化递归):") for i in range(10): print(f"F({i}) = {fibonacci_memo(i)}", end=" ") print() print("\n" + "="*50) # 测试0-1背包问题 print("0-1背包问题测试:") weights = [2, 3, 4, 5] values = [3, 4, 5, 6] capacity = 8 max_value, selected_items = knapsack_01(weights, values, capacity) print(f"物品重量: {weights}") print(f"物品价值: {values}") print(f"背包容量: {capacity}") print(f"最大价值: {max_value}") print(f"选择的物品索引: {selected_items}") print(f"选择的物品重量: {[weights[i] for i in selected_items]}") print(f"选择的物品价值: {[values[i] for i in selected_items]}") print("\n" + "="*50) # 测试贪心算法 print("贪心算法测试:") print("找零钱问题:") coins = [1, 5, 10, 25] amount = 63 change = coin_change_greedy(coins, amount) print(f"硬币面额: {coins}") print(f"金额: {amount}") print(f"找零方案: {change}") print(f"硬币数量: {len(change)}") print("\n活动选择问题:") start_times = [1, 3, 0, 5, 8, 5] finish_times = [2, 4, 6, 7, 9, 9] selected_activities = activity_selection(start_times, finish_times) print(f"开始时间: {start_times}") print(f"结束时间: {finish_times}") print(f"选择的活动: {selected_activities}") print(f"活动数量: {len(selected_activities)}")

思考题16:动脑题

问题:动态规划和贪心算法各有什么优缺点?如何选择?

思考方向:

  • 什么情况下贪心算法能得到最优解?
  • 动态规划的时间复杂度和空间复杂度如何?
  • 记忆化搜索和自底向上动态规划有什么区别?
  • 在实际问题中,如何判断是否具有最优子结构?
  • 分治算法和动态规划有什么区别?

发布者: 作者: 转发
评论区 (0)
U