Skip to content

Commit 85fdd85

Browse files
committed
[Feature] add for new
1 parent f9281a6 commit 85fdd85

File tree

1 file changed

+323
-0
lines changed

1 file changed

+323
-0
lines changed
Lines changed: 323 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,323 @@
1+
<!DOCTYPE html>
2+
<html lang="zh-CN">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>LeetCode 154 - 寻找旋转排序数组中的最小值 可视化</title>
7+
<style>
8+
body {
9+
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
10+
background: #f0f2f5;
11+
margin: 20px;
12+
}
13+
a {
14+
text-decoration: none;
15+
color: #0078d7;
16+
font-weight: bold;
17+
}
18+
.container {
19+
max-width: 900px;
20+
margin: auto;
21+
}
22+
h1 {
23+
text-align: center;
24+
margin-bottom: 15px;
25+
}
26+
label {
27+
font-weight: bold;
28+
}
29+
input[type="text"] {
30+
width: 100%;
31+
font-size: 16px;
32+
padding: 6px 10px;
33+
margin: 8px 0 15px 0;
34+
box-sizing: border-box;
35+
}
36+
button {
37+
font-size: 16px;
38+
padding: 8px 20px;
39+
cursor: pointer;
40+
background-color: #0078d7;
41+
border: none;
42+
border-radius: 3px;
43+
color: white;
44+
}
45+
button:disabled {
46+
background-color: #cccccc;
47+
cursor: not-allowed;
48+
}
49+
.array-container {
50+
margin-top: 20px;
51+
display: flex;
52+
justify-content: center;
53+
gap: 8px;
54+
flex-wrap: wrap;
55+
}
56+
.array-item {
57+
width: 40px;
58+
height: 40px;
59+
background-color: #eee;
60+
border-radius: 6px;
61+
display: flex;
62+
justify-content: center;
63+
align-items: center;
64+
font-weight: 600;
65+
font-size: 18px;
66+
position: relative;
67+
transition: background-color 0.3s ease;
68+
}
69+
/* 指针标记 */
70+
.pointer {
71+
position: absolute;
72+
top: -22px;
73+
width: 40px;
74+
text-align: center;
75+
font-weight: bold;
76+
color: white;
77+
padding: 2px 0;
78+
border-radius: 3px;
79+
}
80+
.pointer.L {
81+
background-color: #e74c3c;
82+
}
83+
.pointer.M {
84+
background-color: #3498db;
85+
}
86+
.pointer.R {
87+
background-color: #2ecc71;
88+
}
89+
/* 高亮当前访问元素 */
90+
.highlight-mid {
91+
background-color: #3498db;
92+
color: white;
93+
}
94+
/* 结果高亮 */
95+
.result {
96+
background-color: #f39c12 !important;
97+
color: white !important;
98+
}
99+
/* 结束结果高亮 */
100+
.result.final {
101+
background-color: #d35400 !important;
102+
}
103+
.log-container {
104+
margin-top: 30px;
105+
background-color: #121212;
106+
color: #eee;
107+
font-family: monospace;
108+
padding: 15px;
109+
height: 250px;
110+
overflow-y: auto;
111+
border-radius: 6px;
112+
white-space: pre-wrap;
113+
}
114+
</style>
115+
</head>
116+
<body>
117+
<div class="container">
118+
<a href="index.html">← 返回首页</a>
119+
<h1>LeetCode 154 - 寻找旋转排序数组中的最小值 可视化</h1>
120+
<label for="inputArray">输入旋转排序数组(逗号分隔):</label>
121+
<input id="inputArray" type="text" value="3,4,5,1,2" placeholder="例如: 3,4,5,1,2" />
122+
<button id="startBtn">开始可视化</button>
123+
124+
<div class="array-container" id="arrayContainer"></div>
125+
126+
<div class="log-container" id="logContainer"></div>
127+
</div>
128+
129+
<script>
130+
const inputArray = document.getElementById("inputArray");
131+
const startBtn = document.getElementById("startBtn");
132+
const arrayContainer = document.getElementById("arrayContainer");
133+
const logContainer = document.getElementById("logContainer");
134+
135+
let nums = [];
136+
let left = 0, right = 0, mid = -1, min = 0;
137+
let steps = [];
138+
let stepIndex = 0;
139+
140+
// 记录日志
141+
function log(str) {
142+
logContainer.textContent += str + "\n";
143+
logContainer.scrollTop = logContainer.scrollHeight;
144+
}
145+
146+
// 渲染数组和指针
147+
function renderArray(l, m, r, highlightMinIndex = -1, final = false) {
148+
arrayContainer.innerHTML = "";
149+
for (let i = 0; i < nums.length; i++) {
150+
const div = document.createElement("div");
151+
div.className = "array-item";
152+
153+
div.textContent = nums[i];
154+
155+
// 高亮最小值方块
156+
if (i === highlightMinIndex) {
157+
div.classList.add("result");
158+
if(final) div.classList.add("final");
159+
}
160+
161+
// L 指针
162+
if (i === l) {
163+
const pointerL = document.createElement("div");
164+
pointerL.textContent = "L";
165+
pointerL.className = "pointer L";
166+
div.appendChild(pointerL);
167+
}
168+
169+
// M 指针,m 为 -1 不显示
170+
if (m !== -1 && i === m) {
171+
const pointerM = document.createElement("div");
172+
pointerM.textContent = "M";
173+
pointerM.className = "pointer M";
174+
div.appendChild(pointerM);
175+
div.classList.add("highlight-mid");
176+
}
177+
178+
// R 指针
179+
if (i === r) {
180+
const pointerR = document.createElement("div");
181+
pointerR.textContent = "R";
182+
pointerR.className = "pointer R";
183+
div.appendChild(pointerR);
184+
}
185+
186+
arrayContainer.appendChild(div);
187+
}
188+
}
189+
190+
// 预处理输入,转成数字数组
191+
function parseInput() {
192+
const val = inputArray.value.trim();
193+
if (!val) return null;
194+
const arr = val.split(",").map(s => s.trim()).filter(s => s.length > 0);
195+
if (arr.length === 0) return null;
196+
let parsed = [];
197+
for (const s of arr) {
198+
const num = Number(s);
199+
if (isNaN(num)) return null;
200+
parsed.push(num);
201+
}
202+
return parsed;
203+
}
204+
205+
// 生成所有步骤,方便动画播放
206+
function generateSteps(nums) {
207+
let steps = [];
208+
let left = 0;
209+
let right = nums.length - 1;
210+
let min = nums[0];
211+
212+
while (left <= right) {
213+
let mid = left + Math.floor((right - left) / 2);
214+
steps.push({
215+
left, right, mid, min, nums: [...nums], note: ""
216+
});
217+
218+
if (nums[left] < nums[right]) {
219+
min = Math.min(min, nums[left]);
220+
steps.push({
221+
left, right, mid, min, nums: [...nums], done: true, note: "整体有序,最小值就是 nums[left]"
222+
});
223+
break;
224+
}
225+
226+
min = Math.min(min, nums[mid]);
227+
228+
if (nums[mid] > nums[right]) {
229+
left = mid + 1;
230+
steps.push({
231+
left, right, mid, min, nums: [...nums], note: "nums[mid] > nums[right],最小值在右边,left = mid + 1"
232+
});
233+
} else if (nums[mid] < nums[right]) {
234+
right = mid;
235+
steps.push({
236+
left, right, mid, min, nums: [...nums], note: "nums[mid] < nums[right],最小值在 mid 或左边,right = mid"
237+
});
238+
} else {
239+
right--;
240+
steps.push({
241+
left, right, mid, min, nums: [...nums], note: "nums[mid] == nums[right],无法判断,right--"
242+
});
243+
}
244+
}
245+
246+
if (steps.length === 0 || !steps[steps.length - 1].done) {
247+
steps.push({
248+
left, right, mid: -1, min, nums: [...nums], done: true, note: "算法结束"
249+
});
250+
}
251+
252+
return steps;
253+
}
254+
255+
// 按步骤播放动画
256+
function playSteps() {
257+
if (stepIndex >= steps.length) {
258+
const last = steps[steps.length - 1];
259+
log(`完成,最小值为: ${last.min}`);
260+
renderArray(
261+
last.left,
262+
-1,
263+
last.right,
264+
nums.indexOf(last.min),
265+
true
266+
);
267+
startBtn.disabled = false;
268+
return;
269+
}
270+
271+
const step = steps[stepIndex];
272+
273+
log(`Step ${stepIndex + 1}: left=${step.left}, mid=${step.mid}, right=${step.right}, min=${step.min}`);
274+
if (step.note) {
275+
log(` 操作说明:${step.note}`);
276+
}
277+
if (step.done) {
278+
log("算法结束");
279+
}
280+
281+
renderArray(step.left, step.mid, step.right, nums.indexOf(step.min));
282+
283+
stepIndex++;
284+
setTimeout(playSteps, 1200);
285+
}
286+
287+
// 按钮点击处理
288+
startBtn.addEventListener("click", () => {
289+
const parsed = parseInput();
290+
if (!parsed || parsed.length === 0) {
291+
alert("请输入有效的数字数组,使用逗号分隔");
292+
return;
293+
}
294+
nums = parsed;
295+
left = 0;
296+
right = nums.length - 1;
297+
min = nums[0];
298+
stepIndex = 0;
299+
steps = generateSteps(nums);
300+
301+
logContainer.textContent = "";
302+
startBtn.disabled = true;
303+
304+
// 初始化渲染:L在左边,R在右边,M不显示
305+
renderArray(left, -1, right, -1);
306+
307+
setTimeout(playSteps, 800);
308+
});
309+
310+
// 页面初始渲染
311+
function init() {
312+
const defaultNums = parseInput();
313+
if (defaultNums) {
314+
nums = defaultNums;
315+
left = 0;
316+
right = nums.length - 1;
317+
renderArray(left, -1, right, -1);
318+
}
319+
}
320+
init();
321+
</script>
322+
</body>
323+
</html>

0 commit comments

Comments
 (0)