Files
illustrated-algorithm/__pycache__/linked_list_algos.cpython-311.pyc
T

1214 lines
73 KiB
Plaintext
Raw Normal View History

§
¼Â‹jFãóf—dZdZdZd„Zd„Zd„Zd„Zd„Zd„Zd „Z d
„Z
d „Z d „Z d
„Z
d„Zd„Zd„ZdS)u@Linked-list algorithm JS generators for LeetCode Hot 100 图解.aa
function llViz(nodes, opts) {
opts = opts || {};
var highs = opts.highs || {};
var ptrs = opts.ptrs || {};
var cycle = opts.cycle != null ? opts.cycle : -1;
var label = opts.label || '';
var showNull = opts.showNull !== false;
var html = '';
if (label) html += '<div style="font-size:13px;font-weight:600;color:var(--text-secondary);margin-bottom:4px;">' + label + '</div>';
html += '<div style="display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:4px 0;">';
for (var i = 0; i < nodes.length; i++) {
var cls = highs[i] || '';
html += '<div style="position:relative;display:inline-flex;flex-direction:column;align-items:center;">';
html += '<div class="ll-node ' + cls + '">';
html += '<span class="val">' + nodes[i] + '</span>';
if (i === cycle) {
html += '<span class="arrow" style="color:var(--red);font-weight:bold;">&#8617;</span>';
} else if (i < nodes.length - 1) {
html += '<span class="arrow">&rarr;</span>';
} else if (showNull && i === nodes.length - 1) {
html += '<span class="arrow" style="color:var(--text-muted);">&empty;</span>';
}
html += '</div>';
var plabels = [];
var pkeys = Object.keys(ptrs);
for (var pi = 0; pi < pkeys.length; pi++) {
if (ptrs[pkeys[pi]] === i) plabels.push(pkeys[pi]);
}
if (plabels.length) {
html += '<div style="font-size:10px;font-weight:700;color:var(--blue);margin-top:2px;white-space:nowrap;">' + plabels.join(',') + '</div>';
}
html += '</div>';
}
html += '</div>';
return html;
}
a
function setPipeline(stages, current) {
$('pipeline').innerHTML = stages.map(function(st) {
var parts = st.split('\u2192');
return '<span class="pipe-step ' + (current===parts[0]?'active':'') + '">' + parts[1] + '</span>';
}).join('<i>\u2192</i>');
}
có&—ttzdzS)Nuî
var examples = [
{listA:[4,1,8,4,5], listB:[5,6,1,8,4,5], skipA:2, skipB:3, label:'示例1: 交点在8'},
{listA:[1,9,1,2,4], listB:[3,2,4], skipA:3, skipB:1, label:'示例2: 交点在2'},
{listA:[2,6,4], listB:[1,5], skipA:-1, skipB:-1, label:'示例3: �相交'},
];
var steps, stepCtrl;
function buildSteps(ex) {
steps = [];
var a = ex.listA, b = ex.listB;
var skipA = ex.skipA, skipB = ex.skipB;
var intersect = skipA >= 0;
steps.push({stage:'init', msg:'�指针拼接法:pA走完A走B,pB走完B走A,若相交必在交点相�', pA:0, pB:0, onA:true, pBonA:true});
var iA = 0, iB = 0, onA = true, pBonA = true;
var maxIter = a.length + b.length + 2;
for (var iter = 0; iter < maxIter; iter++) {
if (intersect && iA === skipA && iB === skipB) {
steps.push({stage:'found', msg:'pA 和 pB 在交点 ' + a[skipA] + ' 相��', pA:skipA, pB:skipB, onA:true, pBonA:true, found:true});
return;
}
if (iA >= a.length && iB >= b.length) {
steps.push({stage:'done', msg:'pA 和 pB �时到达末尾,链表�相交', pA:a.length, pB:b.length, onA:false, pBonA:false, noIntersect:true});
return;
}
steps.push({stage:'step', msg:'pA 在 ' + (onA?'A':'B') + '[' + iA + ']=' + (onA?(iA<a.length?a[iA]:'null'):(iA<b.length?b[iA]:'null')) + ', pB 在 ' + (pBonA?'A':'B') + '[' + iB + ']=' + (pBonA?(iB<a.length?a[iB]:'null'):(iB<b.length?b[iB]:'null')), pA:iA, pB:iB, onA:onA, pBonA:pBonA});
iA++;
if (iA >= (onA ? a.length : b.length)) {
onA = !onA;
iA = 0;
}
iB++;
if (iB >= (pBonA ? a.length : b.length)) {
pBonA = !pBonA;
iB = 0;
}
}
steps.push({stage:'done', msg:'�历完毕,链表�相交', pA:a.length, pB:b.length, onA:false, pBonA:false, noIntersect:true});
}
function render(step) {
var s = steps[step];
var ex = examples[0];
var exIdx = parseInt($('exampleSelect').value) || 0;
ex = examples[exIdx];
var hlA = {}, hlB = {};
if (s.pA < ex.listA.length && s.onA) hlA[s.pA] = 'current';
if (s.pB < ex.listA.length && s.pBonA) hlB[s.pB] = 'active';
if (!s.pBonA && s.pB < ex.listB.length) hlB[s.pB] = 'active';
if (!s.onA && s.pA < ex.listB.length) hlA[s.pA] = 'current';
if (s.found) { hlA[ex.skipA] = 'active'; hlB[ex.skipB] = 'active'; }
var viz = llViz(ex.listA, {highs:hlA, ptrs:{pA: s.onA ? s.pA : -1}, label:'链表 A'});
viz += llViz(ex.listB, {highs:hlB, ptrs:{pB: s.pBonA ? s.pB : -1}, label:'链表 B'});
if (s.found) viz += '<div style="margin-top:8px;color:var(--green);font-weight:700;">交点值为 ' + ex.listA[ex.skipA] + '</div>';
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.found) $('resultContent').innerHTML = '<div class="final-answer">交点引用 = 节点 ' + ex.listA[ex.skipA] + '</div>';
if (s.noIntersect) $('resultContent').innerHTML = '<div class="final-answer" style="border-color:#f87171;background:#fef2f2;">null — 链表�相交</div>';
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','step\u2192移动','found\u2192找到','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = 'A=[4,1,8,4,5] B=[5,6,1,8,4,5] skipA=2 skipB=3';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ var e=examples[parseInt($('exampleSelect').value)]; buildSteps(e); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def getIntersectionNode(headA, headB):\n pA, pB = headA, headB\n while pA != pB:\n pA = pA.next if pA else headB\n pB = pB.next if pB else headA\n return pA', {lang:'Python'});
©Ú_LLVIZÚ_PIPELINE_HELPER©óúO/app/working/workspaces/default/code/illustrated-algorithm/linked_list_algos.pyÚ#js_intersection_of_two_linked_listsr 5s€Ý Õ$Ñ $ðR(ñR ðRrcó&—ttzdzS)NuÎ
var examples = [
{input:[1,2,3,4,5], label:'示例1: [1,2,3,4,5]'},
{input:[1,2], label:'示例2: [1,2]'},
{input:[], label:'示例3: []'},
];
var nodes, steps, stepCtrl;
function buildSteps(arr) {
nodes = arr.slice(); steps = [];
steps.push({stage:'init', msg:'迭代法:prev=null, curr=头节点, �步将 curr.next 指� prev', prev:-1, curr:0, next:1});
var prev = -1, curr = 0;
while (curr < arr.length) {
var nxt = curr + 1;
steps.push({stage:'visit', msg:'访问 curr=' + arr[curr] + ':�存 next=' + (nxt<arr.length?arr[nxt]:'null') + ',将 ' + arr[curr] + '.next 指� ' + (prev>=0?arr[prev]:'null'), prev:prev, curr:curr, next:nxt});
prev = curr;
curr = nxt;
steps.push({stage:'shift', msg:'prev 移到 ' + arr[prev] + ',curr 移到 ' + (curr<arr.length?arr[curr]:'null'), prev:prev, curr:curr, next:curr});
}
steps.push({stage:'done', msg:'�转完�,新头节点为 ' + (prev>=0?arr[prev]:'null'), prev:prev, curr:-1, next:-1});
}
function render(step) {
var s = steps[step];
var hl = {};
if (s.curr >= 0 && s.curr < nodes.length) hl[s.curr] = 'current';
if (s.prev >= 0 && s.prev < nodes.length) hl[s.prev] = 'active';
if (s.next < nodes.length && s.next >= 0) hl[s.next] = 'null-node';
// Show reversed portion in green, unreversed in blue
for (var i = 0; i < nodes.length; i++) {
if (s.prev >= 0 && i <= s.prev && i > s.curr) {
hl[i] = 'active';
}
}
var viz = llViz(nodes, {highs:hl, ptrs:{prev:s.prev>=0&&s.prev<nodes.length?s.prev:-1, curr:s.curr>=0&&s.curr<nodes.length?s.curr:-1, next:s.next<nodes.length&&s.next>=0?s.next:-1}, label:'链表状�'});
if (s.prev >= 0) {
var reversed = [];
for (var i = s.prev; i >= 0; i--) reversed.push(nodes[i]);
viz += llViz(reversed, {highs:{}, label:'已�转部分', showNull:false});
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
var result = [];
for (var i = nodes.length-1; i >= 0; i--) result.push(nodes[i]);
$('resultContent').innerHTML = '<div class="final-answer">�转结果:<b>[' + result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','visit\u2192访问','shift\u2192移动','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[1,2,3,4,5]';
buildSteps(examples[0].input);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ try { var arr=JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); } catch(e){ alert('请输入�法 JSON 数组'); } };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)].input); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def reverseList(head):\n prev, curr = None, head\n while curr:\n nxt = curr.next\n curr.next = prev\n prev = curr\n curr = nxt\n return prev', {lang:'Python'});
rrrrÚjs_reverse_linked_listr ‹s€Ý Õ$Ñ $ðF(ñF ðFrcó&—ttzdzS)Nu’
var examples = [
{input:[1,2,2,1], label:'示例1: [1,2,2,1] 回文'},
{input:[1,2], label:'示例2: [1,2] �回文'},
{input:[1,2,3,2,1], label:'示例3: [1,2,3,2,1] 回文'},
];
var nodes, steps, stepCtrl;
function buildSteps(arr) {
nodes = arr.slice(); steps = [];
if (arr.length === 0) { steps.push({stage:'done',msg:'空链表是回文',phase:'done'}); return; }
steps.push({stage:'init', msg:'快慢指针找中点 + �转��部分 + 比较', phase:'find_mid', slow:0, fast:0});
// Phase 1: find middle
var slow = 0, fast = 0;
while (fast < arr.length - 1 && fast + 1 < arr.length - 1) {
slow++; fast += 2;
steps.push({stage:'move', msg:'slow→'+arr[slow]+', fast→'+arr[Math.min(fast,arr.length-1)], phase:'find_mid', slow:slow, fast:Math.min(fast,arr.length-1)});
}
var mid = slow + 1;
steps.push({stage:'mid', msg:'中点��起始�置:index ' + mid + ' (值=' + arr[mid] + ')', phase:'find_mid', slow:slow, fast:Math.min(fast,arr.length-1), mid:mid});
// Phase 2: reverse second half
var second = arr.slice(mid);
var reversed = second.slice().reverse();
steps.push({stage:'reverse', msg:'�转�� [' + second.join(',') + '] → [' + reversed.join(',') + ']', phase:'reverse', second:second.slice(), reversed:reversed.slice()});
// Phase 3: compare
var first = arr.slice(0, arr.length - second.length);
var isPalin = true;
for (var i = 0; i < reversed.length; i++) {
if (first[i] !== reversed[i]) { isPalin = false; break; }
}
steps.push({stage:'compare', msg:'�� ['+first.join(',')+'] vs �转�� ['+reversed.join(',')+'] → ' + (isPalin?'匹� ✓':'�匹� ✗'), phase:'compare', first:first, reversed:reversed, isPalin:isPalin});
steps.push({stage:'done', msg:'结果:' + (isPalin?'是回文链表':'�是回文链表'), phase:'done', isPalin:isPalin});
}
function render(step) {
var s = steps[step];
var viz = '';
if (s.phase === 'find_mid' || s.stage === 'init') {
var hl = {};
if (s.slow >= 0 && s.slow < nodes.length) hl[s.slow] = 'active';
if (s.fast >= 0 && s.fast < nodes.length) hl[s.fast] = 'current';
viz = llViz(nodes, {highs:hl, ptrs:{slow:s.slow, fast:s.fast}, label:'寻找中点'});
} else if (s.phase === 'reverse') {
viz = llViz(nodes, {highs:{}, label:'原始链表'});
viz += llViz(s.reversed, {highs:{}, label:'�转��', showNull:false});
} else if (s.phase === 'compare' || s.phase === 'done') {
viz = llViz(s.first, {label:'��部分', showNull:false});
viz += llViz(s.reversed, {label:'�转��', showNull:false});
viz += '<div style="margin-top:8px;font-size:15px;">' + (s.isPalin ? '<span style="color:var(--green);font-weight:700;">✓ 匹�</span>' : '<span style="color:var(--red);font-weight:700;">✗ �匹�</span>') + '</div>';
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">' + (s.isPalin?'是回文链表 ✓':'�是回文链表 ✗') + '</div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192开始','move\u2192快慢','mid\u2192中点','reverse\u2192�转','compare\u2192比较','done\u2192结果'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[1,2,2,1]';
buildSteps(examples[0].input);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ try { var arr=JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); } catch(e){ alert('请输入�法 JSON 数组'); } };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)].input); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def isPalindrome(head):\n slow = fast = head\n while fast and fast.next:\n slow = slow.next\n fast = fast.next.next\n prev = None\n while slow:\n nxt = slow.next\n slow.next = prev\n prev = slow\n slow = nxt\n left, right = head, prev\n while right:\n if left.val != right.val:\n return False\n left = left.next\n right = right.next\n return True', {lang:'Python'});
rrrrÚjs_palindrome_linked_listr
Õs€Ý Õ$Ñ $ðN(ñN ðNrcó&—ttzdzS)Nup
var examples = [
{input:[3,2,0,-4], pos:1, label:'示例1: [3,2,0,-4] pos=1 有环'},
{input:[1,2], pos:0, label:'示例2: [1,2] pos=0 有环'},
{input:[1], pos:-1, label:'示例3: [1] 无环'},
];
var nodes, steps, stepCtrl;
function buildSteps(ex) {
nodes = ex.input.slice(); steps = [];
var hasCycle = ex.pos >= 0;
steps.push({stage:'init', msg:'快慢指针:slow�次走1步,fast�次走2步,若相�则有环', slow:0, fast:0, cyclePos:ex.pos});
var slow = 0, fast = 0, n = nodes.length;
var maxIter = n + 2;
for (var iter = 0; iter < maxIter; iter++) {
slow = (slow + 1) % n;
fast = (fast + 2) % n;
if (!hasCycle) {
if (fast >= n) {
steps.push({stage:'noreturn', msg:'fast 到达链表末尾,无环', slow:slow, fast:n, cyclePos:-1});
steps.push({stage:'done', msg:'链表无环', slow:slow, fast:n, cyclePos:-1, result:false});
return;
}
}
steps.push({stage:'step', msg:'slow→'+nodes[slow]+', fast→'+nodes[fast] + (slow===fast&&hasCycle?' 相��有环�':''), slow:slow, fast:fast, cyclePos:ex.pos, met:slow===fast&&hasCycle});
if (slow === fast && hasCycle) {
steps.push({stage:'done', msg:'快慢指针在节点 '+nodes[slow]+' 相�,链表有环', slow:slow, fast:fast, cyclePos:ex.pos, result:true});
return;
}
}
steps.push({stage:'done', msg:'�历完毕,链表无环', slow:0, fast:0, cyclePos:-1, result:false});
}
function render(step) {
var s = steps[step];
var hl = {};
if (s.slow >= 0 && s.slow < nodes.length) hl[s.slow] = 'active';
if (s.fast >= 0 && s.fast < nodes.length) {
hl[s.fast] = s.slow === s.fast ? 'active' : 'current';
}
var viz = llViz(nodes, {highs:hl, ptrs:{slow:s.slow>=0&&s.slow<nodes.length?s.slow:-1, fast:s.fast>=0&&s.fast<nodes.length?s.fast:-1}, cycle:s.cyclePos, label:'链表'});
if (s.met) viz += '<div style="margin-top:8px;font-size:15px;color:var(--green);font-weight:700;">⭕ 快慢指针相��有环�</div>';
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = s.result
? '<div class="final-answer">链表有环 ✓ (环入�在 index ' + s.cyclePos + ')</div>'
: '<div class="final-answer" style="border-color:#f87171;background:#fef2f2;">链表无环 ✗</div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','step\u2192移动','noreturn\u2192无返回','done\u2192结果'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[3,2,0,-4] pos=1';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def hasCycle(head):\n slow = fast = head\n while fast and fast.next:\n slow = slow.next\n fast = fast.next.next\n if slow is fast:\n return True\n return False', {lang:'Python'});
rrrrÚjs_linked_list_cycler'ó€Ý Õ$Ñ $ðH(ñH ðHrcó&—ttzdzS)Nu�
var examples = [
{input:[3,2,0,-4], pos:1, label:'示例1: [3,2,0,-4] pos=1'},
{input:[1,2], pos:0, label:'示例2: [1,2] pos=0'},
{input:[1], pos:-1, label:'示例3: 无环'},
];
var nodes, steps, stepCtrl;
function buildSteps(ex) {
nodes = ex.input.slice(); steps = [];
var hasCycle = ex.pos >= 0;
var n = nodes.length;
steps.push({stage:'init', msg:'Floyd 算法:Phase1 快慢指针检测环,Phase2 找环入�', phase:'detect', slow:0, fast:0, cyclePos:ex.pos});
if (!hasCycle) {
steps.push({stage:'done', msg:'链表无环,返回 null', phase:'done', entry:-1, result:null});
return;
}
// Phase 1: detect
var slow = 0, fast = 0;
var maxD = n + 2;
for (var d = 0; d < maxD; d++) {
slow = (slow + 1) % n;
fast = (fast + 2) % n;
steps.push({stage:'detect', msg:'Phase1: slow→'+nodes[slow]+', fast→'+nodes[fast], phase:'detect', slow:slow, fast:fast, cyclePos:ex.pos});
if (slow === fast) {
steps.push({stage:'met', msg:'Phase1: 快慢在 '+nodes[slow]+' 相�,确认有环', phase:'detect', slow:slow, fast:fast, cyclePos:ex.pos});
break;
}
}
// Phase 2: find entry
steps.push({stage:'phase2', msg:'Phase2: ptr1 从头出�,ptr2 从相�点出�,�走1步', phase:'entry', ptr1:0, ptr2:slow, cyclePos:ex.pos});
var ptr1 = 0, ptr2 = slow;
for (var e = 0; e < n + 2; e++) {
if (ptr1 === ptr2) {
steps.push({stage:'found', msg:'Phase2: ptr1 和 ptr2 在 '+nodes[ptr1]+' 相�,这就是环入��', phase:'entry', ptr1:ptr1, ptr2:ptr2, cyclePos:ex.pos, entry:ptr1});
break;
}
ptr1 = (ptr1 + 1) % n;
ptr2 = (ptr2 + 1) % n;
steps.push({stage:'seek', msg:'Phase2: ptr1→'+nodes[ptr1]+', ptr2→'+nodes[ptr2], phase:'entry', ptr1:ptr1, ptr2:ptr2, cyclePos:ex.pos});
}
steps.push({stage:'done', msg:'环入�为节点 '+nodes[ex.pos]+' (index '+ex.pos+')', phase:'done', entry:ex.pos, cyclePos:ex.pos});
}
function render(step) {
var s = steps[step];
var hl = {};
var ptrs = {};
if (s.phase === 'detect') {
if (s.slow >= 0 && s.slow < nodes.length) hl[s.slow] = 'active';
if (s.fast >= 0 && s.fast < nodes.length) hl[s.fast] = s.slow===s.fast ? 'active' : 'current';
ptrs = {slow:s.slow, fast:s.fast};
} else if (s.phase === 'entry') {
if (s.ptr1 >= 0 && s.ptr1 < nodes.length) hl[s.ptr1] = 'active';
if (s.ptr2 >= 0 && s.ptr2 < nodes.length) hl[s.ptr2] = s.ptr1===s.ptr2 ? 'active' : 'current';
ptrs = {ptr1:s.ptr1, ptr2:s.ptr2};
}
var viz = llViz(nodes, {highs:hl, ptrs:ptrs, cycle:s.cyclePos||-1, label:'链表'});
if (s.stage === 'found' || s.stage === 'met') {
viz += '<div style="margin-top:8px;font-size:15px;color:var(--green);font-weight:700;">🎯 ' + s.msg + '</div>';
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
if (s.entry >= 0) {
$('resultContent').innerHTML = '<div class="final-answer">环入� = 节点 <b>'+nodes[s.entry]+'</b> (index '+s.entry+')</div>';
} else {
$('resultContent').innerHTML = '<div class="final-answer" style="border-color:#f87171;background:#fef2f2;">无环,返回 null</div>';
}
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','detect\u2192检测','met\u2192相�','phase2\u2192找入�','seek\u2192�索','found\u2192找到','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[3,2,0,-4] pos=1';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def detectCycle(head):\n slow = fast = head\n while fast and fast.next:\n slow = slow.next\n fast = fast.next.next\n if slow is fast:\n ptr1, ptr2 = head, slow\n while ptr1 is not ptr2:\n ptr1 = ptr1.next\n ptr2 = ptr2.next\n return ptr1\n return None', {lang:'Python'});
rrrrÚjs_linked_list_cycle_iirss€Ý Õ$Ñ $ð](ñ] ð]rcó&—ttzdzS)Nuž
var examples = [
{list1:[1,2,4], list2:[1,3,4], label:'示例1: [1,2,4]+[1,3,4]'},
{list1:[], list2:[], label:'示例2: 两空链表'},
{list1:[], list2:[0], label:'示例3: []+[0]'},
];
var steps, stepCtrl;
function buildSteps(ex) {
steps = [];
var l1 = ex.list1.slice(), l2 = ex.list2.slice();
steps.push({stage:'init', msg:'�指针�并:比较两个链表头,�较�者加入结果', i1:0, i2:0, merged:[], phase:'merge'});
var i1 = 0, i2 = 0, merged = [];
while (i1 < l1.length && i2 < l2.length) {
steps.push({stage:'compare', msg:'比较 l1['+i1+']=' + l1[i1] + ' vs l2['+i2+']=' + l2[i2], i1:i1, i2:i2, merged:merged.slice(), phase:'merge'});
if (l1[i1] <= l2[i2]) {
merged.push(l1[i1]);
steps.push({stage:'take1', msg:'� l1['+i1+']=' + l1[i1] + ' 加入结果', i1:i1+1, i2:i2, merged:merged.slice(), phase:'merge'});
i1++;
} else {
merged.push(l2[i2]);
steps.push({stage:'take2', msg:'� l2['+i2+']=' + l2[i2] + ' 加入结果', i1:i1, i2:i2+1, merged:merged.slice(), phase:'merge'});
i2++;
}
}
while (i1 < l1.length) { merged.push(l1[i1]); i1++; }
while (i2 < l2.length) { merged.push(l2[i2]); i2++; }
steps.push({stage:'done', msg:'�并完�:[' + merged.join(',') + ']', i1:i1, i2:i2, merged:merged.slice(), phase:'done'});
}
function render(step) {
var s = steps[step];
var exIdx = parseInt($('exampleSelect').value) || 0;
var ex = examples[exIdx];
var hl1 = {}, hl2 = {};
if (s.i1 < ex.list1.length) {
hl1[s.i1] = s.stage === 'take1' ? 'active' : 'current';
}
if (s.i2 < ex.list2.length) {
hl2[s.i2] = s.stage === 'take2' ? 'active' : 'current';
}
var viz = llViz(ex.list1, {highs:hl1, ptrs:{i1:s.i1<ex.list1.length?s.i1:-1}, label:'链表 1'});
viz += llViz(ex.list2, {highs:hl2, ptrs:{i2:s.i2<ex.list2.length?s.i2:-1}, label:'链表 2'});
if (s.merged.length > 0) {
viz += llViz(s.merged, {highs:{[s.merged.length-1]:'active'}, label:'�并结果', showNull:s.phase==='done'?true:false});
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">�并结果:<b>[' + s.merged.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','compare\u2192比较','take1\u2192�L1','take2\u2192�L2','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = 'L1=[1,2,4] L2=[1,3,4]';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def mergeTwoLists(l1, l2):\n dummy = ListNode(0)\n curr = dummy\n while l1 and l2:\n if l1.val <= l2.val:\n curr.next = l1\n l1 = l1.next\n else:\n curr.next = l2\n l2 = l2.next\n curr = curr.next\n curr.next = l1 or l2\n return dummy.next', {lang:'Python'});
rrrrÚjs_merge_two_sorted_listsrÔs€Ý Õ$Ñ $ðJ(ñJ ðJrcó&—ttzdzS)NuF
var examples = [
{l1:[2,4,3], l2:[5,6,4], label:'示例1: 342+465=807'},
{l1:[0], l2:[0], label:'示例2: 0+0=0'},
{l1:[9,9,9,9,9,9,9], l2:[9,9,9,9], label:'示例3: 大数相加'},
];
var steps, stepCtrl;
function buildSteps(ex) {
steps = [];
var l1 = ex.l1.slice(), l2 = ex.l2.slice();
steps.push({stage:'init', msg:'��相加 + 进�,链表已按逆�存储��数字', i1:0, i2:0, carry:0, result:[], phase:'add'});
var i1 = 0, i2 = 0, carry = 0, result = [];
while (i1 < l1.length || i2 < l2.length || carry) {
var v1 = i1 < l1.length ? l1[i1] : 0;
var v2 = i2 < l2.length ? l2[i2] : 0;
var sum = v1 + v2 + carry;
var digit = sum % 10;
var newCarry = Math.floor(sum / 10);
steps.push({stage:'calc', msg: v1+' + '+v2+' + '+carry+'(carry) = '+sum+' → 写 '+digit+',进� '+newCarry, i1:i1, i2:i2, carry:carry, sum:sum, digit:digit, newCarry:newCarry, result:result.slice(), phase:'add'});
result.push(digit);
carry = newCarry;
i1++; i2++;
}
steps.push({stage:'done', msg:'结果:[' + result.join(',') + ']', result:result.slice(), phase:'done'});
}
function render(step) {
var s = steps[step];
var exIdx = parseInt($('exampleSelect').value) || 0;
var ex = examples[exIdx];
var hl1 = {}, hl2 = {}, hlR = {};
if (s.i1 < ex.l1.length) hl1[s.i1] = 'current';
if (s.i2 < ex.l2.length) hl2[s.i2] = 'current';
if (s.digit !== undefined && s.result) {
for (var ri = 0; ri < s.result.length; ri++) hlR[ri] = 'active';
}
var viz = llViz(ex.l1, {highs:hl1, ptrs:{p1:s.i1<ex.l1.length?s.i1:-1}, label:'链表1 (逆�)'});
viz += llViz(ex.l2, {highs:hl2, ptrs:{p2:s.i2<ex.l2.length?s.i2:-1}, label:'链表2 (逆�)'});
if (s.carry !== undefined && s.stage !== 'done') {
viz += '<div style="margin:6px 0;font-size:14px;"><b>carry = ' + s.carry + '</b></div>';
}
if (s.result && s.result.length > 0) {
viz += llViz(s.result, {highs:hlR, label:'结果 (逆�)'});
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
var num1 = ex.l1.slice().reverse().join('');
var num2 = ex.l2.slice().reverse().join('');
var numR = s.result.slice().reverse().join('');
$('resultContent').innerHTML = '<div class="final-answer">' + num1 + ' + ' + num2 + ' = ' + numR + '<br>结果链表:<b>[' + s.result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192开始','calc\u2192计算','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = 'l1=[2,4,3] l2=[5,6,4]';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def addTwoNumbers(l1, l2):\n dummy = ListNode(0)\n curr = dummy\n carry = 0\n while l1 or l2 or carry:\n v1 = l1.val if l1 else 0\n v2 = l2.val if l2 else 0\n s = v1 + v2 + carry\n carry = s // 10\n curr.next = ListNode(s % 10)\n curr = curr.next\n l1 = l1.next if l1 else None\n l2 = l2.next if l2 else None\n return dummy.next', {lang:'Python'});
rrrrÚjs_add_two_numbersr"s€Ý Õ$Ñ $ðL(ñL ðLrcó&—ttzdzS)NuZ
var examples = [
{input:[1,2,3,4,5], n:2, label:'示例1: [1,2,3,4,5] n=2 删除4'},
{input:[1], n:1, label:'示例2: [1] n=1 删除1'},
{input:[1,2], n:1, label:'示例3: [1,2] n=1 删除2'},
];
var nodes, steps, stepCtrl;
function buildSteps(ex) {
nodes = ex.input.slice(); steps = [];
var n = ex.n;
var len = nodes.length;
steps.push({stage:'init', msg:'快慢指针:fast 先走 n 步,然� slow 和 fast �步走', slow:-1, fast:0, phase:'advance'});
// Use dummy node represented as index -1
var slow = -1, fast = 0;
// fast advances n steps
for (var i = 0; i < n; i++) {
steps.push({stage:'advance', msg:'fast �进:' + (i+1) + '/' + n + ' 步,fast=' + (fast < len ? nodes[fast] : 'null'), slow:-1, fast:fast, phase:'advance'});
fast++;
}
steps.push({stage:'ready', msg:'fast 已领先 ' + n + ' 步,现在 slow 和 fast �步�进', slow:-1, fast:fast, phase:'move'});
// both advance until fast reaches end
while (fast < len) {
slow++;
if (fast < len) fast++;
steps.push({stage:'move', msg:'slow→' + (slow>=0?nodes[slow]:'dummy') + ', fast→' + (fast<len?nodes[fast]:'null'), slow:slow, fast:fast, phase:'move'});
}
// slow.next is the node to delete
var delIdx = slow + 1;
steps.push({stage:'delete', msg:'删除 slow.next = 节点 ' + nodes[delIdx] + ' (index ' + delIdx + ')', slow:slow, fast:fast, delIdx:delIdx, phase:'delete'});
var result = nodes.slice();
result.splice(delIdx, 1);
steps.push({stage:'done', msg:'删除�结果:[' + result.join(',') + ']', result:result, phase:'done'});
}
function render(step) {
var s = steps[step];
var hl = {};
var ptrs = {};
if (s.slow >= 0 && s.slow < nodes.length) { hl[s.slow] = 'active'; ptrs.slow = s.slow; }
if (s.fast >= 0 && s.fast < nodes.length) { hl[s.fast] = 'current'; ptrs.fast = s.fast; }
if (s.delIdx !== undefined && s.delIdx < nodes.length) hl[s.delIdx] = 'null-node';
var vizLabel = '链表 (n=' + examples[parseInt($('exampleSelect').value)||0].n + ')';
var viz = llViz(nodes, {highs:hl, ptrs:ptrs, label:vizLabel});
if (s.slow === -1 && (s.phase === 'advance' || s.phase === 'ready')) {
viz += '<div style="font-size:12px;color:var(--text-muted);">slow 在 dummy 节点</div>';
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">删除�:<b>[' + s.result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','advance\u2192快进','ready\u2192就绪','move\u2192�步','delete\u2192删除','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[1,2,3,4,5] n=2';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def removeNthFromEnd(head, n):\n dummy = ListNode(0, head)\n fast = slow = dummy\n for _ in range(n):\n fast = fast.next\n while fast.next:\n slow = slow.next\n fast = fast.next\n slow.next = slow.next.next\n return dummy.next', {lang:'Python'});
rrrrÚ#js_remove_nth_node_from_end_of_listrrs€Ý Õ$Ñ $ðK(ñK ðKrcó&—ttzdzS)NuÎ
var examples = [
{input:[1,2,3,4], label:'示例1: [1,2,3,4]'},
{input:[], label:'示例2: []'},
{input:[1], label:'示例3: [1]'},
{input:[1,2,3], label:'示例4: [1,2,3]'},
];
var nodes, steps, stepCtrl;
function buildSteps(arr) {
nodes = arr.slice(); steps = [];
if (arr.length < 2) {
steps.push({stage:'done', msg:'节点数�足2,无需交�', result:nodes.slice(), phase:'done'});
return;
}
steps.push({stage:'init', msg:'两两交�:对�对相邻节点 (a,b) 进行三步指针交�', pairStart:0, phase:'swap'});
var result = nodes.slice();
for (var i = 0; i + 1 < result.length; i += 2) {
steps.push({stage:'identify', msg:'识别交�对:' + result[i] + ' ↔ ' + result[i+1], pairStart:i, result:result.slice(), phase:'swap'});
// swap
var tmp = result[i];
result[i] = result[i+1];
result[i+1] = tmp;
steps.push({stage:'swap', msg:'交� ' + nodes[i] + ' 和 ' + nodes[i+1] + ':[' + result.join(',') + ']', pairStart:i, result:result.slice(), phase:'swap'});
}
steps.push({stage:'done', msg:'交�完�:[' + result.join(',') + ']', result:result.slice(), phase:'done'});
}
function render(step) {
var s = steps[step];
var hl = {};
if (s.pairStart !== undefined && s.pairStart + 1 < nodes.length) {
if (s.stage === 'identify') {
hl[s.pairStart] = 'current';
hl[s.pairStart + 1] = 'current';
} else if (s.stage === 'swap') {
hl[s.pairStart] = 'active';
hl[s.pairStart + 1] = 'active';
}
}
var viz = llViz(s.result || nodes, {highs:hl, ptrs:{}, label:'链表状�'});
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">交�结果:<b>[' + s.result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','identify\u2192识别','swap\u2192交�','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[1,2,3,4]';
buildSteps(examples[0].input);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ try { var arr=JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); } catch(e){ alert('请输入�法 JSON 数组'); } };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)].input); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def swapPairs(head):\n dummy = ListNode(0, head)\n prev = dummy\n while prev.next and prev.next.next:\n a, b = prev.next, prev.next.next\n prev.next = b\n a.next = b.next\n b.next = a\n prev = a\n return dummy.next', {lang:'Python'});
rrrrÚjs_swap_nodes_in_pairsrÁs€Ý Õ$Ñ $ðE(ñE ðErcó&—ttzdzS)NuJ
var examples = [
{input:[1,2,3,4,5], k:2, label:'示例1: [1,2,3,4,5] k=2'},
{input:[1,2,3,4,5], k:3, label:'示例2: [1,2,3,4,5] k=3'},
{input:[1,2,3,4,5], k:1, label:'示例3: k=1 �翻转'},
];
var nodes, steps, stepCtrl;
function buildSteps(ex) {
nodes = ex.input.slice(); steps = [];
var k = ex.k;
var result = nodes.slice();
steps.push({stage:'init', msg:'K个一组翻转链表,k=' + k + ':�组k个节点内部�转,�足k个�翻转', phase:'group', result:result.slice(), groupIdx:0});
var groupIdx = 0;
for (var i = 0; i + k <= result.length; i += k) {
steps.push({stage:'identify', msg:'第 ' + (groupIdx+1) + ' 组:[' + result.slice(i, i+k).join(',') + ']', phase:'group', result:result.slice(), groupStart:i, groupIdx:groupIdx});
// reverse in-place
var left = i, right = i + k - 1;
while (left < right) {
var tmp = result[left];
result[left] = result[right];
result[right] = tmp;
left++; right--;
}
steps.push({stage:'reverse', msg:'第 ' + (groupIdx+1) + ' 组翻转�:[' + result.slice(i, i+k).join(',') + ']', phase:'group', result:result.slice(), groupStart:i, groupIdx:groupIdx});
groupIdx++;
}
var remaining = result.length % k;
if (remaining > 0) {
steps.push({stage:'skip', msg:'剩余 ' + remaining + ' 个节点�足 k=' + k + ',�翻转', phase:'group', result:result.slice()});
}
steps.push({stage:'done', msg:'翻转完�:[' + result.join(',') + ']', phase:'done', result:result.slice()});
}
function render(step) {
var s = steps[step];
var hl = {};
if (s.groupStart !== undefined) {
var k = examples[parseInt($('exampleSelect').value)||0].k;
for (var gi = s.groupStart; gi < s.groupStart + k && gi < nodes.length; gi++) {
hl[gi] = s.stage === 'reverse' ? 'active' : 'current';
}
}
var viz = llViz(s.result || nodes, {highs:hl, label:'链表状�'});
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">结果:<b>[' + s.result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','identify\u2192识别组','reverse\u2192翻转','skip\u2192跳过','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[1,2,3,4,5] k=2';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def reverseKGroup(head, k):\n def reverse(head, tail):\n prev = None\n curr = head\n while curr != tail:\n nxt = curr.next\n curr.next = prev\n prev = curr\n curr = nxt\n return prev\n\n dummy = ListNode(0, head)\n prev = dummy\n while True:\n tail = prev.next\n for _ in range(k):\n if not tail: return dummy.next\n tail = tail.next\n new_head = reverse(prev.next, tail)\n tmp = prev.next\n prev.next = new_head\n tmp.next = tail\n prev = tmp\n return dummy.next', {lang:'Python'});
rrrrÚjs_reverse_nodes_in_k_groupr
rrcó&—ttzdzS)NuZ
var examples = [
{nodes:[[7,null],[13,0],[11,4],[10,2],[1,0]], label:'示例1: 5个节点'},
{nodes:[[1,1],[2,1]], label:'示例2: 2个节点'},
{nodes:[[3,null],[3,0],[3,null]], label:'示例3: 3个节点'},
];
var steps, stepCtrl;
function buildSteps(ex) {
steps = [];
var n = ex.nodes.length;
steps.push({stage:'init', msg:'交错�制法:①在�个原节点��入�制节点 ②设置random指针 ③拆分', phase:'interleave'});
// Phase 1: interleave
var interleaved = [];
for (var i = 0; i < n; i++) {
interleaved.push({val:ex.nodes[i][0], isCopy:false, origIdx:i});
interleaved.push({val:ex.nodes[i][0], isCopy:true, origIdx:i});
}
steps.push({stage:'interleave', msg:'Phase1:在�个原节点��入其�制 → ' + interleaved.map(function(x){return x.val+(x.isCopy?"'":"");}).join(','), phase:'interleave', interleaved:interleaved.slice()});
// Phase 2: set random pointers
var randomLinks = [];
for (var i = 0; i < n; i++) {
var rnd = ex.nodes[i][1];
if (rnd !== null) {
randomLinks.push({from:i*2+1, to:rnd*2+1, msg:"copy["+i+"].random = copy["+rnd+"] (值="+ex.nodes[rnd][0]+")"});
} else {
randomLinks.push({from:i*2+1, to:-1, msg:"copy["+i+"].random = null"});
}
}
steps.push({stage:'random', msg:'Phase2:设置random指针 → ' + randomLinks.map(function(r){return r.msg;}).join('; '), phase:'random', interleaved:interleaved.slice(), randomLinks:randomLinks});
// Phase 3: split
var original = [], copy = [];
for (var i = 0; i < interleaved.length; i++) {
if (i%2===0) original.push(interleaved[i].val);
else copy.push(interleaved[i].val);
}
steps.push({stage:'split', msg:'Phase3:拆分为原链表和�制链表', phase:'split', original:original, copy:copy});
steps.push({stage:'done', msg:'深拷�完�', phase:'done', copy:copy});
}
function renderInterleaved(interleaved, hlIdx) {
var html = '<div style="display:flex;flex-wrap:wrap;align-items:center;gap:4px;padding:8px 0;">';
for (var i = 0; i < interleaved.length; i++) {
var n = interleaved[i];
var bg = n.isCopy ? 'var(--green-light)' : 'var(--blue-light)';
var border = n.isCopy ? 'var(--green)' : 'var(--blue)';
var label = n.val + (n.isCopy ? "'" : '');
var hl = (hlIdx === i) ? 'box-shadow:0 0 0 3px var(--orange);' : '';
html += '<div style="display:inline-flex;flex-direction:column;align-items:center;">';
html += '<div style="min-width:40px;height:36px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:13px;padding:0 8px;border:2px solid '+border+';border-radius:8px;background:'+bg+';'+hl+';">'+label+'</div>';
html += '<div style="font-size:9px;color:var(--text-muted);">'+(n.isCopy?'copy':'orig')+'</div>';
html += '</div>';
if (i < interleaved.length - 1) html += '<span style="color:var(--text-muted);">&rarr;</span>';
}
html += '</div>';
return html;
}
function render(step) {
var s = steps[step];
var viz = '';
if (s.phase === 'interleave' && s.interleaved) {
viz = renderInterleaved(s.interleaved, -1);
} else if (s.phase === 'random' && s.interleaved) {
viz = renderInterleaved(s.interleaved, -1);
viz += '<div style="margin-top:8px;"><b>Random 指针设置:</b></div>';
s.randomLinks.forEach(function(r) {
viz += '<div style="font-size:13px;margin:2px 0;">' + r.msg + '</div>';
});
} else if (s.phase === 'split' || s.phase === 'done') {
viz = llViz(s.original, {highlights:{}, label:'原链表', showNull:false});
viz += llViz(s.copy, {highlights:{}, label:'�制链表', showNull:false});
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">深拷�链表:<b>[' + s.copy.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','interleave\u2192交错�入','random\u2192设置�机','split\u2192拆分','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[[7,null],[13,0],[11,4],[10,2],[1,0]]';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def copyRandomList(head):\n if not head: return None\n # 1. interleave copies\n cur = head\n while cur:\n copy = Node(cur.val, cur.next, None)\n cur.next = copy\n cur = copy.next\n # 2. set random pointers\n cur = head\n while cur:\n if cur.random:\n cur.next.random = cur.random.next\n cur = cur.next.next\n # 3. split\n cur = head\n copy_head = head.next\n while cur:\n copy = cur.next\n cur.next = copy.next\n if copy.next:\n copy.next = copy.next.next\n cur = cur.next\n return copy_head', {lang:'Python'});
rrrrÚ js_copy_list_with_random_pointerrVs€Ý Õ$Ñ $ðe(ñe ðercó&—ttzdzS)Nu¦
var examples = [
{input:[4,2,1,3], label:'示例1: [4,2,1,3]'},
{input:[-1,5,3,4,0], label:'示例2: [-1,5,3,4,0]'},
{input:[], label:'示例3: []'},
];
var steps, stepCtrl;
function buildSteps(arr) {
steps = [];
if (arr.length === 0) { steps.push({stage:'done',msg:'空链表',result:[],phase:'done'}); return; }
function mergeSort(a, depth) {
if (a.length <= 1) return a;
var mid = Math.floor(a.length / 2);
var left = a.slice(0, mid);
var right = a.slice(mid);
steps.push({stage:'split', msg:'分割 ['+a.join(',')+'] → ['+left.join(',')+'] + ['+right.join(',')+']', phase:'split', depth:depth, arr:a.slice(), left:left, right:right});
var sortedL = mergeSort(left, depth+1);
var sortedR = mergeSort(right, depth+1);
// merge
var merged = [];
var i = 0, j = 0;
while (i < sortedL.length && j < sortedR.length) {
if (sortedL[i] <= sortedR[j]) { merged.push(sortedL[i]); i++; }
else { merged.push(sortedR[j]); j++; }
}
while (i < sortedL.length) { merged.push(sortedL[i]); i++; }
while (j < sortedR.length) { merged.push(sortedR[j]); j++; }
steps.push({stage:'merge', msg:'�并 ['+sortedL.join(',')+'] + ['+sortedR.join(',')+'] → ['+merged.join(',')+']', phase:'merge', depth:depth, sortedL:sortedL, sortedR:sortedR, result:merged});
return merged;
}
steps.push({stage:'init', msg:'归并排�:递归分割 + �并', phase:'init', arr:arr.slice()});
var result = mergeSort(arr.slice(), 0);
steps.push({stage:'done', msg:'排�完�:[' + result.join(',') + ']', phase:'done', result:result});
}
function render(step) {
var s = steps[step];
var viz = '';
if (s.phase === 'init') {
viz = llViz(s.arr, {label:'原始链表'});
} else if (s.phase === 'split') {
viz = llViz(s.arr, {label:'分割�', showNull:false});
viz += llViz(s.left, {label:'左�', showNull:false});
viz += llViz(s.right, {label:'��', showNull:false});
viz += '<div style="margin-top:4px;font-size:12px;color:var(--text-muted);">递归深度: ' + s.depth + '</div>';
} else if (s.phase === 'merge') {
viz = llViz(s.sortedL, {label:'左�(已排�)', showNull:false});
viz += llViz(s.sortedR, {label:'��(已排�)', showNull:false});
viz += llViz(s.result, {highlights:{}, label:'�并结果', showNull:false});
} else if (s.phase === 'done') {
viz = llViz(s.result, {label:'排�结果'});
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">排�结果:<b>[' + s.result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192开始','split\u2192分割','merge\u2192�并','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[4,2,1,3]';
buildSteps(examples[0].input);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ try { var arr=JSON.parse($('inputArea').value); buildSteps(arr); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); } catch(e){ alert('请输入�法 JSON 数组'); } };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)].input); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('def sortList(head):\n if not head or not head.next:\n return head\n # find middle\n slow = fast = head\n while fast.next and fast.next.next:\n slow = slow.next\n fast = fast.next.next\n mid = slow.next\n slow.next = None\n left = sortList(head)\n right = sortList(mid)\n # merge\n dummy = ListNode(0)\n cur = dummy\n while left and right:\n if left.val <= right.val:\n cur.next = left\n left = left.next\n else:\n cur.next = right\n right = right.next\n cur = cur.next\n cur.next = left or right\n return dummy.next', {lang:'Python'});
rrrrÚ js_sort_listr ¿ó€Ý Õ$Ñ $ðS(ñS ðSrcó&—ttzdzS)Nu›
var examples = [
{lists:[[1,4,5],[1,3,4],[2,6]], label:'示例1: 3个链表'},
{lists:[], label:'示例2: 空列表'},
{lists:[[]], label:'示例3: 1个空链表'},
];
var steps, stepCtrl;
function buildSteps(ex) {
steps = [];
var lists = ex.lists.filter(function(l){return l.length>0;});
if (lists.length === 0) { steps.push({stage:'done',msg:'无有效链表',result:[],phase:'done'}); return; }
steps.push({stage:'init', msg:'使用最�堆�并K个有�链表:�次�当�所有链表头的最�值', phase:'init', heads:lists.map(function(l){return 0;}), result:[]});
// simulate min-heap merge
var ptrs = lists.map(function(){return 0;}); // current index in each list
var result = [];
var totalLen = lists.reduce(function(s,l){return s+l.length;}, 0);
for (var step = 0; step < totalLen; step++) {
// find min among current heads
var minVal = Infinity, minIdx = -1;
for (var i = 0; i < lists.length; i++) {
if (ptrs[i] < lists[i].length && lists[i][ptrs[i]] < minVal) {
minVal = lists[i][ptrs[i]];
minIdx = i;
}
}
steps.push({stage:'compare', msg:'比较�链表头:' + lists.map(function(l,i){return ptrs[i]<l.length?l[ptrs[i]]:'∅';}).join(',') + ' → 最�值 '+minVal+' �自链表'+(minIdx+1), phase:'compare', heads:ptrs.slice(), minIdx:minIdx, minVal:minVal, result:result.slice()});
result.push(minVal);
ptrs[minIdx]++;
steps.push({stage:'pick', msg:'� '+minVal+' 加入结果,链表'+(minIdx+1)+'指针�进', phase:'pick', heads:ptrs.slice(), minIdx:minIdx, result:result.slice()});
}
steps.push({stage:'done', msg:'�并完�:[' + result.join(',') + ']', phase:'done', result:result});
}
function render(step) {
var s = steps[step];
var exIdx = parseInt($('exampleSelect').value) || 0;
var ex = examples[exIdx];
var lists = ex.lists.filter(function(l){return l.length>0;});
var viz = '';
var hl, ptrs2;
for (var li = 0; li < lists.length; li++) {
hl = {};
ptrs2 = {};
if (s.heads && s.heads[li] < lists[li].length) {
hl[s.heads[li]] = (s.minIdx === li) ? 'active' : 'current';
ptrs2['L'+(li+1)] = s.heads[li];
}
viz += llViz(lists[li], {highs:hl, ptrs:ptrs2, label:'链表 '+(li+1), showNull:false});
}
if (s.result && s.result.length > 0) {
viz += llViz(s.result, {highlights:{[s.result.length-1]:'active'}, label:'�并结果', showNull:false});
}
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">�并结果:<b>[' + s.result.join(' → ') + ']</b></div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','compare\u2192比较','pick\u2192选�','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = '[[1,4,5],[1,3,4],[2,6]]';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();
$('codeArea').innerHTML = renderCode('import heapq\ndef mergeKLists(lists):\n heap = []\n for i, node in enumerate(lists):\n if node:\n heapq.heappush(heap, (node.val, i, node))\n dummy = ListNode(0)\n cur = dummy\n while heap:\n val, i, node = heapq.heappop(heap)\n cur.next = node\n cur = cur.next\n if node.next:\n heapq.heappush(heap, (node.next.val, i, node.next))\n return dummy.next', {lang:'Python'});
rrrrÚjs_merge_k_sorted_listsr#r!rcó&—ttzdzS)Nu\"
var examples = [
{ops:['LRUCache(2)','put(1,1)','put(2,2)','get(1)','put(3,3)','get(2)','put(4,4)','get(1)','get(3)','get(4)'], label:'示例1: capacity=2'},
{ops:['LRUCache(3)','put(1,1)','put(2,2)','put(3,3)','get(1)','put(4,4)','get(2)'], label:'示例2: capacity=3'},
];
var steps, stepCtrl;
function buildSteps(ex) {
steps = [];
var cap = 2;
var ops = ex.ops;
var capMatch = ops[0].match(/LRUCache\((\d+)\)/);
if (capMatch) cap = parseInt(capMatch[1]);
// DLL: head <-> ... <-> tail (head=most recent, tail=least recent)
var dll = []; // [{key, val}]
var map = {}; // key -> index in dll
var capacity = cap;
steps.push({stage:'init', msg:'LRU 缓存�始化,capacity=' + cap + '。��链表(头=最近,尾=最久)+ 哈希表', dll:[], map:{}, capacity:capacity, op:ops[0]});
for (var opIdx = 1; opIdx < ops.length; opIdx++) {
var op = ops[opIdx];
var getMatch = op.match(/get\((\d+)\)/);
var putMatch = op.match(/put\((\d+),(\d+)\)/);
if (getMatch) {
var key = getMatch[1];
if (key in map) {
// hit: move to front
var idx = map[key];
var node = dll[idx];
dll.splice(idx, 1);
dll.unshift(node);
// rebuild map
map = {};
for (var mi = 0; mi < dll.length; mi++) map[dll[mi].key] = mi;
steps.push({stage:'hit', msg:'get('+key+') 命中�返回 '+node.val+',移到链表头部', dll:JSON.parse(JSON.stringify(dll)), map:JSON.parse(JSON.stringify(map)), capacity:capacity, op:op, hitVal:node.val});
} else {
steps.push({stage:'miss', msg:'get('+key+') 未命中,返回 -1', dll:JSON.parse(JSON.stringify(dll)), map:JSON.parse(JSON.stringify(map)), capacity:capacity, op:op, hitVal:-1});
}
} else if (putMatch) {
var key = putMatch[1];
var val = putMatch[2];
if (key in map) {
// update: move to front
var idx = map[key];
dll[idx].val = val;
var node = dll[idx];
dll.splice(idx, 1);
dll.unshift(node);
map = {};
for (var mi = 0; mi < dll.length; mi++) map[dll[mi].key] = mi;
steps.push({stage:'update', msg:'put('+key+','+val+') 更新已有key,移到头部', dll:JSON.parse(JSON.stringify(dll)), map:JSON.parse(JSON.stringify(map)), capacity:capacity, op:op});
} else {
if (dll.length >= capacity) {
// evict LRU (tail)
var evicted = dll.pop();
delete map[evicted.key];
steps.push({stage:'evict', msg:'put('+key+','+val+') 容�已满,淘汰尾部 key='+evicted.key+' (val='+evicted.val+')', dll:JSON.parse(JSON.stringify(dll)), map:JSON.parse(JSON.stringify(map)), capacity:capacity, op:op, evicted:evicted.key});
}
dll.unshift({key:key, val:val});
map = {};
for (var mi = 0; mi < dll.length; mi++) map[dll[mi].key] = mi;
steps.push({stage:'insert', msg:'put('+key+','+val+') �入到头部', dll:JSON.parse(JSON.stringify(dll)), map:JSON.parse(JSON.stringify(map)), capacity:capacity, op:op});
}
}
}
steps.push({stage:'done', msg:'�作完毕', dll:JSON.parse(JSON.stringify(dll)), map:JSON.parse(JSON.stringify(map)), capacity:capacity});
}
function renderDLL(dll) {
if (!dll || dll.length === 0) return '<div style="color:var(--text-muted);font-style:italic;padding:8px 0;">[空]</div>';
var html = '<div style="display:flex;flex-wrap:wrap;align-items:center;gap:2px;padding:4px 0;">';
html += '<div style="padding:2px 6px;font-size:11px;font-weight:700;color:var(--green);">HEAD</div>';
for (var i = 0; i < dll.length; i++) {
var n = dll[i];
var isHead = (i === 0);
var isTail = (i === dll.length - 1);
var bg = isHead ? 'var(--green-light)' : isTail ? 'var(--orange-light)' : 'var(--blue-light)';
var border = isHead ? 'var(--green)' : isTail ? 'var(--orange)' : 'var(--blue)';
html += '<div style="border:2px solid '+border+';border-radius:8px;overflow:hidden;display:inline-flex;">';
html += '<div style="min-width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12px;padding:0 6px;background:'+bg+';color:var(--text);">k'+n.key+'</div>';
html += '<div style="min-width:24px;height:32px;display:flex;align-items:center;justify-content:center;font-size:12px;padding:0 4px;background:#f8fafc;border-left:1px solid var(--border);">v'+n.val+'</div>';
html += '</div>';
if (i < dll.length - 1) html += '<span style="color:var(--text-muted);">&harr;</span>';
}
html += '<div style="padding:2px 6px;font-size:11px;font-weight:700;color:var(--orange);">TAIL</div>';
html += '</div>';
return html;
}
function render(step) {
var s = steps[step];
var viz = '<div style="margin-bottom:6px;font-size:14px;font-weight:600;color:var(--text-secondary);">当��作:<code>' + s.op + '</code></div>';
viz += renderDLL(s.dll);
// Show map
if (s.map && Object.keys(s.map).length > 0) {
viz += '<div style="margin-top:8px;font-size:13px;"><b>HashMap:</b>';
Object.keys(s.map).forEach(function(k) {
viz += '<code>'+k+'→idx'+s.map[k]+'</code> ';
});
viz += '</div>';
}
viz += '<div style="margin-top:6px;font-size:12px;color:var(--text-muted);">容�: ' + s.dll.length + '/' + s.capacity + '</div>';
$('vizArea').innerHTML = viz;
$('detailContent').innerHTML = '<div class="calc-block">' + s.msg + '</div>';
if (s.hitVal !== undefined) {
$('detailContent').innerHTML += '<div class="current-answer">返回值:<b>' + s.hitVal + '</b></div>';
}
if (s.stage === 'done') {
$('resultContent').innerHTML = '<div class="final-answer">缓存最终状�:[' + s.dll.map(function(n){return n.key+':'+n.val;}).join(', ') + ']</div>';
}
$('hintText').textContent = s.msg;
setPipeline(['init\u2192�始化','hit\u2192命中','miss\u2192未命中','update\u2192更新','evict\u2192淘汰','insert\u2192�入','done\u2192完�'], s.stage);
}
function init() {
var sel = $('exampleSelect');
examples.forEach(function(e,i){ sel.innerHTML += '<option value="'+i+'">'+e.label+'</option>'; });
$('inputArea').value = 'LRUCache(2) put(1,1) put(2,2) get(1) put(3,3) get(2)';
buildSteps(examples[0]);
stepCtrl = new StepController({onStep:render});
stepCtrl.setSteps(steps.map(function(_,i){return i;}));
$('stepInfo').textContent = '步骤 1 / ' + steps.length;
stepCtrl.onStep = function(idx){ render(idx); $('stepInfo').textContent = '步骤 '+(idx+1)+' / '+steps.length; };
$('applyBtn').onclick = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('exampleSelect').onchange = function(){ buildSteps(examples[parseInt($('exampleSelect').value)]); stepCtrl.setSteps(steps.map(function(_,i){return i;})); render(0); };
$('prevBtn').onclick = function(){ stepCtrl.prev(); };
$('nextBtn').onclick = function(){ stepCtrl.next(); };
$('jumpBtn').onclick = function(){ stepCtrl.jumpToEnd(); };
$('autoBtn').onclick = function(){ var on=stepCtrl.toggleAuto(); $('autoBtn').textContent=on?'暂�':'自动播放'; };
$('resetBtn').onclick = function(){ stepCtrl.reset(); $('autoBtn').textContent='自动播放'; };
}
init();