🐸 青蛙编程网
首页 / Python / 算法导论 / 6. 综合练习

综合练习:经典题三连

这是什么:三道经典算法题,把查找、排序、递归、动态规划一次性串起来考。

为什么用:查找、排序、递归、动态规划都过了一遍,是时候综合验收了。学得越多越容易混,只有真上手做题才分得清什么时候用哪一招。

如何用:本章先猜数字(二分查找)、再排序排成绩单、最后用动态规划算爬楼梯,每题先自己想 5 分钟。

第 1 题:二分查找猜数字

经典游戏:我心里想一个 1~100 的数,你来猜,我只说"大了"或"小了"。这回换电脑当猜的人——用二分查找的思路,永远猜剩余范围的中间:

target = 66                 # 心里想好的答案(1~100)
low, high = 1, 100          # 二分查找的左右指针
count = 0

while low <= high:
    mid = (low + high) // 2
    count += 1
    if mid == target:
        print(f"第 {count} 次猜 {mid},猜中啦!")
        break
    elif mid < target:
        print(f"第 {count} 次猜 {mid},太小")
        low = mid + 1
    else:
        print(f"第 {count} 次猜 {mid},太大")
        high = mid - 1

你会看到:

第 1 次猜 50,太小
第 2 次猜 75,太大
第 3 次猜 62,太小
第 4 次猜 68,太大
第 5 次猜 65,太小
第 6 次猜 66,猜中啦!

1~100 最多 7 次必中;把范围换成 1~100 万?也只要 20 次。这就是二分查找的底气。

第 2 题:冒泡排序成绩单

四名同学的成绩单是乱序的,用第 3 章手写的冒泡排序,按分数从高到低排好:

scores = [("阿呆", 76), ("阿瓜", 92), ("小青蛙", 88), ("豆豆", 64)]

n = len(scores)
for i in range(n - 1):
    for j in range(n - 1 - i):
        if scores[j][1] < scores[j + 1][1]:    # 分数高的往前冒
            scores[j], scores[j + 1] = scores[j + 1], scores[j]

for name, score in scores:
    print(f"{name} {score} 分")

你会看到:

阿瓜 92 分
小青蛙 88 分
阿呆 76 分
豆豆 64 分

要点就一处:比较的是 scores[j][1](元组里的分数),交换的却是整个元组。顺便一提,正式项目里一行 sorted(scores, key=lambda s: s[1], reverse=True) 也能达到同样效果。

第 3 题:爬楼梯(动态规划招牌题)

楼梯有 n 级,每次能上 1 级或 2 级,有多少种走法?想通了它就是斐波那契:到第 n 级的最后一步,要么从 n-1 级跨 1 级,要么从 n-2 级跨 2 级,所以 ways(n) = ways(n-1) + ways(n-2)。加上 @lru_cache 记住答案,50 级也瞬间出结果:

from functools import lru_cache

@lru_cache(maxsize=None)
def ways(n):
    if n <= 2:
        return n                # 1 级有 1 种走法,2 级有 2 种
    return ways(n - 1) + ways(n - 2)

print("爬 10 级楼梯有", ways(10), "种走法")
print("爬 30 级楼梯有", ways(30), "种走法")
print("爬 50 级楼梯有", ways(50), "种走法")

你会看到:

爬 10 级楼梯有 89 种走法
爬 30 级楼梯有 1346269 种走法
爬 50 级楼梯有 20365011074 种走法

试试去掉装饰器再算 ways(50)——等到天荒地老也算不完。一道题串起"递归 + 动态规划",这就是内功。

算法导论毕业 🎉 内功有了,招式继续:设计模式 教你把代码组织得更好维护——回首页看看下一个目标

📥 下载本章代码

ZIP 内含本章全部代码文件与说明,解压即用,与教程内容同步生成。