Skip to content

12.5 六问诊断法:面对陌生问题如何系统推进

核心问题:面对陌生问题如何系统推进?

直觉:像飞行员起飞前跑清单——逐项检查,不漏环节,不靠"灵感"。


一、问题情境:War Story——从迷茫到六问

想象你是一个新手程序员,面试官给你一个问题:

"给定一个字符串,找出最长回文子串。"

你脑袋一片空白:这是什么问题?该用什么方法?

常见错误反应

  1. 等待灵感:盯着屏幕发呆,期望"突然想到"解法
  2. 乱试方法:想到一个方法就直接写代码,不管是否适用
  3. 查搜索引擎:直接搜答案,不理解原理

正确反应:像飞行员一样,跑一个清单——六问清单


二、直觉引入:试飞员心态

2.1 为什么需要六问?

问题:新手面对陌生问题时,容易:

  • 遗漏关键信息(没看清约束条件)
  • 选错范式(没判断清楚就用方法)
  • 跳过验证(写完代码不测试)

解决:一个系统化的"检查清单",强制你走完关键步骤。

2.2 试飞员心态

飞行员起飞前不会靠"感觉",而是逐项执行清单:

起飞前检查清单:
✅ 燃油量够吗?
✅ 仪表盘正常吗?
✅ 天气条件允许吗?
✅ 襟翼位置正确吗?
...

算法设计也一样

六问诊断清单:
Q1: 我真的理解问题吗?
Q2: 有没有简单正确的算法?
Q3: 我的问题在已知目录中吗?
Q4: 特殊情况是否可解?
Q5: 哪种范式最像?
Q6: 还是想不出怎么办?

2.3 六问清单详解

问题目的关键词典型输出
Q1理解问题理解输入输出约束明确
Q2简单基线基线暴力解法 + 瓶颈分析
Q3查目录目录问题类型匹配
Q4特殊情况特殊化简化版本启发
Q5范式匹配范式候选方案 + 复杂度比较
Q6循环修正迭代反例测试 + 重新建模

三、算法定义:六问诊断法形式化

3.1 六问流程图

输入:问题描述

┌──────────────────────┐
│ Q1: 理解问题         │
│ 输入?输出?约束?    │
└──────────────────────┘

┌──────────────────────┐
│ Q2: 简单算法         │
│ 暴力基线 + 瓶颈       │
└──────────────────────┘

┌──────────────────────┐
│ Q3: 查目录           │
│ 匹配问题类型          │
└──────────────────────┘

┌──────────────────────┐
│ Q4: 特殊情况         │
│ 参数固定、规模缩小    │
└──────────────────────┘

┌──────────────────────┐
│ Q5: 范式匹配         │
│ 候选方案 + 复杂度比较 │
└──────────────────────┘

┌──────────────────────┐
│ Q6: 循环修正         │
│ 反例测试、重新建模    │
└──────────────────────┘

输出:推荐方案 + 待确认点

3.2 六问详解

Q1:我真的理解问题吗?

检查清单

Q1 检查:
├── 输入是什么?(类型、规模、格式)
├── 输出是什么?(形式、要求)
├── 约束是什么?(时间、空间、正确性)
├── 有小例子吗?(手动计算验证)
└── 有等价表述吗?(重述问题)

常见错误

  • 混淆输入输出
  • 忽略隐式约束
  • 未构造小例子

Q2:有没有简单正确的算法?

检查清单

Q2 检查:
├── 暴力解法是什么?(枚举所有可能性)
├── 暴力解法的复杂度?
├── 暴力解法慢在哪里?
└── 有启发式基线吗?

意义

  • 验证问题理解是否正确
  • 作为正确性对照
  • 发现瓶颈所在

Q3:我的问题在已知目录中吗?

检查清单

Q3 检查:
├── 能否映射到已知问题?
├── 是否是已知问题的变体?
└── 是否组合多个已知问题?

已知问题目录:
├── 排序问题(Ch3)
├── 查找问题(Ch3)
├── 图问题(Ch7)
│   ├── 遍历(BFS/DFS)
│   ├── 最短路径
│   ├── MST
│   ├── 拓扑排序
│   └── 流问题
├── 组合搜索(Ch5回溯)
├── 优化问题(Ch6 DP, Ch7贪心)
├── NP完全问题(Ch8)
├── 分布式问题(Ch9)
├── 流式问题(Ch10)
└── 检索融合问题(Ch11)

