Skip to content

Commit bed9ba3

Browse files
committed
[Feature] add for new
1 parent f7ec94c commit bed9ba3

File tree

2 files changed

+281
-2
lines changed

2 files changed

+281
-2
lines changed

T304-prefix-sum-matrix-summary.html

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>T403-二维前缀和公式可视化</title>
7+
<style>
8+
body {
9+
font-family: Arial, sans-serif;
10+
margin: 20px;
11+
}
12+
h1 {
13+
margin-bottom: 10px;
14+
}
15+
.config {
16+
margin-bottom: 20px;
17+
}
18+
.config label {
19+
margin-right: 10px;
20+
}
21+
input[type="number"] {
22+
width: 50px;
23+
margin-right: 15px;
24+
}
25+
button {
26+
padding: 5px 15px;
27+
font-size: 14px;
28+
}
29+
.matrix-container {
30+
display: flex;
31+
flex-wrap: wrap;
32+
}
33+
.matrix {
34+
display: inline-block;
35+
margin: 10px;
36+
}
37+
.matrix table {
38+
border-collapse: collapse;
39+
}
40+
.matrix td {
41+
border: 1px solid #999;
42+
width: 40px;
43+
height: 40px;
44+
text-align: center;
45+
vertical-align: middle;
46+
font-size: 14px;
47+
position: relative;
48+
user-select: none;
49+
}
50+
.legend {
51+
margin-top: 30px;
52+
}
53+
.highlight-a { background-color: #FFD580; } /* +prefix[row2+1][col2+1] */
54+
.highlight-b { background-color: #B6E2D3; } /* -prefix[row1][col2+1] */
55+
.highlight-c { background-color: #FFB6C1; } /* -prefix[row2+1][col1] */
56+
.highlight-d { background-color: #FFFACD; } /* +prefix[row1][col1] */
57+
.highlight-region {
58+
border: 3px solid #4A90E2 !important; /* 蓝色边框强调目标区域 */
59+
box-sizing: border-box;
60+
font-weight: bold;
61+
}
62+
.matrix-caption {
63+
text-align: center;
64+
font-weight: bold;
65+
margin-bottom: 5px;
66+
}
67+
</style>
68+
</head>
69+
<body>
70+
<h1>二维前缀和公式可视化</h1>
71+
72+
<div class="config">
73+
<label>行数 (rows): <input type="number" id="inputRows" value="5" min="1" max="20" /></label>
74+
<label>列数 (cols): <input type="number" id="inputCols" value="5" min="1" max="20" /></label>
75+
<br /><br />
76+
<label>row1: <input type="number" id="inputRow1" value="2" min="0" /></label>
77+
<label>col1: <input type="number" id="inputCol1" value="2" min="0" /></label>
78+
<label>row2: <input type="number" id="inputRow2" value="3" min="0" /></label>
79+
<label>col2: <input type="number" id="inputCol2" value="3" min="0" /></label>
80+
<br /><br />
81+
<button id="btnFill">填充矩阵并更新</button>
82+
<a href="index.html">返回首页</a>
83+
<div id="errorMsg" style="color:red; margin-top:10px;"></div>
84+
</div>
85+
86+
<div class="matrix-container" id="matricesContainer">
87+
<!-- 6 矩阵容器,JS动态生成 -->
88+
</div>
89+
90+
<div class="legend">
91+
<h2>颜色说明</h2>
92+
<ul>
93+
<li><span class="highlight-a">橙色</span>:+ prefix[row2+1][col2+1],表示从 (0,0) 到 (row2,col2) 的区域</li>
94+
<li><span class="highlight-b">绿色</span>:- prefix[row1][col2+1],表示从 (0,0) 到 (row1-1,col2) 的区域</li>
95+
<li><span class="highlight-c">粉色</span>:- prefix[row2+1][col1],表示从 (0,0) 到 (row2,col1-1) 的区域</li>
96+
<li><span class="highlight-d">黄色</span>:+ prefix[row1][col1],表示从 (0,0) 到 (row1-1,col1-1) 的区域</li>
97+
<li><span class="highlight-region" style="border:none; display:inline-block; width:18px; height:18px; margin-right:5px;"></span> 蓝色边框:实际求和区域 (row1,col1) 到 (row2,col2)</li>
98+
</ul>
99+
<h2>公式与计算说明</h2>
100+
<p>求和区域为 <code>(row1, col1) 到 (row2, col2)</code>,则公式:</p>
101+
<pre>
102+
return prefix[row2+1][col2+1]
103+
- prefix[row1][col2+1]
104+
- prefix[row2+1][col1]
105+
+ prefix[row1][col1]
106+
</pre>
107+
<p>其中每个 <code>prefix[x][y]</code> 表示从 (0,0) 到 (x-1,y-1) 的累加和。</p>
108+
</div>
109+
110+
<script>
111+
const matricesNames = [
112+
{id: 'A', caption: 'prefix[row2+1][col2+1]', className: 'highlight-a'},
113+
{id: 'B', caption: 'prefix[row1][col2+1]', className: 'highlight-b'},
114+
{id: 'C', caption: 'prefix[row2+1][col1]', className: 'highlight-c'},
115+
{id: 'D', caption: 'prefix[row1][col1]', className: 'highlight-d'},
116+
{id: 'region', caption: '目标求和区域 (row1, col1) 到 (row2, col2)', className: 'highlight-region'}
117+
];
118+
119+
function createTable(rows, cols) {
120+
const table = document.createElement('table');
121+
const tbody = document.createElement('tbody');
122+
for(let r=0; r<rows; r++) {
123+
const tr = document.createElement('tr');
124+
for(let c=0; c<cols; c++) {
125+
const td = document.createElement('td');
126+
td.textContent = ''; // will fill later
127+
tr.appendChild(td);
128+
}
129+
tbody.appendChild(tr);
130+
}
131+
table.appendChild(tbody);
132+
return table;
133+
}
134+
135+
function buildMatrixDiv(caption, rows, cols) {
136+
const div = document.createElement('div');
137+
div.className = 'matrix';
138+
const cap = document.createElement('div');
139+
cap.className = 'matrix-caption';
140+
cap.textContent = caption;
141+
div.appendChild(cap);
142+
div.appendChild(createTable(rows, cols));
143+
return div;
144+
}
145+
146+
function generateRandomMatrix(rows, cols) {
147+
const matrix = [];
148+
for(let i=0; i<rows; i++) {
149+
const row = [];
150+
for(let j=0; j<cols; j++) {
151+
row.push(Math.floor(Math.random() * 101));
152+
}
153+
matrix.push(row);
154+
}
155+
return matrix;
156+
}
157+
158+
function computePrefixSum(matrix, rows, cols) {
159+
// prefix sums: (rows+1) x (cols+1)
160+
const prefix = Array(rows+1).fill(0).map(() => Array(cols+1).fill(0));
161+
for(let r=1; r<=rows; r++) {
162+
for(let c=1; c<=cols; c++) {
163+
prefix[r][c] = matrix[r-1][c-1] + prefix[r-1][c] + prefix[r][c-1] - prefix[r-1][c-1];
164+
}
165+
}
166+
return prefix;
167+
}
168+
169+
function clearHighlights(tdList) {
170+
tdList.forEach(td => {
171+
td.classList.remove('highlight-a', 'highlight-b', 'highlight-c', 'highlight-d', 'highlight-region');
172+
});
173+
}
174+
175+
function highlightArea(matrixDiv, rows, cols, rStart, cStart, rEnd, cEnd, className) {
176+
const table = matrixDiv.querySelector('table');
177+
const tbody = table.tBodies[0];
178+
for(let r=0; r<rows; r++) {
179+
for(let c=0; c<cols; c++) {
180+
const td = tbody.rows[r].cells[c];
181+
if(r >= rStart && r <= rEnd && c >= cStart && c <= cEnd) {
182+
td.classList.add(className);
183+
} else {
184+
td.classList.remove(className);
185+
}
186+
}
187+
}
188+
}
189+
190+
function fillMatrixValues(matrixDiv, matrix) {
191+
const table = matrixDiv.querySelector('table');
192+
const tbody = table.tBodies[0];
193+
for(let r=0; r<matrix.length; r++) {
194+
for(let c=0; c<matrix[0].length; c++) {
195+
tbody.rows[r].cells[c].textContent = matrix[r][c];
196+
}
197+
}
198+
}
199+
200+
function renderMatrices(matrix, prefix, row1, col1, row2, col2) {
201+
const container = document.getElementById('matricesContainer');
202+
container.innerHTML = '';
203+
const rows = matrix.length;
204+
const cols = matrix[0].length;
205+
206+
// 构建矩阵A-D和目标区域矩阵
207+
// A: prefix[row2+1][col2+1], highlight (0,0)-(row2,col2)
208+
const matrixA = buildMatrixDiv('prefix[row2+1][col2+1]', rows, cols);
209+
fillMatrixValues(matrixA, matrix);
210+
highlightArea(matrixA, rows, cols, 0, 0, row2, col2, 'highlight-a');
211+
container.appendChild(matrixA);
212+
213+
// B: prefix[row1][col2+1], highlight (0,0)-(row1-1,col2)
214+
const matrixB = buildMatrixDiv('prefix[row1][col2+1]', rows, cols);
215+
fillMatrixValues(matrixB, matrix);
216+
highlightArea(matrixB, rows, cols, 0, 0, row1 - 1, col2, 'highlight-b');
217+
container.appendChild(matrixB);
218+
219+
// C: prefix[row2+1][col1], highlight (0,0)-(row2,col1-1)
220+
const matrixC = buildMatrixDiv('prefix[row2+1][col1]', rows, cols);
221+
fillMatrixValues(matrixC, matrix);
222+
highlightArea(matrixC, rows, cols, 0, 0, row2, col1 - 1, 'highlight-c');
223+
container.appendChild(matrixC);
224+
225+
// D: prefix[row1][col1], highlight (0,0)-(row1-1,col1-1)
226+
const matrixD = buildMatrixDiv('prefix[row1][col1]', rows, cols);
227+
fillMatrixValues(matrixD, matrix);
228+
highlightArea(matrixD, rows, cols, 0, 0, row1 - 1, col1 - 1, 'highlight-d');
229+
container.appendChild(matrixD);
230+
231+
// 目标区域矩阵,全部填数字,只高亮目标区域用蓝色边框
232+
const matrixRegion = buildMatrixDiv('目标求和区域 (row1, col1) 到 (row2, col2)', rows, cols);
233+
fillMatrixValues(matrixRegion, matrix);
234+
highlightArea(matrixRegion, rows, cols, row1, col1, row2, col2, 'highlight-region');
235+
container.appendChild(matrixRegion);
236+
}
237+
238+
function validateInputs(rows, cols, row1, col1, row2, col2) {
239+
if(rows <= 0 || cols <= 0) return '行数和列数必须大于0';
240+
if(row1 < 0 || col1 < 0 || row2 < 0 || col2 < 0) return '索引不能为负数';
241+
if(row1 > row2) return 'row1 不能大于 row2';
242+
if(col1 > col2) return 'col1 不能大于 col2';
243+
if(row2 >= rows) return 'row2 不能超出矩阵最大行数';
244+
if(col2 >= cols) return 'col2 不能超出矩阵最大列数';
245+
return '';
246+
}
247+
248+
document.getElementById('btnFill').addEventListener('click', () => {
249+
const rows = parseInt(document.getElementById('inputRows').value);
250+
const cols = parseInt(document.getElementById('inputCols').value);
251+
const row1 = parseInt(document.getElementById('inputRow1').value);
252+
const col1 = parseInt(document.getElementById('inputCol1').value);
253+
const row2 = parseInt(document.getElementById('inputRow2').value);
254+
const col2 = parseInt(document.getElementById('inputCol2').value);
255+
256+
const error = validateInputs(rows, cols, row1, col1, row2, col2);
257+
const errorDiv = document.getElementById('errorMsg');
258+
if(error) {
259+
errorDiv.textContent = error;
260+
return;
261+
}
262+
errorDiv.textContent = '';
263+
264+
const matrix = generateRandomMatrix(rows, cols);
265+
const prefix = computePrefixSum(matrix, rows, cols);
266+
267+
renderMatrices(matrix, prefix, row1, col1, row2, col2);
268+
});
269+
270+
// 页面加载时自动填充一次
271+
document.getElementById('btnFill').click();
272+
</script>
273+
</body>
274+
</html>

index.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,13 @@ <h1>算法可视化 · 资源导航</h1>
140140

141141
<div class="grid" id="cardGrid">
142142
<!-- 示例卡片 -->
143-
T164-radix-sort-prefix-sum.html
144-
143+
144+
<div class="card" data-tags="T304 prefix-sum prefix-sum-matrix array 前缀和 二维前缀和">
145+
<div class="card-title">T304 二维区域和检索 - 矩阵不可变</div>
146+
<div class="card-desc">T304 二维区域和检索 - 矩阵不可变 前缀和+二维前缀和</div>
147+
<a href="./T304-prefix-sum-matrix-summary.html" target="_blank">打开页面</a>
148+
</div>
149+
145150
<div class="card" data-tags="T164 sort radix-sort bucket-sort counting-sort array">
146151
<div class="card-title">T164 最大间距-基数排序</div>
147152
<div class="card-desc">T164 最大间距-基数排序+计数排序+前缀和</div>

0 commit comments

Comments
 (0)