Skip to content

Commit ea7affc

Browse files
committed
feat: 增强动态规划算法的可视化时间线
1 parent 751e9ce commit ea7affc

File tree

1 file changed

+119
-10
lines changed

1 file changed

+119
-10
lines changed

src/algorithms/dpAlgorithm.ts

Lines changed: 119 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@ import { AnimationTimeline } from '../state/animationSlice';
33
/**
44
* 使用动态规划算法生成爬楼梯问题的解和动画时间线
55
* @param n 楼梯的阶数
6-
* @returns 包含结果和动画时间线的对象
6+
* @returns 包含结果、时间线和可视化数据的对象
77
*/
88
export function generateDPSolution(n: number): {
99
result: number;
1010
timeline: AnimationTimeline[];
11+
staircase?: {
12+
nodes: { id: number; x: number; y: number; value: number }[];
13+
links: { source: number; target: number }[];
14+
};
1115
} {
1216
// 初始化时间线
1317
const timeline: AnimationTimeline[] = [];
@@ -37,14 +41,14 @@ export function generateDPSolution(n: number): {
3741
result: 1,
3842
timeline: [{
3943
timestamp: stepCounter++ * 1000,
40-
description: "只有1阶楼梯,只有1种爬法",
44+
description: "只有1阶楼梯,只有1种爬法(一次爬1阶)",
4145
visualChanges: {
4246
nodeUpdates: [
4347
{ index: 0, props: { x: 50, y: 300, value: 1 } },
4448
{ index: 1, props: { x: 150, y: 260, value: 1 } }
4549
],
4650
matrixUpdates: [],
47-
formulaUpdate: "n = 1,返回1"
51+
formulaUpdate: "f(1) = 1"
4852
},
4953
interactionPoints: []
5054
}]
@@ -56,10 +60,46 @@ export function generateDPSolution(n: number): {
5660
dp[0] = 1;
5761
dp[1] = 1;
5862

63+
// 引入基本概念
64+
timeline.push({
65+
timestamp: stepCounter++ * 1000,
66+
description: "爬楼梯问题:每次可以爬1阶或2阶,问爬到第n阶有多少种方法?",
67+
visualChanges: {
68+
nodeUpdates: [],
69+
matrixUpdates: [],
70+
formulaUpdate: "动态规划解法"
71+
},
72+
interactionPoints: []
73+
});
74+
75+
// 解释动态规划思路
76+
timeline.push({
77+
timestamp: stepCounter++ * 1000,
78+
description: "动态规划的关键是找到状态转移方程,我们需要思考如何到达第n阶",
79+
visualChanges: {
80+
nodeUpdates: [],
81+
matrixUpdates: [],
82+
formulaUpdate: "f(n) = f(n-1) + f(n-2)"
83+
},
84+
interactionPoints: []
85+
});
86+
87+
// 解释状态转移方程
88+
timeline.push({
89+
timestamp: stepCounter++ * 1000,
90+
description: "想要到达第n阶,可以从第n-1阶爬1阶,或从第n-2阶爬2阶,所以f(n) = f(n-1) + f(n-2)",
91+
visualChanges: {
92+
nodeUpdates: [],
93+
matrixUpdates: [],
94+
formulaUpdate: "f(n) = f(n-1) + f(n-2)"
95+
},
96+
interactionPoints: []
97+
});
98+
5999
// 初始化阶段的时间线
60100
timeline.push({
61101
timestamp: stepCounter++ * 1000,
62-
description: "初始化阶段,第 0 阶和第 1 阶各有 1 种爬法",
102+
description: "初始化:第0阶表示起点,有1种方法(不爬);第1阶有1种方法(爬1阶)",
63103
visualChanges: {
64104
nodeUpdates: [
65105
{ index: 0, props: { x: 50, y: 300, value: 1 } },
@@ -71,15 +111,37 @@ export function generateDPSolution(n: number): {
71111
interactionPoints: []
72112
});
73113

114+
// 创建节点之间的连接关系
115+
const links = [];
116+
74117
// 状态转移阶段
75118
for (let i = 2; i <= n; i++) {
76119
// 计算当前阶的值
77120
dp[i] = dp[i - 1] + dp[i - 2];
78121

79-
// 添加到时间线
122+
// 添加连接
123+
links.push({ source: i-1, target: i });
124+
links.push({ source: i-2, target: i });
125+
126+
// 添加到时间线 - 状态转移前的解释
127+
timeline.push({
128+
timestamp: stepCounter++ * 1000,
129+
description: `准备计算第${i}阶的爬法数量,需要用到第${i-1}阶和第${i-2}阶的结果`,
130+
visualChanges: {
131+
nodeUpdates: [
132+
{ index: i-1, props: { x: 50 + (i-1) * 100, y: 300 - Math.min((i-1) * 50, 200), value: dp[i-1] } },
133+
{ index: i-2, props: { x: 50 + (i-2) * 100, y: 300 - Math.min((i-2) * 50, 200), value: dp[i-2] } }
134+
],
135+
matrixUpdates: [],
136+
formulaUpdate: `f(${i}) = f(${i-1}) + f(${i-2})`
137+
},
138+
interactionPoints: []
139+
});
140+
141+
// 添加到时间线 - 状态转移计算
80142
timeline.push({
81143
timestamp: stepCounter++ * 1000,
82-
description: `计算第 ${i} 阶的爬法数量`,
144+
description: `计算第${i}阶的爬法:从第${i-1}阶爬1阶的方法数(${dp[i-1]})加上从第${i-2}阶爬2阶的方法数(${dp[i-2]})`,
83145
visualChanges: {
84146
nodeUpdates: [
85147
{
@@ -98,21 +160,68 @@ export function generateDPSolution(n: number): {
98160
});
99161
}
100162

101-
// 滚动数组优化阶段
163+
// 结果展示
164+
timeline.push({
165+
timestamp: stepCounter++ * 1000,
166+
description: `得到结果:爬到第${n}阶总共有${dp[n]}种不同的方法`,
167+
visualChanges: {
168+
nodeUpdates: [
169+
{ index: n, props: { x: 50 + n * 100, y: 300 - Math.min(n * 50, 200), value: dp[n] } }
170+
],
171+
matrixUpdates: [],
172+
formulaUpdate: `f(${n})=${dp[n]}`
173+
},
174+
interactionPoints: []
175+
});
176+
177+
// 空间优化解释
178+
timeline.push({
179+
timestamp: stepCounter++ * 1000,
180+
description: "动态规划优化:我们只需要保存前两个状态,可以将空间复杂度从O(n)优化到O(1)",
181+
visualChanges: {
182+
nodeUpdates: [],
183+
matrixUpdates: [],
184+
formulaUpdate: "滚动数组:p=f(n-2), q=f(n-1), r=p+q"
185+
},
186+
interactionPoints: []
187+
});
188+
189+
// 空间优化过程
190+
timeline.push({
191+
timestamp: stepCounter++ * 1000,
192+
description: "使用三个变量p, q, r分别表示f(n-2), f(n-1), f(n),计算完一个状态后向前滚动",
193+
visualChanges: {
194+
nodeUpdates: [],
195+
matrixUpdates: [],
196+
formulaUpdate: "p←q, q←r, r=p+q"
197+
},
198+
interactionPoints: []
199+
});
200+
201+
// 时间复杂度分析
102202
timeline.push({
103203
timestamp: stepCounter++ * 1000,
104-
description: "通过滚动数组,我们将空间复杂度从 O(n) 优化到 O(1)",
204+
description: "算法分析:时间复杂度O(n),空间复杂度O(1),这是爬楼梯问题的最优解法",
105205
visualChanges: {
106206
nodeUpdates: [],
107207
matrixUpdates: [],
108-
formulaUpdate: "空间优化:只需保存前两个状态,p=f(n-2), q=f(n-1), r=p+q"
208+
formulaUpdate: "时间O(n),空间O(1)"
109209
},
110210
interactionPoints: []
111211
});
112212

113213
return {
114214
result: dp[n],
115-
timeline
215+
timeline,
216+
staircase: {
217+
nodes: Array.from({length: n+1}, (_, i) => ({
218+
id: i,
219+
x: 50 + i * 100,
220+
y: 300 - Math.min(i * 50, 200),
221+
value: dp[i]
222+
})),
223+
links: links
224+
}
116225
};
117226
}
118227

0 commit comments

Comments
 (0)