Q4:特殊情况是否可解?

检查清单

Q4 检查:
├── 固定一个参数,问题是否变简单?
├── 缩小规模,能否看清结构?
├── 删掉一个约束,能否求解?
└── 特殊输入(有序、空、单元素)?

意义

  • 发现问题的隐藏结构
  • 获得启发和洞察
  • 为推广做准备

Q5:哪种范式最像?

检查清单

Q5 检查:
├── 能分解? → 分治(子问题独立)
├── 能贪心? → 需证明无后效性
├── 有重叠子问题? → DP
├── 无有效分解? → 回溯/搜索
├── NP完全? → 近似/启发式
└── 规模极大? → 分布式/流式

对比方法:
├── 每种范式给出一个候选方案
├── 分析各方案的复杂度
└── 选择最优适配

Q6:还是想不出怎么办?

检查清单

Q6 检查:
├── 回到问题定义,重新审视
├── 构造反例,检验假设
├── 查阅文献/教程
├── 请教专家或同伴
└── 让Agent生成候选方案,逐一审查

Agent协作模式:
├── 输入问题描述
├── Agent走六问流程
├── 输出结构化诊断报告
├── 人工审查每步判断
└── 修正并迭代

3.3 六问与前章节的映射

六问步骤关联章节关联知识点
理解问题Ch1问题定义、输入输出、约束
简单算法Ch1-Ch12暴力基线、启发式
查目录Ch2-Ch11问题分类、范式识别
特殊情况Ch5-Ch8参数固定、规模缩小
范式匹配Ch3分治, Ch5回溯, Ch6DP, Ch7贪心四大范式选择
循环修正Ch1六问循环、Agent协作

四、实现示例:最长回文子串的六问诊断

让我们用六问诊断法分析"最长回文子串"问题。

4.1 问题陈述

问题描述:
给定一个字符串s,找出其中最长的回文子串。

回文:正读反读都相同的字符串。
子串:字符串的连续片段。

4.2 六问诊断过程

Q1:理解问题

输入:字符串s(长度n)
输出:最长回文子串(字符串)
约束:
├── 时间限制:O(n²)可接受,O(n³)可能太慢
├── 空间限制:O(n)或O(n²)可接受
└── 正确性:必须是最长,且是回文

小例子:
s = "babad"
回文子串:"bab"、"aba"、"a"、"b"、"d"
最长:"bab"或"aba"(长度3)

等价表述:
找字符串中满足s[i:j]是回文且长度最大的子串

Q2:简单算法

暴力解法:
枚举所有子串,检查是否回文,找最长的

复杂度:
├── 子串数:O(n²)
├── 回文检查:O(n)
└── 总复杂度:O(n³)

瓶颈:
回文检查需要O(n)时间,重复计算很多次

基线验证:
n=5时:O(125)可行
n=1000时:O(10^9)太慢

Q3:查目录

问题类型匹配:
├── 字符串问题(Ch3扩展)
├── 最优化问题(Ch6 DP)
├── 搜索问题(回溯扩展)
└── 不是排序、查找、图的直接应用

候选范式:
├── DP:有可能(重叠子问题)
├── 分治:不适合(子问题不独立)
├── 贪心:不适合(无贪心选择性质)
└── 回溯:可行(枚举所有回文子串),但效率不如DP

Q4:特殊情况

固定参数:
├── 单字符:总是回文(长度1)
├── 双字符:相邻相同才回文
└── 已知对称性:可利用

缩小规模:
├── 先看小例子:"a"、"aa"、"aba"
└── 发现规律:回文有对称中心

启发:
回文可以看作"从中心向外扩展"

Q5:范式匹配

候选方案:

方案1:DP
状态:dp[i][j] = s[i:j]是否是回文
转移:dp[i][j] = (s[i]==s[j]) and (j-i<=2 or dp[i+1][j-1])
复杂度:O(n²)时间,O(n²)空间

方案2:中心扩展
思路:枚举中心,向两边扩展直到不回文
复杂度:O(n²)时间,O(1)空间

方案3:Manacher算法(高级)
思路:利用对称性,避免重复检查
复杂度:O(n)时间,O(n)空间

