Playground Component
Merge Sorted
Linked List Visualizer
An interactive visualization for merging two sorted linked lists by comparing current nodes and building a sorted result list.

Merge Two Sorted Lists
i (List A)
19
j (List B)
24
Comparisons
0
Merged Length
0
Action
-
Status
Ready
List A
HeadNULL
i
19
41
50
60
65
List B
HeadNULL
j
24
36
59
78
95
Merged Result
HeadEmpty — press Merge Lists
Algorithm
1while (l1 && l2) {2 if (l1.val <= l2.val) {3 tail.next = l1; l1 = l1.next;4 } else {5 tail.next = l2; l2 = l2.next;6 }7 tail = tail.next;8}9tail.next = l1 || l2;
Copy Code
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
<title>Merge Two Sorted Lists</title>
<style>
:root{--bg:#090909;--panel:#111;--gold:#f4c430;--gold-dark:#b88700;--text:#f5f5f5;--border:#2f2f2f}*{box-sizing:border-box}body{margin:0}.merge-viz{min-height:100vh;padding:40px;background:radial-gradient(circle at top,#1a1505 0%,#090909 40%);color:var(--text);font-family:Poppins,Arial,sans-serif}.merge-viz h2{text-align:center;font-size:3rem;color:var(--gold);margin:0 0 35px}.merge-controls{display:flex;justify-content:center;gap:15px;flex-wrap:wrap;margin-bottom:35px}.merge-button{padding:14px 22px;border:0;border-radius:12px;cursor:pointer;font-weight:600;font-size:15px;background:linear-gradient(180deg,#ffd54d,#d9a900);color:#111;display:inline-flex;align-items:center;gap:10px;transition:.25s}.merge-button:hover{transform:translateY(-2px)}.merge-button:disabled{cursor:not-allowed;opacity:.55;transform:none}.merge-stats{width:100%;max-width:960px;margin:auto;margin-bottom:40px;display:grid;grid-template-columns:repeat(6,1fr);gap:18px}.merge-stat-box{background:#151515;border:1px solid #2b2b2b;border-radius:14px;padding:18px;text-align:center}.merge-stat-box h4{margin:0;color:#999;font-size:.9rem}.merge-stat-box p{margin:8px 0 0;color:var(--gold);font-size:1.4rem;font-weight:bold}.merge-code,.merge-stage{width:100%;max-width:1180px;margin:0 auto 30px;padding:24px 26px 20px;border:1px solid var(--border);border-radius:18px;background:#111}.merge-code h3,.merge-stage h3{margin:0 0 16px;text-align:center;color:var(--gold);font-size:1.6rem;font-weight:700}.merge-code-block{margin:0;font-family:"Fira Code","Courier New",monospace;font-size:1rem;line-height:1.9;background:#0c0c0c;border:1px solid #222;border-radius:12px;padding:16px 10px}.merge-code-line{display:flex;gap:16px;padding:2px 14px;border-left:3px solid transparent;border-radius:6px;color:#8a8a8a;white-space:pre;transition:.3s}.merge-code-linenum{color:#4a4a4a;font-size:.85rem;min-width:16px;text-align:right}.merge-code-line.active{color:var(--gold);background:rgba(244,196,48,.1);border-left-color:var(--gold);transform:translateX(4px);animation:glow 1.1s ease-in-out infinite}.merge-code-line.active .merge-code-linenum{color:var(--gold)}@keyframes glow{50%{box-shadow:0 0 18px rgba(244,196,48,.32) inset,0 0 20px rgba(244,196,48,.28)}}.merge-stage{padding:28px 30px 34px}.merge-stage h3{margin:26px 0 18px;font-size:1.35rem}.merge-stage h3:first-child{margin-top:0}.merge-row-scroll{overflow-x:auto;padding-bottom:6px}.merge-row{display:flex;align-items:flex-start;gap:10px;width:max-content;min-height:96px;padding:4px 6px 0}.merge-label,.merge-null{color:var(--gold);font-weight:700;font-size:1rem;white-space:nowrap;margin-top:24px}.merge-empty{color:#666;font-style:italic;font-size:.95rem;margin-top:24px}.merge-node-wrap{display:flex;flex-direction:column;align-items:center;gap:6px;min-width:82px}.merge-pointer-stack{width:100%;height:26px;display:flex;align-items:flex-end;justify-content:center;gap:4px}.merge-pointer-badge{min-width:24px;min-height:18px;padding:2px 6px;border:1px solid #343434;border-radius:5px;background:#111;color:#999;display:inline-flex;align-items:center;justify-content:center;font-size:.68rem;font-weight:700;text-transform:uppercase}.i-pointer{border-color:#4ea8ff;color:#9cc9ff}.j-pointer{border-color:#ff6b6b;color:#ffaaa8}.merge-node{width:64px;height:64px;border-radius:14px;background:#171717;border:2px solid #2c2c2c;color:var(--text);display:flex;align-items:center;justify-content:center;font-size:1.2rem;font-weight:700;transition:.25s}.merge-node.checking{background:#ffb000;color:#000;border-color:#ffd54d;transform:translateY(-8px) scale(1.06);box-shadow:0 0 20px rgba(255,193,7,.45)}.merge-node.picked{background:#25c05a;color:#fff;border-color:#43e97b;transform:translateY(-10px) scale(1.1);box-shadow:0 0 22px rgba(37,192,90,.5)}.merge-node.consumed{opacity:.28}.merge-node.result{background:#1c2b1f;border-color:#43e97b;color:#eafff1;animation:pop .35s ease}@keyframes pop{from{opacity:0;transform:translateY(-14px) scale(.85)}}.merge-arrow{position:relative;width:58px;height:18px;flex:0 0 58px;margin-top:24px}.merge-arrow:before{content:"";position:absolute;left:0;right:11px;top:50%;height:3px;border-radius:999px;background:var(--gold);transform:translateY(-50%)}.merge-arrow:after{content:"";position:absolute;right:0;top:50%;border-top:9px solid transparent;border-bottom:9px solid transparent;border-left:14px solid var(--gold);transform:translateY(-50%)}.merge-dot{width:16px;height:16px;border-radius:50%;background:var(--gold-dark);margin-top:28px;box-shadow:0 0 14px rgba(244,196,48,.3)}@media(max-width:768px){.merge-viz{padding:20px}.merge-viz h2{font-size:2rem}.merge-stats{grid-template-columns:repeat(2,1fr)}.merge-code,.merge-stage{padding:18px 14px 14px}.merge-code-block{font-size:.85rem}.merge-node{width:52px;height:52px}.merge-node-wrap{min-width:58px}.merge-arrow{width:42px;flex-basis:42px}}
</style>
</head>
<body><main class="merge-viz"><h2>Merge Two Sorted Lists</h2><div class="merge-controls"><button id="randomBtn" class="merge-button">Randomize</button><button id="mergeBtn" class="merge-button">Merge Lists</button><button id="pauseBtn" class="merge-button">Pause</button><button id="speedBtn" class="merge-button"></button><button id="resetBtn" class="merge-button">Reset</button></div><div id="stats" class="merge-stats"></div><section class="merge-code"><h3>Algorithm</h3><pre id="code" class="merge-code-block"></pre></section><section id="stage" class="merge-stage"></section></main>
<script>
const LEN_A=5,LEN_B=5,speedLevels=[["Very Slow",900],["Slow",650],["Normal",450],["Fast",260],["Very Fast",120]],codeLines=["while (l1 && l2) {"," if (l1.val <= l2.val) {"," tail.next = l1; l1 = l1.next;"," } else {"," tail.next = l2; l2 = l2.next;"," }"," tail = tail.next;","}","tail.next = l1 || l2;"],lineMap={"Compare values":1,"Take from List A":2,"Take from List B":4,"Advance tail":6,"Attach remainder":8};
let listA,listB,statesA,statesB,merged,iIndex=0,jIndex=0,comparisons=0,action="-",status="Ready",speedIndex=2,running=false,paused=false;
const sleep=ms=>new Promise(async r=>setTimeout(async()=>{while(paused)await new Promise(x=>setTimeout(x,120));r()},ms));
function randomSortedLists(){let used=new Set(),pool=[];while(pool.length<LEN_A+LEN_B){let v=Math.floor(Math.random()*90)+10;if(!used.has(v)){used.add(v);pool.push(v)}}listA=pool.slice(0,LEN_A).sort((a,b)=>a-b);listB=pool.slice(LEN_A).sort((a,b)=>a-b)}
function reset(force=false){if(running&&!force)return;paused=false;statesA=Array(LEN_A).fill("default");statesB=Array(LEN_B).fill("default");merged=[];iIndex=0;jIndex=0;comparisons=0;action="-";status="Ready";running=false;render()}
function row(title,list,states,pointer){let html=`<h3>${title}</h3><div class="merge-row-scroll"><div class="merge-row"><span class="merge-label">Head</span><span class="merge-arrow"></span>`;list.forEach((v,i)=>{html+=`<div class="merge-node-wrap"><div class="merge-pointer-stack">${i===pointer?`<span class="merge-pointer-badge ${title==="List A"?"i-pointer":"j-pointer"}">${title==="List A"?"i":"j"}</span>`:""}</div><div class="merge-node ${states[i]}">${v}</div></div>`;if(i<list.length-1)html+=`<span class="merge-arrow"></span>`});return html+`<span class="merge-arrow"></span><span class="merge-dot"></span><span class="merge-null">NULL</span></div></div>`}
function render(){randomBtn.disabled=running;mergeBtn.disabled=running||status==="Merged!";pauseBtn.disabled=!running;pauseBtn.textContent=paused?"Play":"Pause";speedBtn.textContent="Speed: "+speedLevels[speedIndex][0];stats.innerHTML=[["i (List A)",iIndex>=LEN_A?"NULL":listA[iIndex]],["j (List B)",jIndex>=LEN_B?"NULL":listB[jIndex]],["Comparisons",comparisons],["Merged Length",merged.length],["Action",action],["Status",status]].map(x=>`<div class="merge-stat-box"><h4>${x[0]}</h4><p>${x[1]}</p></div>`).join("");let active=lineMap[action];code.innerHTML=codeLines.map((l,i)=>`<div class="merge-code-line ${active===i?"active":""}"><span class="merge-code-linenum">${i+1}</span><span>${l}</span></div>`).join("");let result=`<h3>Merged Result</h3><div class="merge-row-scroll"><div class="merge-row"><span class="merge-label">Head</span><span class="merge-arrow"></span>`;result+=merged.length?merged.map((v,i)=>`<div class="merge-node-wrap"><div class="merge-pointer-stack"></div><div class="merge-node result">${v}</div></div>${i<merged.length-1?'<span class="merge-arrow"></span>':''}`).join(""):`<span class="merge-empty">Empty - press Merge Lists</span>`;if(merged.length)result+=`<span class="merge-arrow"></span><span class="merge-dot"></span><span class="merge-null">NULL</span>`;result+=`</div></div>`;stage.innerHTML=row("List A",listA,statesA,iIndex<LEN_A?iIndex:-1)+row("List B",listB,statesB,jIndex<LEN_B?jIndex:-1)+result}
async function mergeLists(){if(running)return;reset(true);running=true;status="Merging";let i=0,j=0,comps=0,result=[];render();while(i<LEN_A&&j<LEN_B){action="Compare values";iIndex=i;jIndex=j;statesA[i]="checking";statesB[j]="checking";comparisons=++comps;render();await sleep(speedLevels[speedIndex][1]);if(listA[i]<=listB[j]){action="Take from List A";statesA[i]="picked";render();await sleep(speedLevels[speedIndex][1]);result.push(listA[i]);merged=[...result];statesA[i]="consumed";statesB[j]="default";i++}else{action="Take from List B";statesB[j]="picked";render();await sleep(speedLevels[speedIndex][1]);result.push(listB[j]);merged=[...result];statesB[j]="consumed";statesA[i]="default";j++}iIndex=i;jIndex=j;action="Advance tail";render();await sleep(speedLevels[speedIndex][1]/2)}if(i<LEN_A||j<LEN_B){action="Attach remainder";render();await sleep(speedLevels[speedIndex][1]);while(i<LEN_A){statesA[i]="picked";render();await sleep(speedLevels[speedIndex][1]/1.4);result.push(listA[i]);merged=[...result];statesA[i]="consumed";iIndex=++i;render()}while(j<LEN_B){statesB[j]="picked";render();await sleep(speedLevels[speedIndex][1]/1.4);result.push(listB[j]);merged=[...result];statesB[j]="consumed";jIndex=++j;render()}}status="Merged!";running=false;paused=false;render()}
randomBtn.onclick=()=>{if(running)return;randomSortedLists();reset(true)};mergeBtn.onclick=mergeLists;pauseBtn.onclick=()=>{paused=!paused;status=paused?"Paused":"Merging";render()};speedBtn.onclick=()=>{speedIndex=(speedIndex+1)%speedLevels.length;render()};resetBtn.onclick=()=>reset();randomSortedLists();reset(true);
</script></body></html>
About this Component
Merging two sorted linked lists combines both lists into one sorted list by repeatedly choosing the smaller current node. This visualizer animates the current pointers in List A and List B, each comparison, the selected node, and the growing merged result. It makes the tail pointer movement and remainder attachment easier to follow. The merge process runs in O(m + n) time and uses O(1) extra pointer space when nodes are relinked in place.
Explore More Components
Discover more components from the Playground.