推荐方案:
├── 简单场景:中心扩展(O(n²),空间O(1))
├── 一般场景:DP(O(n²),空间O(n²),易理解)
└── 高级场景:Manacher(O(n),复杂)

Q6:循环修正

测试方案1(DP):
小例子验证:s="babad"
dp[0][2] = (b==b) and (2-0<=2) = True ✓
dp[1][3] = (a==a) and (3-1<=2) = True ✓
dp[0][4] = (b==d) = False

反例测试:
s="cbbd"
dp[1][2] = (b==b) and True = True ✓(回文"bb")
最长回文是"bb",长度2

结论:DP方案正确 ✓

4.3 六问诊断报告

┌─────────────────────────────────────┐
│ 问题理解:                           │
│   输入:字符串s(长度n)             │
│   输出:最长回文子串                 │
│   约束:O(n²)时间可接受              │
│ 简单基线:                           │
│   方案:枚举所有子串检查回文         │
│   瓶颈:回文检查O(n),总O(n³)        │
│ 候选范式:                           │
│   [DP, 中心扩展, Manacher]           │
│   选择理由:重叠子问题适合DP         │
│ 复杂度比较:                         │
│   DP:O(n²)时间,O(n²)空间           │
│   中心扩展:O(n²)时间,O(1)空间      │
│   Manacher:O(n)时间,O(n)空间       │
│ 推荐方案:                           │
│   一般场景:中心扩展                 │
│   需最优效率:Manacher               │
│ 反例与边界:                         │
│   已验证小例子                       │
│ 仍需人工确认:                       │
│   Manacher实现复杂度                 │
└─────────────────────────────────────┘

五、与前章节关联:六问贯穿全书

5.1 六问与全书章节对应

六问对应章节教学内容
Q1:理解问题Ch1开场输入输出约束定义
Q2:简单算法Ch3排序基线暴力解法+瓶颈分析
Q3:查目录Ch4图+Ch5回溯+Ch6DP+Ch7贪心问题类型识别
Q4:特殊情况Ch8NP完全参数固定、规模限制
Q5:范式匹配Ch3-Ch11综合范式选择决策树
Q6:循环修正全书迭代案例反例构造、重新建模

5.2 六问法作为全书主线

六问法不是"最后一章才出现"的方法,而是贯穿全书的思维框架:

  • Ch1:Q1理解问题的基础
  • Ch2:数据结构选择影响Q5范式匹配
  • Ch3:分治原型,Q5候选之一
  • Ch4-Ch7:贪心、图算法原型,Q5候选
  • Ch5:回溯原型,Q5候选之一
  • Ch6:DP原型,Q5候选之一
  • Ch8:NP完全性,Q4特殊情况分析
  • Ch9-Ch10:分布式、流式,Q5扩展候选
  • Ch11:Transformer视角,LLM时代的新范式
  • Ch12:六问法综合,系统化凝练

六、设计反思:策略先于战术

6.1 策略 vs 战术

策略(Strategy):做什么——选择范式、选择数据结构

战术(Tactics):怎么做——实现细节、代码优化、边界处理

重要原则:策略错误,再好的战术也救不了。

例子

问题:最长递增子序列(LIS)

错误策略:用贪心
战术:优化贪心选择规则、优化代码
结果:无论如何优化,贪心都是错的(不能保证最优)

正确策略:用DP
战术:朴素DP实现、空间优化
结果:保证最优解

6.2 从简单到正确到高效

三层递进

第一层:简单
├── 暴力解法
├── 正确性验证
└── 理解瓶颈

第二层:正确
├── 选择正确范式
├── 设计正确算法
└── 构造反例验证

第三层:高效
├── 优化复杂度
├── 空间优化
└── 常数因子优化

不可跳过第二层——先保证正确,再追求高效。


七、知识卡片

C12-33 六问诊断法

直觉解释:像医生看病,按清单逐项检查,不漏环节。

核心内容:Q1理解问题 → Q2简单算法 → Q3查目录 → Q4特殊情况 → Q5范式匹配 → Q6循环修正。

关键特征:系统化推进,每步都有明确目标,避免"等灵感"。

常见误解

  • ❌ 六问法是线性流程——实际会回环迭代
  • ✅ 正确理解:六问法是循环,可能回到前面的步骤

实例:最长回文子串——按六问逐步推进,发现中心扩展法和Manacher算法。

C12-34 Q1: 理解问题

直觉解释:先搞清楚"要什么",别急着动手。

核心内容:明确输入输出、约束条件、构造小例子验证理解。

关键特征:不要模糊理解就开始写代码,小例子能发现隐藏约束。

常见误解

  • ❌ 理解问题很简单——复杂问题的输入输出约束很隐蔽
  • ✅ 正确理解:仔细阅读问题描述,构造小例子验证

实例:二分查找——理解"有序数组"的约束是关键前提。

C12-35 Q2: 简单算法

直觉解释:先用"笨办法"做出来,别想一步到位。

核心内容:给出暴力基线方案,分析瓶颈在哪里。

关键特征:基线方案不一定高效,但能验证理解和发现瓶颈。

常见误解

  • ❌ 简单算法没用——它是正确性验证和优化起点
  • ✅ 正确理解:基线方案是优化的起点

实例:排序问题——暴力O(n²),分析瓶颈是"比较太多",引出分治优化。

C12-36 Q3: 查目录

直觉解释:翻开"知识库",看问题属于哪类。

核心内容:识别问题类型:排序/搜索/图/字符串/动态规划...

关键特征:问题类型匹配算法范式的知识库。

常见误解

  • ❌ 问题都能归入某一类——有些问题是混合类型,需要拆解
  • ✅ 正确理解:可能需要组合多种范式

实例:最短路径——查目录发现图论问题,候选范式:贪心(Dijkstra)/DP(Bellman-Ford)。

C12-37 Q4: 特殊情况

直觉解释:参数固定或规模缩小,问题变简单。

核心内容:固定某些参数、限制规模、考虑退化情况。

关键特征:特殊参数可能让NP问题变成P问题。

常见误解

  • ❌ 特殊情况不重要——它们常是突破口或边界验证
  • ✅ 正确理解:特殊情况能发现隐藏结构

实例:图着色问题——k=2时多项式可解(二分图判断),k≥3时NP完全。

C12-38 Q5: 范式匹配

直觉解释:从候选方案中选最适合的。

核心内容:分治/贪心/DP/回溯/近似的选择依据和复杂度比较。

关键特征:范式选择依赖问题特征:子问题是否独立?是否有贪心选择性质?是否有重叠子问题?

常见误解

  • ❌ 每个范式对应一类问题——很多问题多种范式都适用,需比较复杂度
  • ✅ 正确理解:多种范式可能都适用,需对比选择

实例:背包问题——贪心(近似)、DP(精确)、回溯(求所有解)。

C12-39 Q6: 循环修正

直觉解释:方案不是一次对的,要反复检查。

核心内容:构造反例测试、参考专家方案、必要时重新建模。

关键特征:反例是发现设计漏洞的利器,专家方案提供验证。

常见误解

  • ❌ 一次设计就正确——复杂问题需要多次迭代修正
  • ✅ 正确理解:迭代修正直到正确

实例:贪心策略初稿→发现反例→修正策略或换范式。

C12-40 试飞员心态

直觉解释:像飞行员起飞前跑清单,不要靠"感觉"。

核心内容:系统性思考(按六问清单) > 灵感式思考(等想法)。

关键特征:结构化方法保证覆盖关键环节,避免遗漏。

常见误解

  • ❌ 方法论限制创造力——方法论是基础,创造力在此基础上发挥
  • ✅ 正确理解:方法论是框架,创造力在框架内发挥

实例:资深程序员也有Checklist(代码审查清单、部署清单)。


八、练习设计

基础练习(理解六问)

练习 12.5-1:用六问诊断法分析"最长公共子序列(LCS)"问题。

参考答案

Q1: 理解问题
输入:两个字符串X和Y(长度m和n)
输出:最长公共子序列的长度(或子序列本身)
约束:O(mn)时间可接受
小例子:X="ABC", Y="AC" → LCS="AC"长度2

Q2: 简单算法
暴力解法:枚举X的所有子序列,检查是否是Y的子序列,找最长的
复杂度:O(2^m × n) —— 指数爆炸
瓶颈:子序列数是O(2^m)

Q3: 查目录
问题类型:字符串问题、最优化问题
候选范式:DP(重叠子问题)

Q4: 特殊情况
固定参数:单字符匹配
启发:LCS(X,Y)可能依赖LCS(X前缀,Y前缀)

Q5: 范式匹配
候选方案:DP
状态:dp[i][j] = X前i字符与Y前j字符的LCS长度
转移:dp[i][j] = max(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]+1 if X[i]==Y[j])
复杂度:O(mn)时间,O(mn)空间(可优化到O(min(m,n)))

Q6: 循环修正
小例子验证:X="ABC", Y="AC"
dp[1][1] = (A==A) → dp[0][0]+1 = 1 ✓
dp[2][2] = (B!=C) → max(dp[1][2], dp[2][1]) = 1 ✓
dp[3][2] = (C==C) → dp[2][1]+1 = 2 ✓
结论:DP方案正确 ✓

进阶练习(六问应用)

练习 12.5-2:用六问诊断法分析"三数之和"问题。

思路

Q1: 理解问题
输入:数组nums,目标target
输出:三个数的下标,使其和=target
约束:无重复解,O(n²)时间可接受

Q2: 简单算法
暴力解法:三重循环枚举所有三元组
复杂度:O(n³)
瓶颈:三重循环

Q3: 查目录
问题类型:数组问题、查找问题
候选范式:排序+双指针(贪心扩展)

Q4: 特殊情况
固定一个数:变成"两数之和"问题
启发:两数之和有O(n)解法(哈希表)

Q5: 范式匹配
候选方案:
├── 排序+双指针:O(n²)
└── 固定一个数+哈希表:O(n²)

推荐方案:排序+双指针(空间O(1))

Q6: 循环修正
小例子验证:nums=[-1,0,1,2,-1,-4], target=0
排序后:[-4,-1,-1,0,1,2]
双指针找到:[-1,-1,2]和[-1,0,1]
结论:方案正确 ✓

设计练习(六问→Agent)

练习 12.5-3:设计六问诊断Agent Skill的伪代码。

思路

python
def six_question_diagnosis_agent(problem_description):
    """
    六问诊断Agent Skill
    
    输入:问题描述文本
    输出:结构化诊断报告
    """
    report = {}
    
    # Q1: 理解问题
    report['Q1'] = {
        'input': parse_input(problem_description),
        'output': parse_output(problem_description),
        'constraints': parse_constraints(problem_description),
        'examples': generate_examples(problem_description)
    }
    
    # Q2: 简单算法
    baseline = generate_baseline_solution(report['Q1'])
    report['Q2'] = {
        'baseline_solution': baseline,
        'baseline_complexity': analyze_complexity(baseline),
        'bottleneck': identify_bottleneck(baseline)
    }
    
    # Q3: 查目录
    problem_type = match_problem_type(report['Q1'])
    report['Q3'] = {
        'problem_type': problem_type,
        'related_chapters': get_related_chapters(problem_type)
    }
    
    # Q4: 特殊情况
    special_cases = generate_special_cases(report['Q1'])
    report['Q4'] = {
        'special_cases': special_cases,
        'insights': analyze_special_cases(special_cases)
    }
    
    # Q5: 范式匹配
    candidates = generate_paradigm_candidates(report['Q3'])
    report['Q5'] = {
        'candidates': candidates,
        'complexity_comparison': compare_complexity(candidates),
        'recommendation': select_best_candidate(candidates)
    }
    
    # Q6: 循环修正
    report['Q6'] = {
        'test_cases': generate_test_cases(report['Q5']['recommendation']),
        'need_human_review': identify_review_points(report)
    }
    
    return report

开放练习

练习 12.5-4:六问法如何与LLM协同工作?

思考方向

LLM擅长:
├── Q1理解问题:解析文本,提取输入输出约束
├── Q2简单算法:生成基线方案代码
├── Q3查目录:检索知识库,匹配问题类型

LLM不擅长:
├── Q4特殊情况:判断哪些特殊情况有意义
├── Q5范式匹配:复杂度比较需要精确分析
├── Q6循环修正:构造反例需要创造性思维

人机协作分工:
├── LLM:Q1-Q3自动化
├── 人:审查LLM输出,补充Q4-Q6
└── 循环:人反馈修正→LLM更新诊断

协同模式:
├── LLM生成诊断报告初稿
├── 人审查并标注待确认点
├── LLM根据反馈迭代修正
└── 人最终确认方案

导航上一节:12.4 回溯回顾 | 下一节:12.6 综合练习

新时代的算法课程