@tomaszjarosz/react-visualizers 0.2.14 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +84 -10
- package/dist/index.cjs +1082 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1082 -167
- package/dist/index.js.map +1 -1
- package/dist/react-visualizers.css +13 -0
- package/package.json +6 -1
package/dist/index.js
CHANGED
|
@@ -501,17 +501,20 @@ const ControlPanel = ({
|
|
|
501
501
|
extraControls
|
|
502
502
|
}) => {
|
|
503
503
|
const colors = ACCENT_COLORS$4[accentColor];
|
|
504
|
-
|
|
505
|
-
|
|
504
|
+
const speedLabelId = React.useId();
|
|
505
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-3", role: "toolbar", "aria-label": "Playback controls", children: [
|
|
506
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Navigation", children: [
|
|
506
507
|
isPlaying && /* @__PURE__ */ jsxs(
|
|
507
508
|
"span",
|
|
508
509
|
{
|
|
509
510
|
className: `flex items-center gap-1 text-xs ${colors.playing} font-medium`,
|
|
511
|
+
"aria-live": "polite",
|
|
510
512
|
children: [
|
|
511
513
|
/* @__PURE__ */ jsx(
|
|
512
514
|
"span",
|
|
513
515
|
{
|
|
514
|
-
className: `w-2 h-2 ${colors.playingDot} rounded-full animate-pulse
|
|
516
|
+
className: `w-2 h-2 ${colors.playingDot} rounded-full animate-pulse`,
|
|
517
|
+
"aria-hidden": "true"
|
|
515
518
|
}
|
|
516
519
|
),
|
|
517
520
|
"Playing"
|
|
@@ -524,7 +527,9 @@ const ControlPanel = ({
|
|
|
524
527
|
onClick: onPlayPause,
|
|
525
528
|
className: `p-2 text-white rounded-lg transition-colors ${isPlaying ? colors.buttonActive : colors.button}`,
|
|
526
529
|
title: "Play/Pause (P)",
|
|
527
|
-
|
|
530
|
+
"aria-label": isPlaying ? "Pause" : "Play",
|
|
531
|
+
"aria-pressed": isPlaying,
|
|
532
|
+
children: isPlaying ? /* @__PURE__ */ jsx(Pause, { className: "w-4 h-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsx(Play, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
528
533
|
}
|
|
529
534
|
),
|
|
530
535
|
/* @__PURE__ */ jsx(
|
|
@@ -534,7 +539,8 @@ const ControlPanel = ({
|
|
|
534
539
|
disabled: isPlaying || currentStep <= 0,
|
|
535
540
|
className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50",
|
|
536
541
|
title: "Step Back ([)",
|
|
537
|
-
|
|
542
|
+
"aria-label": "Step back",
|
|
543
|
+
children: /* @__PURE__ */ jsx(SkipBack, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
538
544
|
}
|
|
539
545
|
),
|
|
540
546
|
/* @__PURE__ */ jsx(
|
|
@@ -544,7 +550,8 @@ const ControlPanel = ({
|
|
|
544
550
|
disabled: isPlaying || currentStep >= totalSteps - 1,
|
|
545
551
|
className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50",
|
|
546
552
|
title: "Step Forward (])",
|
|
547
|
-
|
|
553
|
+
"aria-label": "Step forward",
|
|
554
|
+
children: /* @__PURE__ */ jsx(SkipForward, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
548
555
|
}
|
|
549
556
|
),
|
|
550
557
|
/* @__PURE__ */ jsx(
|
|
@@ -553,7 +560,8 @@ const ControlPanel = ({
|
|
|
553
560
|
onClick: onReset,
|
|
554
561
|
className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors",
|
|
555
562
|
title: "Reset (R)",
|
|
556
|
-
|
|
563
|
+
"aria-label": "Reset",
|
|
564
|
+
children: /* @__PURE__ */ jsx(RotateCcw, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
557
565
|
}
|
|
558
566
|
),
|
|
559
567
|
showShuffle && onShuffle && /* @__PURE__ */ jsx(
|
|
@@ -563,13 +571,14 @@ const ControlPanel = ({
|
|
|
563
571
|
disabled: isPlaying,
|
|
564
572
|
className: `bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50 ${shuffleLabel ? "px-3 py-2 text-sm" : "p-2"}`,
|
|
565
573
|
title: shuffleLabel || "Shuffle",
|
|
566
|
-
|
|
574
|
+
"aria-label": shuffleLabel || "Shuffle",
|
|
575
|
+
children: shuffleLabel || /* @__PURE__ */ jsx(Shuffle, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
567
576
|
}
|
|
568
577
|
)
|
|
569
578
|
] }),
|
|
570
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
|
|
579
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", role: "group", "aria-label": "Speed control", children: [
|
|
571
580
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
572
|
-
/* @__PURE__ */ jsx("label", { className: "text-xs text-gray-500", children: "Speed" }),
|
|
581
|
+
/* @__PURE__ */ jsx("label", { id: speedLabelId, className: "text-xs text-gray-500", children: "Speed" }),
|
|
573
582
|
/* @__PURE__ */ jsx(
|
|
574
583
|
"input",
|
|
575
584
|
{
|
|
@@ -578,7 +587,11 @@ const ControlPanel = ({
|
|
|
578
587
|
max: "100",
|
|
579
588
|
value: speed,
|
|
580
589
|
onChange: (e) => onSpeedChange(Number(e.target.value)),
|
|
581
|
-
className: "w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
|
590
|
+
className: "w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer",
|
|
591
|
+
"aria-labelledby": speedLabelId,
|
|
592
|
+
"aria-valuemin": 1,
|
|
593
|
+
"aria-valuemax": 100,
|
|
594
|
+
"aria-valuenow": speed
|
|
582
595
|
}
|
|
583
596
|
)
|
|
584
597
|
] }),
|
|
@@ -1446,11 +1459,11 @@ const InterviewModePanel = ({
|
|
|
1446
1459
|
}) => {
|
|
1447
1460
|
const colors = ACCENT_COLORS[accentColor];
|
|
1448
1461
|
if (isComplete) {
|
|
1449
|
-
return /* @__PURE__ */ jsx("div", { className: "p-6 bg-gradient-to-br from-gray-50 to-white rounded-xl border border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1450
|
-
/* @__PURE__ */ jsx(Trophy, { className: "w-16 h-16 mx-auto mb-4 text-yellow-500" }),
|
|
1462
|
+
return /* @__PURE__ */ jsx("div", { className: "p-6 bg-gradient-to-br from-gray-50 to-white rounded-xl border border-gray-200", role: "region", "aria-label": "Quiz results", children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
|
|
1463
|
+
/* @__PURE__ */ jsx(Trophy, { className: "w-16 h-16 mx-auto mb-4 text-yellow-500", "aria-hidden": "true" }),
|
|
1451
1464
|
/* @__PURE__ */ jsx("h3", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Interview Complete!" }),
|
|
1452
|
-
/* @__PURE__ */ jsxs("div", { className: "mb-6", children: [
|
|
1453
|
-
/* @__PURE__ */ jsxs("div", { className: "text-5xl font-bold text-gray-900 mb-1", children: [
|
|
1465
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-6", "aria-live": "polite", children: [
|
|
1466
|
+
/* @__PURE__ */ jsxs("div", { className: "text-5xl font-bold text-gray-900 mb-1", "aria-label": `Score: ${score.percentage} percent`, children: [
|
|
1454
1467
|
score.percentage,
|
|
1455
1468
|
"%"
|
|
1456
1469
|
] }),
|
|
@@ -1466,7 +1479,7 @@ const InterviewModePanel = ({
|
|
|
1466
1479
|
${score.percentage >= 80 ? "bg-green-100 text-green-700" : ""}
|
|
1467
1480
|
${score.percentage >= 60 && score.percentage < 80 ? "bg-yellow-100 text-yellow-700" : ""}
|
|
1468
1481
|
${score.percentage < 60 ? "bg-red-100 text-red-700" : ""}
|
|
1469
|
-
`, children: [
|
|
1482
|
+
`, role: "status", children: [
|
|
1470
1483
|
score.percentage >= 80 && "🎉 Excellent! Ready for interviews!",
|
|
1471
1484
|
score.percentage >= 60 && score.percentage < 80 && "👍 Good job! Keep practicing!",
|
|
1472
1485
|
score.percentage < 60 && "📚 Review the concepts and try again!"
|
|
@@ -1475,13 +1488,14 @@ const InterviewModePanel = ({
|
|
|
1475
1488
|
"button",
|
|
1476
1489
|
{
|
|
1477
1490
|
onClick: onRestart,
|
|
1491
|
+
"aria-label": "Restart quiz",
|
|
1478
1492
|
className: `
|
|
1479
1493
|
flex items-center gap-2 mx-auto px-6 py-3 rounded-lg
|
|
1480
1494
|
text-white font-medium transition-colors
|
|
1481
1495
|
${colors.button}
|
|
1482
1496
|
`,
|
|
1483
1497
|
children: [
|
|
1484
|
-
/* @__PURE__ */ jsx(RotateCcw, { className: "w-5 h-5" }),
|
|
1498
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "w-5 h-5", "aria-hidden": "true" }),
|
|
1485
1499
|
"Try Again"
|
|
1486
1500
|
]
|
|
1487
1501
|
}
|
|
@@ -1521,7 +1535,7 @@ const InterviewModePanel = ({
|
|
|
1521
1535
|
] }),
|
|
1522
1536
|
/* @__PURE__ */ jsxs("div", { className: "p-4", children: [
|
|
1523
1537
|
/* @__PURE__ */ jsx("h4", { className: "text-lg font-semibold text-gray-900 mb-4", children: currentQuestion.question }),
|
|
1524
|
-
/* @__PURE__ */ jsx("div", { className: "space-y-2 mb-4", children: currentQuestion.options.map((option, index) => {
|
|
1538
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2 mb-4", role: "radiogroup", "aria-label": "Answer options", children: currentQuestion.options.map((option, index) => {
|
|
1525
1539
|
const isSelected = selectedAnswer === index;
|
|
1526
1540
|
const isCorrect = index === currentQuestion.correctAnswer;
|
|
1527
1541
|
const showResult = showExplanation;
|
|
@@ -1540,6 +1554,9 @@ const InterviewModePanel = ({
|
|
|
1540
1554
|
{
|
|
1541
1555
|
onClick: () => !isAnswered && onSelectAnswer(index),
|
|
1542
1556
|
disabled: isAnswered,
|
|
1557
|
+
role: "radio",
|
|
1558
|
+
"aria-checked": isSelected,
|
|
1559
|
+
"aria-disabled": isAnswered,
|
|
1543
1560
|
className: `
|
|
1544
1561
|
w-full p-3 rounded-lg border-2 text-left transition-all
|
|
1545
1562
|
flex items-center gap-3
|
|
@@ -1547,24 +1564,31 @@ const InterviewModePanel = ({
|
|
|
1547
1564
|
${isAnswered ? "cursor-default" : "cursor-pointer"}
|
|
1548
1565
|
`,
|
|
1549
1566
|
children: [
|
|
1550
|
-
/* @__PURE__ */ jsxs(
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1567
|
+
/* @__PURE__ */ jsxs(
|
|
1568
|
+
"span",
|
|
1569
|
+
{
|
|
1570
|
+
className: `
|
|
1571
|
+
w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold
|
|
1572
|
+
${showResult && isCorrect ? "bg-green-500 text-white" : ""}
|
|
1573
|
+
${showResult && isSelected && !isCorrect ? "bg-red-500 text-white" : ""}
|
|
1574
|
+
${!showResult ? "bg-gray-200 text-gray-600" : ""}
|
|
1575
|
+
`,
|
|
1576
|
+
"aria-hidden": "true",
|
|
1577
|
+
children: [
|
|
1578
|
+
showResult && isCorrect && /* @__PURE__ */ jsx(CircleCheckBig, { className: "w-4 h-4" }),
|
|
1579
|
+
showResult && isSelected && !isCorrect && /* @__PURE__ */ jsx(CircleX, { className: "w-4 h-4" }),
|
|
1580
|
+
!showResult && String.fromCharCode(65 + index)
|
|
1581
|
+
]
|
|
1582
|
+
}
|
|
1583
|
+
),
|
|
1560
1584
|
/* @__PURE__ */ jsx("span", { className: `flex-1 ${showResult && isCorrect ? "font-medium text-green-700" : ""}`, children: option })
|
|
1561
1585
|
]
|
|
1562
1586
|
},
|
|
1563
1587
|
index
|
|
1564
1588
|
);
|
|
1565
1589
|
}) }),
|
|
1566
|
-
currentQuestion.hint && !showExplanation && /* @__PURE__ */ jsx("div", { className: "mb-4", children: showHint ? /* @__PURE__ */ jsx("div", { className: "p-3 bg-yellow-50 rounded-lg border border-yellow-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1567
|
-
/* @__PURE__ */ jsx(Lightbulb, { className: "w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5" }),
|
|
1590
|
+
currentQuestion.hint && !showExplanation && /* @__PURE__ */ jsx("div", { className: "mb-4", children: showHint ? /* @__PURE__ */ jsx("div", { className: "p-3 bg-yellow-50 rounded-lg border border-yellow-200", role: "note", "aria-label": "Hint", children: /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1591
|
+
/* @__PURE__ */ jsx(Lightbulb, { className: "w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
|
|
1568
1592
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
1569
1593
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-yellow-800 mb-1", children: "Hint" }),
|
|
1570
1594
|
/* @__PURE__ */ jsx("div", { className: "text-sm text-yellow-700", children: currentQuestion.hint })
|
|
@@ -1574,8 +1598,9 @@ const InterviewModePanel = ({
|
|
|
1574
1598
|
{
|
|
1575
1599
|
onClick: onUseHint,
|
|
1576
1600
|
className: "flex items-center gap-2 text-sm text-yellow-600 hover:text-yellow-700",
|
|
1601
|
+
"aria-label": "Show hint for this question",
|
|
1577
1602
|
children: [
|
|
1578
|
-
/* @__PURE__ */ jsx(Lightbulb, { className: "w-4 h-4" }),
|
|
1603
|
+
/* @__PURE__ */ jsx(Lightbulb, { className: "w-4 h-4", "aria-hidden": "true" }),
|
|
1579
1604
|
"Show hint"
|
|
1580
1605
|
]
|
|
1581
1606
|
}
|
|
@@ -1591,19 +1616,20 @@ const InterviewModePanel = ({
|
|
|
1591
1616
|
] })
|
|
1592
1617
|
] }) })
|
|
1593
1618
|
] }),
|
|
1594
|
-
/* @__PURE__ */ jsxs("
|
|
1619
|
+
/* @__PURE__ */ jsxs("nav", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200 flex items-center justify-between", "aria-label": "Question navigation", children: [
|
|
1595
1620
|
/* @__PURE__ */ jsxs(
|
|
1596
1621
|
"button",
|
|
1597
1622
|
{
|
|
1598
1623
|
onClick: onPreviousQuestion,
|
|
1599
1624
|
disabled: currentQuestionIndex === 0,
|
|
1625
|
+
"aria-label": "Previous question",
|
|
1600
1626
|
className: `
|
|
1601
1627
|
flex items-center gap-1 px-3 py-2 rounded-lg text-sm font-medium
|
|
1602
1628
|
transition-colors
|
|
1603
1629
|
${currentQuestionIndex === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-200"}
|
|
1604
1630
|
`,
|
|
1605
1631
|
children: [
|
|
1606
|
-
/* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4" }),
|
|
1632
|
+
/* @__PURE__ */ jsx(ChevronLeft, { className: "w-4 h-4", "aria-hidden": "true" }),
|
|
1607
1633
|
"Previous"
|
|
1608
1634
|
]
|
|
1609
1635
|
}
|
|
@@ -1613,6 +1639,7 @@ const InterviewModePanel = ({
|
|
|
1613
1639
|
{
|
|
1614
1640
|
onClick: onNextQuestion,
|
|
1615
1641
|
disabled: currentQuestionIndex === totalQuestions - 1,
|
|
1642
|
+
"aria-label": currentQuestionIndex === totalQuestions - 1 ? "Finish quiz" : "Next question",
|
|
1616
1643
|
className: `
|
|
1617
1644
|
flex items-center gap-1 px-4 py-2 rounded-lg text-sm font-medium
|
|
1618
1645
|
transition-colors text-white
|
|
@@ -1620,7 +1647,7 @@ const InterviewModePanel = ({
|
|
|
1620
1647
|
`,
|
|
1621
1648
|
children: [
|
|
1622
1649
|
currentQuestionIndex === totalQuestions - 1 ? "Finish" : "Next",
|
|
1623
|
-
/* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4" })
|
|
1650
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
1624
1651
|
]
|
|
1625
1652
|
}
|
|
1626
1653
|
)
|
|
@@ -1729,7 +1756,7 @@ const BINARY_SEARCH_CODE = [
|
|
|
1729
1756
|
" right = mid - 1",
|
|
1730
1757
|
" return -1 // Not found"
|
|
1731
1758
|
];
|
|
1732
|
-
const LEGEND_ITEMS$
|
|
1759
|
+
const LEGEND_ITEMS$A = [
|
|
1733
1760
|
{ color: "bg-blue-100", label: "Search Space" },
|
|
1734
1761
|
{ color: "bg-gray-200", label: "Eliminated" },
|
|
1735
1762
|
{ color: "bg-purple-500", label: "Mid" },
|
|
@@ -2129,7 +2156,7 @@ const BinarySearchVisualizerComponent = ({
|
|
|
2129
2156
|
accentColor: "green"
|
|
2130
2157
|
}
|
|
2131
2158
|
),
|
|
2132
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
2159
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$A })
|
|
2133
2160
|
] })
|
|
2134
2161
|
]
|
|
2135
2162
|
}
|
|
@@ -2194,7 +2221,7 @@ const ALGORITHM_CODE = {
|
|
|
2194
2221
|
" else arr[k++] = right[j++]"
|
|
2195
2222
|
]
|
|
2196
2223
|
};
|
|
2197
|
-
const LEGEND_ITEMS$
|
|
2224
|
+
const LEGEND_ITEMS$z = [
|
|
2198
2225
|
{ color: "bg-blue-500", label: "Default" },
|
|
2199
2226
|
{ color: "bg-yellow-400", label: "Comparing" },
|
|
2200
2227
|
{ color: "bg-red-500", label: "Swapping" },
|
|
@@ -3056,7 +3083,7 @@ const SortingVisualizerComponent = ({
|
|
|
3056
3083
|
extraControls: sizeControl
|
|
3057
3084
|
}
|
|
3058
3085
|
),
|
|
3059
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
3086
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$z })
|
|
3060
3087
|
] })
|
|
3061
3088
|
]
|
|
3062
3089
|
}
|
|
@@ -3602,7 +3629,7 @@ const DIJKSTRA_CODE = [
|
|
|
3602
3629
|
" dist[v] = dist[u]+w",
|
|
3603
3630
|
" pq.add((dist[v], v))"
|
|
3604
3631
|
];
|
|
3605
|
-
const LEGEND_ITEMS$
|
|
3632
|
+
const LEGEND_ITEMS$y = [
|
|
3606
3633
|
{ color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
|
|
3607
3634
|
{ color: "bg-yellow-400", label: "Current", border: "#ca8a04" },
|
|
3608
3635
|
{ color: "bg-green-400", label: "Visited", border: "#16a34a" }
|
|
@@ -3963,7 +3990,7 @@ const DijkstraVisualizerComponent = ({
|
|
|
3963
3990
|
accentColor: "orange"
|
|
3964
3991
|
}
|
|
3965
3992
|
),
|
|
3966
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
3993
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$y })
|
|
3967
3994
|
] })
|
|
3968
3995
|
]
|
|
3969
3996
|
}
|
|
@@ -3987,7 +4014,7 @@ const KNAPSACK_CODE = [
|
|
|
3987
4014
|
" take = dp[i-1][w-weight] + value",
|
|
3988
4015
|
" dp[i][w] = max(skip, take)"
|
|
3989
4016
|
];
|
|
3990
|
-
const LEGEND_ITEMS$
|
|
4017
|
+
const LEGEND_ITEMS$x = [
|
|
3991
4018
|
{ color: "bg-gray-50", label: "Not computed", border: "#d1d5db" },
|
|
3992
4019
|
{ color: "bg-blue-100", label: "Computed" },
|
|
3993
4020
|
{ color: "bg-green-400", label: "Take item" },
|
|
@@ -4283,7 +4310,7 @@ const DPVisualizerComponent = ({
|
|
|
4283
4310
|
accentColor: "teal"
|
|
4284
4311
|
}
|
|
4285
4312
|
),
|
|
4286
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
4313
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$x })
|
|
4287
4314
|
] })
|
|
4288
4315
|
]
|
|
4289
4316
|
}
|
|
@@ -4322,7 +4349,7 @@ const BFS_CODE = [
|
|
|
4322
4349
|
" visited[neighbor] = true",
|
|
4323
4350
|
" queue.enqueue(neighbor)"
|
|
4324
4351
|
];
|
|
4325
|
-
const LEGEND_ITEMS$
|
|
4352
|
+
const LEGEND_ITEMS$w = [
|
|
4326
4353
|
{ color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
|
|
4327
4354
|
{ color: "bg-yellow-400", label: "Current", border: "#ca8a04" },
|
|
4328
4355
|
{ color: "bg-green-400", label: "Visited", border: "#16a34a" }
|
|
@@ -4749,7 +4776,7 @@ const GraphVisualizerComponent = ({
|
|
|
4749
4776
|
accentColor: "purple"
|
|
4750
4777
|
}
|
|
4751
4778
|
),
|
|
4752
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
4779
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$w })
|
|
4753
4780
|
] })
|
|
4754
4781
|
]
|
|
4755
4782
|
}
|
|
@@ -4757,7 +4784,7 @@ const GraphVisualizerComponent = ({
|
|
|
4757
4784
|
};
|
|
4758
4785
|
const GraphVisualizer = React.memo(GraphVisualizerComponent);
|
|
4759
4786
|
const BUCKET_COUNT$2 = 8;
|
|
4760
|
-
const OPERATIONS$
|
|
4787
|
+
const OPERATIONS$j = [
|
|
4761
4788
|
{ op: "put", key: "Alice", value: 25 },
|
|
4762
4789
|
{ op: "put", key: "Bob", value: 30 },
|
|
4763
4790
|
{ op: "put", key: "Charlie", value: 35 },
|
|
@@ -4789,7 +4816,7 @@ const HASHMAP_CODE = [
|
|
|
4789
4816
|
" return entry.value",
|
|
4790
4817
|
" return null"
|
|
4791
4818
|
];
|
|
4792
|
-
const LEGEND_ITEMS$
|
|
4819
|
+
const LEGEND_ITEMS$v = [
|
|
4793
4820
|
{ color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
|
|
4794
4821
|
{ color: "bg-blue-500", label: "Insert/Update" },
|
|
4795
4822
|
{ color: "bg-green-400", label: "Found" },
|
|
@@ -4814,7 +4841,7 @@ function generateHashMapSteps$1() {
|
|
|
4814
4841
|
description: `Initialize HashMap with ${BUCKET_COUNT$2} buckets. Each bucket is a linked list for collision handling.`,
|
|
4815
4842
|
codeLine: -1
|
|
4816
4843
|
});
|
|
4817
|
-
for (const { op, key, value } of OPERATIONS$
|
|
4844
|
+
for (const { op, key, value } of OPERATIONS$j) {
|
|
4818
4845
|
const hash = simpleHash$7(key);
|
|
4819
4846
|
const index = hash % BUCKET_COUNT$2;
|
|
4820
4847
|
if (op === "put") {
|
|
@@ -5101,7 +5128,7 @@ const HashMapVisualizerComponent = ({
|
|
|
5101
5128
|
accentColor: "indigo"
|
|
5102
5129
|
}
|
|
5103
5130
|
),
|
|
5104
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
5131
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$v })
|
|
5105
5132
|
] })
|
|
5106
5133
|
]
|
|
5107
5134
|
}
|
|
@@ -5109,7 +5136,7 @@ const HashMapVisualizerComponent = ({
|
|
|
5109
5136
|
};
|
|
5110
5137
|
const HashMapVisualizer = React.memo(HashMapVisualizerComponent);
|
|
5111
5138
|
const BUCKET_COUNT$1 = 8;
|
|
5112
|
-
const OPERATIONS$
|
|
5139
|
+
const OPERATIONS$i = [
|
|
5113
5140
|
{ op: "put", key: "Alice", value: 25 },
|
|
5114
5141
|
{ op: "put", key: "Bob", value: 30 },
|
|
5115
5142
|
{ op: "put", key: "Charlie", value: 35 },
|
|
@@ -5118,7 +5145,7 @@ const OPERATIONS$g = [
|
|
|
5118
5145
|
{ op: "put", key: "Alice", value: 26 },
|
|
5119
5146
|
{ op: "get", key: "Frank" }
|
|
5120
5147
|
];
|
|
5121
|
-
const LEGEND_ITEMS$
|
|
5148
|
+
const LEGEND_ITEMS$u = [
|
|
5122
5149
|
{ color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
|
|
5123
5150
|
{ color: "bg-blue-500", label: "Insert/Update" },
|
|
5124
5151
|
{ color: "bg-green-400", label: "Found" },
|
|
@@ -5247,7 +5274,7 @@ function generateHashMapSteps() {
|
|
|
5247
5274
|
buckets: buckets.map((b) => ({ entries: [...b.entries] })),
|
|
5248
5275
|
description: `Initialize HashMap with ${BUCKET_COUNT$1} buckets`
|
|
5249
5276
|
});
|
|
5250
|
-
for (const { op, key, value } of OPERATIONS$
|
|
5277
|
+
for (const { op, key, value } of OPERATIONS$i) {
|
|
5251
5278
|
const hash = simpleHash$6(key);
|
|
5252
5279
|
const index = hash % BUCKET_COUNT$1;
|
|
5253
5280
|
if (op === "put") {
|
|
@@ -5488,7 +5515,7 @@ const HashMapInterviewVisualizerComponent = ({
|
|
|
5488
5515
|
accentColor: "indigo"
|
|
5489
5516
|
}
|
|
5490
5517
|
),
|
|
5491
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
5518
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$u })
|
|
5492
5519
|
] })
|
|
5493
5520
|
]
|
|
5494
5521
|
}
|
|
@@ -5496,7 +5523,7 @@ const HashMapInterviewVisualizerComponent = ({
|
|
|
5496
5523
|
};
|
|
5497
5524
|
const HashMapInterviewVisualizer = React.memo(HashMapInterviewVisualizerComponent);
|
|
5498
5525
|
const INITIAL_DATA = [10, 20, 30, 40, 50];
|
|
5499
|
-
const OPERATIONS$
|
|
5526
|
+
const OPERATIONS$h = [
|
|
5500
5527
|
{ op: "get", index: 2 },
|
|
5501
5528
|
{ op: "addLast", value: 60 },
|
|
5502
5529
|
{ op: "addFirst", value: 5 },
|
|
@@ -5505,7 +5532,7 @@ const OPERATIONS$f = [
|
|
|
5505
5532
|
{ op: "removeLast" },
|
|
5506
5533
|
{ op: "removeMiddle", index: 2 }
|
|
5507
5534
|
];
|
|
5508
|
-
const LEGEND_ITEMS$
|
|
5535
|
+
const LEGEND_ITEMS$t = [
|
|
5509
5536
|
{ color: "bg-blue-500", label: "ArrayList" },
|
|
5510
5537
|
{ color: "bg-green-500", label: "LinkedList" },
|
|
5511
5538
|
{ color: "bg-yellow-400", label: "Current element" },
|
|
@@ -5531,7 +5558,7 @@ function generateListComparisonSteps() {
|
|
|
5531
5558
|
description: `Initialize both lists with [${INITIAL_DATA.join(", ")}]`,
|
|
5532
5559
|
phase: "complete"
|
|
5533
5560
|
});
|
|
5534
|
-
for (const { op, index, value } of OPERATIONS$
|
|
5561
|
+
for (const { op, index, value } of OPERATIONS$h) {
|
|
5535
5562
|
switch (op) {
|
|
5536
5563
|
case "get": {
|
|
5537
5564
|
const idx = index;
|
|
@@ -6033,14 +6060,14 @@ const ListComparisonVisualizerComponent = ({
|
|
|
6033
6060
|
accentColor: "blue"
|
|
6034
6061
|
}
|
|
6035
6062
|
),
|
|
6036
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
6063
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$t })
|
|
6037
6064
|
] })
|
|
6038
6065
|
]
|
|
6039
6066
|
}
|
|
6040
6067
|
);
|
|
6041
6068
|
};
|
|
6042
6069
|
const ListComparisonVisualizer = React.memo(ListComparisonVisualizerComponent);
|
|
6043
|
-
const OPERATIONS$
|
|
6070
|
+
const OPERATIONS$g = [
|
|
6044
6071
|
{ op: "add", value: 50 },
|
|
6045
6072
|
{ op: "add", value: 30 },
|
|
6046
6073
|
{ op: "add", value: 70 },
|
|
@@ -6051,7 +6078,7 @@ const OPERATIONS$e = [
|
|
|
6051
6078
|
{ op: "contains", value: 40 },
|
|
6052
6079
|
{ op: "contains", value: 55 }
|
|
6053
6080
|
];
|
|
6054
|
-
const LEGEND_ITEMS$
|
|
6081
|
+
const LEGEND_ITEMS$s = [
|
|
6055
6082
|
{ color: "bg-yellow-100", label: "Path", border: "#facc15" },
|
|
6056
6083
|
{ color: "bg-green-500", label: "Current/Found" },
|
|
6057
6084
|
{ color: "bg-red-500", label: "Not found" }
|
|
@@ -6215,7 +6242,7 @@ function generateTreeSetSteps$1() {
|
|
|
6215
6242
|
path: [],
|
|
6216
6243
|
description: "Initialize empty TreeSet (Binary Search Tree)"
|
|
6217
6244
|
});
|
|
6218
|
-
for (const { op, value } of OPERATIONS$
|
|
6245
|
+
for (const { op, value } of OPERATIONS$g) {
|
|
6219
6246
|
if (op === "add") {
|
|
6220
6247
|
const path = tree ? findPath$1(tree, value) : [];
|
|
6221
6248
|
if (tree) {
|
|
@@ -6514,7 +6541,7 @@ const TreeSetInterviewVisualizerComponent = ({
|
|
|
6514
6541
|
accentColor: "green"
|
|
6515
6542
|
}
|
|
6516
6543
|
),
|
|
6517
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
6544
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$s })
|
|
6518
6545
|
] })
|
|
6519
6546
|
]
|
|
6520
6547
|
}
|
|
@@ -6522,7 +6549,7 @@ const TreeSetInterviewVisualizerComponent = ({
|
|
|
6522
6549
|
};
|
|
6523
6550
|
const TreeSetInterviewVisualizer = React.memo(TreeSetInterviewVisualizerComponent);
|
|
6524
6551
|
const INITIAL_ARRAY = [64, 34, 25, 12, 22, 11, 90];
|
|
6525
|
-
const LEGEND_ITEMS$
|
|
6552
|
+
const LEGEND_ITEMS$r = [
|
|
6526
6553
|
{ color: "bg-yellow-400", label: "Comparing" },
|
|
6527
6554
|
{ color: "bg-red-400", label: "Swapping" },
|
|
6528
6555
|
{ color: "bg-green-400", label: "Sorted" },
|
|
@@ -6845,7 +6872,7 @@ const SortingInterviewVisualizerComponent = ({
|
|
|
6845
6872
|
accentColor: "orange"
|
|
6846
6873
|
}
|
|
6847
6874
|
),
|
|
6848
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
6875
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$r })
|
|
6849
6876
|
] })
|
|
6850
6877
|
]
|
|
6851
6878
|
}
|
|
@@ -6870,7 +6897,7 @@ const EDGES$1 = [
|
|
|
6870
6897
|
{ from: "C", to: "G" },
|
|
6871
6898
|
{ from: "E", to: "F" }
|
|
6872
6899
|
];
|
|
6873
|
-
const LEGEND_ITEMS$
|
|
6900
|
+
const LEGEND_ITEMS$q = [
|
|
6874
6901
|
{ color: "bg-blue-500", label: "Current" },
|
|
6875
6902
|
{ color: "bg-green-400", label: "Visited" },
|
|
6876
6903
|
{ color: "bg-yellow-100", label: "In Queue/Stack", border: "#facc15" }
|
|
@@ -7292,7 +7319,7 @@ const GraphInterviewVisualizerComponent = ({
|
|
|
7292
7319
|
accentColor: "blue"
|
|
7293
7320
|
}
|
|
7294
7321
|
),
|
|
7295
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
7322
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$q })
|
|
7296
7323
|
] })
|
|
7297
7324
|
]
|
|
7298
7325
|
}
|
|
@@ -7301,7 +7328,7 @@ const GraphInterviewVisualizerComponent = ({
|
|
|
7301
7328
|
const GraphInterviewVisualizer = React.memo(GraphInterviewVisualizerComponent);
|
|
7302
7329
|
const BIT_ARRAY_SIZE$1 = 16;
|
|
7303
7330
|
const K_HASH_FUNCTIONS = 3;
|
|
7304
|
-
const OPERATIONS$
|
|
7331
|
+
const OPERATIONS$f = [
|
|
7305
7332
|
{ op: "add", element: "apple" },
|
|
7306
7333
|
{ op: "add", element: "banana" },
|
|
7307
7334
|
{ op: "add", element: "cherry" },
|
|
@@ -7309,7 +7336,7 @@ const OPERATIONS$d = [
|
|
|
7309
7336
|
{ op: "query", element: "grape" },
|
|
7310
7337
|
{ op: "query", element: "banana" }
|
|
7311
7338
|
];
|
|
7312
|
-
const LEGEND_ITEMS$
|
|
7339
|
+
const LEGEND_ITEMS$p = [
|
|
7313
7340
|
{ color: "bg-gray-200", label: "Bit = 0" },
|
|
7314
7341
|
{ color: "bg-purple-500", label: "Bit = 1" },
|
|
7315
7342
|
{ color: "bg-pink-400", label: "Current hash position" }
|
|
@@ -7467,7 +7494,7 @@ function generateBloomFilterSteps$1() {
|
|
|
7467
7494
|
bitArray: [...bitArray],
|
|
7468
7495
|
description: `Initialize Bloom Filter: ${BIT_ARRAY_SIZE$1} bits, ${K_HASH_FUNCTIONS} hash functions`
|
|
7469
7496
|
});
|
|
7470
|
-
for (const { op, element } of OPERATIONS$
|
|
7497
|
+
for (const { op, element } of OPERATIONS$f) {
|
|
7471
7498
|
const positions = getHashPositions(element);
|
|
7472
7499
|
if (op === "add") {
|
|
7473
7500
|
steps.push({
|
|
@@ -7657,7 +7684,7 @@ const BloomFilterInterviewVisualizerComponent = ({
|
|
|
7657
7684
|
accentColor: "purple"
|
|
7658
7685
|
}
|
|
7659
7686
|
),
|
|
7660
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
7687
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$p })
|
|
7661
7688
|
] })
|
|
7662
7689
|
]
|
|
7663
7690
|
}
|
|
@@ -7665,7 +7692,7 @@ const BloomFilterInterviewVisualizerComponent = ({
|
|
|
7665
7692
|
};
|
|
7666
7693
|
const BloomFilterInterviewVisualizer = React.memo(BloomFilterInterviewVisualizerComponent);
|
|
7667
7694
|
const ORDER$1 = 3;
|
|
7668
|
-
const OPERATIONS$
|
|
7695
|
+
const OPERATIONS$e = [
|
|
7669
7696
|
{ op: "insert", value: 10 },
|
|
7670
7697
|
{ op: "insert", value: 20 },
|
|
7671
7698
|
{ op: "insert", value: 5 },
|
|
@@ -7674,7 +7701,7 @@ const OPERATIONS$c = [
|
|
|
7674
7701
|
{ op: "search", value: 15 },
|
|
7675
7702
|
{ op: "search", value: 12 }
|
|
7676
7703
|
];
|
|
7677
|
-
const LEGEND_ITEMS$
|
|
7704
|
+
const LEGEND_ITEMS$o = [
|
|
7678
7705
|
{ color: "bg-green-400", label: "Current node" },
|
|
7679
7706
|
{ color: "bg-yellow-100", label: "Search path", border: "#facc15" },
|
|
7680
7707
|
{ color: "bg-blue-400", label: "Found/Inserted" }
|
|
@@ -7866,7 +7893,7 @@ function generateBTreeSteps$1() {
|
|
|
7866
7893
|
}
|
|
7867
7894
|
return { node };
|
|
7868
7895
|
}
|
|
7869
|
-
for (const { op, value } of OPERATIONS$
|
|
7896
|
+
for (const { op, value } of OPERATIONS$e) {
|
|
7870
7897
|
if (op === "insert") {
|
|
7871
7898
|
steps.push({
|
|
7872
7899
|
operation: "insert",
|
|
@@ -8101,14 +8128,14 @@ const BTreeInterviewVisualizerComponent = ({
|
|
|
8101
8128
|
accentColor: "green"
|
|
8102
8129
|
}
|
|
8103
8130
|
),
|
|
8104
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
8131
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$o })
|
|
8105
8132
|
] })
|
|
8106
8133
|
]
|
|
8107
8134
|
}
|
|
8108
8135
|
);
|
|
8109
8136
|
};
|
|
8110
8137
|
const BTreeInterviewVisualizer = React.memo(BTreeInterviewVisualizerComponent);
|
|
8111
|
-
const LEGEND_ITEMS$
|
|
8138
|
+
const LEGEND_ITEMS$n = [
|
|
8112
8139
|
{ color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
|
|
8113
8140
|
{ color: "bg-yellow-400", label: "Current" },
|
|
8114
8141
|
{ color: "bg-green-400", label: "Visited" }
|
|
@@ -8502,14 +8529,14 @@ const DijkstraInterviewVisualizerComponent = ({
|
|
|
8502
8529
|
accentColor: "orange"
|
|
8503
8530
|
}
|
|
8504
8531
|
),
|
|
8505
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
8532
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$n })
|
|
8506
8533
|
] })
|
|
8507
8534
|
]
|
|
8508
8535
|
}
|
|
8509
8536
|
);
|
|
8510
8537
|
};
|
|
8511
8538
|
const DijkstraInterviewVisualizer = React.memo(DijkstraInterviewVisualizerComponent);
|
|
8512
|
-
const LEGEND_ITEMS$
|
|
8539
|
+
const LEGEND_ITEMS$m = [
|
|
8513
8540
|
{ color: "bg-gray-50", label: "Not computed", border: "#d1d5db" },
|
|
8514
8541
|
{ color: "bg-blue-100", label: "Computed" },
|
|
8515
8542
|
{ color: "bg-green-400", label: "Take item" },
|
|
@@ -8896,14 +8923,14 @@ const DPInterviewVisualizerComponent = ({
|
|
|
8896
8923
|
accentColor: "cyan"
|
|
8897
8924
|
}
|
|
8898
8925
|
),
|
|
8899
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
8926
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$m })
|
|
8900
8927
|
] })
|
|
8901
8928
|
]
|
|
8902
8929
|
}
|
|
8903
8930
|
);
|
|
8904
8931
|
};
|
|
8905
8932
|
const DPInterviewVisualizer = React.memo(DPInterviewVisualizerComponent);
|
|
8906
|
-
const LEGEND_ITEMS$
|
|
8933
|
+
const LEGEND_ITEMS$l = [
|
|
8907
8934
|
{ color: "bg-blue-500", label: "Server A" },
|
|
8908
8935
|
{ color: "bg-green-500", label: "Server B" },
|
|
8909
8936
|
{ color: "bg-purple-500", label: "Server C" },
|
|
@@ -9346,14 +9373,14 @@ const ConsistentHashingInterviewVisualizerComponent = ({
|
|
|
9346
9373
|
accentColor: "cyan"
|
|
9347
9374
|
}
|
|
9348
9375
|
),
|
|
9349
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
9376
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$l })
|
|
9350
9377
|
] })
|
|
9351
9378
|
]
|
|
9352
9379
|
}
|
|
9353
9380
|
);
|
|
9354
9381
|
};
|
|
9355
9382
|
const ConsistentHashingInterviewVisualizer = React.memo(ConsistentHashingInterviewVisualizerComponent);
|
|
9356
|
-
const LEGEND_ITEMS$
|
|
9383
|
+
const LEGEND_ITEMS$k = [
|
|
9357
9384
|
{ color: "bg-gray-400", label: "Follower" },
|
|
9358
9385
|
{ color: "bg-yellow-500", label: "Candidate" },
|
|
9359
9386
|
{ color: "bg-green-500", label: "Leader" },
|
|
@@ -9510,7 +9537,7 @@ const STATE_COLORS$1 = {
|
|
|
9510
9537
|
candidate: "bg-yellow-500",
|
|
9511
9538
|
leader: "bg-green-500"
|
|
9512
9539
|
};
|
|
9513
|
-
function cloneNodes$
|
|
9540
|
+
function cloneNodes$3(nodes) {
|
|
9514
9541
|
return nodes.map((n) => ({
|
|
9515
9542
|
...n,
|
|
9516
9543
|
log: n.log.map((e) => ({ ...e }))
|
|
@@ -9528,7 +9555,7 @@ function generateRaftSteps$1() {
|
|
|
9528
9555
|
}));
|
|
9529
9556
|
steps.push({
|
|
9530
9557
|
operation: "init",
|
|
9531
|
-
nodes: cloneNodes$
|
|
9558
|
+
nodes: cloneNodes$3(nodes),
|
|
9532
9559
|
description: `Initialize ${NODE_IDS$1.length} nodes as followers (term 0)`
|
|
9533
9560
|
});
|
|
9534
9561
|
const candidateId = "N1";
|
|
@@ -9538,7 +9565,7 @@ function generateRaftSteps$1() {
|
|
|
9538
9565
|
candidateNode.votedFor = candidateId;
|
|
9539
9566
|
steps.push({
|
|
9540
9567
|
operation: "timeout",
|
|
9541
|
-
nodes: cloneNodes$
|
|
9568
|
+
nodes: cloneNodes$3(nodes),
|
|
9542
9569
|
description: `${candidateId} timeout! Becomes candidate, term=1`,
|
|
9543
9570
|
highlightNode: candidateId
|
|
9544
9571
|
});
|
|
@@ -9550,7 +9577,7 @@ function generateRaftSteps$1() {
|
|
|
9550
9577
|
votes++;
|
|
9551
9578
|
steps.push({
|
|
9552
9579
|
operation: "vote",
|
|
9553
|
-
nodes: cloneNodes$
|
|
9580
|
+
nodes: cloneNodes$3(nodes),
|
|
9554
9581
|
description: `${node.id} votes for ${candidateId} (${votes}/${NODE_IDS$1.length})`,
|
|
9555
9582
|
highlightNode: node.id,
|
|
9556
9583
|
highlightEdge: { from: node.id, to: candidateId, type: "vote" }
|
|
@@ -9559,7 +9586,7 @@ function generateRaftSteps$1() {
|
|
|
9559
9586
|
candidateNode.state = "leader";
|
|
9560
9587
|
steps.push({
|
|
9561
9588
|
operation: "becomeLeader",
|
|
9562
|
-
nodes: cloneNodes$
|
|
9589
|
+
nodes: cloneNodes$3(nodes),
|
|
9563
9590
|
description: `${candidateId} wins election! (${votes} votes)`,
|
|
9564
9591
|
highlightNode: candidateId
|
|
9565
9592
|
});
|
|
@@ -9569,7 +9596,7 @@ function generateRaftSteps$1() {
|
|
|
9569
9596
|
}
|
|
9570
9597
|
steps.push({
|
|
9571
9598
|
operation: "heartbeat",
|
|
9572
|
-
nodes: cloneNodes$
|
|
9599
|
+
nodes: cloneNodes$3(nodes),
|
|
9573
9600
|
description: `Leader ${candidateId} sends heartbeats`,
|
|
9574
9601
|
highlightNode: candidateId,
|
|
9575
9602
|
messageType: "Heartbeat"
|
|
@@ -9579,7 +9606,7 @@ function generateRaftSteps$1() {
|
|
|
9579
9606
|
leader.log.push({ term: 1, command, committed: false });
|
|
9580
9607
|
steps.push({
|
|
9581
9608
|
operation: "replicate",
|
|
9582
|
-
nodes: cloneNodes$
|
|
9609
|
+
nodes: cloneNodes$3(nodes),
|
|
9583
9610
|
description: `Client request: "${command}"`,
|
|
9584
9611
|
highlightNode: leader.id
|
|
9585
9612
|
});
|
|
@@ -9599,7 +9626,7 @@ function generateRaftSteps$1() {
|
|
|
9599
9626
|
}
|
|
9600
9627
|
steps.push({
|
|
9601
9628
|
operation: "commit",
|
|
9602
|
-
nodes: cloneNodes$
|
|
9629
|
+
nodes: cloneNodes$3(nodes),
|
|
9603
9630
|
description: `Entry committed! (${acks} acks)`,
|
|
9604
9631
|
highlightNode: leader.id
|
|
9605
9632
|
});
|
|
@@ -9609,7 +9636,7 @@ function generateRaftSteps$1() {
|
|
|
9609
9636
|
}
|
|
9610
9637
|
steps.push({
|
|
9611
9638
|
operation: "done",
|
|
9612
|
-
nodes: cloneNodes$
|
|
9639
|
+
nodes: cloneNodes$3(nodes),
|
|
9613
9640
|
description: `✓ Consensus achieved! Leader: ${leader.id}`
|
|
9614
9641
|
});
|
|
9615
9642
|
return steps;
|
|
@@ -9813,14 +9840,14 @@ const RaftInterviewVisualizerComponent = ({
|
|
|
9813
9840
|
accentColor: "orange"
|
|
9814
9841
|
}
|
|
9815
9842
|
),
|
|
9816
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
9843
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$k })
|
|
9817
9844
|
] })
|
|
9818
9845
|
]
|
|
9819
9846
|
}
|
|
9820
9847
|
);
|
|
9821
9848
|
};
|
|
9822
9849
|
const RaftInterviewVisualizer = React.memo(RaftInterviewVisualizerComponent);
|
|
9823
|
-
const INITIAL_SIZE = 7;
|
|
9850
|
+
const INITIAL_SIZE$1 = 7;
|
|
9824
9851
|
const LOAD_FACTOR_THRESHOLD = 0.7;
|
|
9825
9852
|
const SAMPLE_KEYS = [
|
|
9826
9853
|
"apple",
|
|
@@ -9841,7 +9868,7 @@ const HASH_TABLE_CODE = [
|
|
|
9841
9868
|
" if loadFactor > threshold:",
|
|
9842
9869
|
" rehash(newSize = size * 2)"
|
|
9843
9870
|
];
|
|
9844
|
-
const LEGEND_ITEMS$
|
|
9871
|
+
const LEGEND_ITEMS$j = [
|
|
9845
9872
|
{ color: "bg-gray-50", label: "Empty", border: "#d1d5db" },
|
|
9846
9873
|
{ color: "bg-yellow-50", label: "Hashing", border: "#facc15" },
|
|
9847
9874
|
{ color: "bg-red-50", label: "Collision", border: "#f87171" },
|
|
@@ -9856,7 +9883,7 @@ function simpleHash$3(key, size) {
|
|
|
9856
9883
|
}
|
|
9857
9884
|
function generateHashSteps(keys) {
|
|
9858
9885
|
const steps = [];
|
|
9859
|
-
let buckets = Array(INITIAL_SIZE).fill(null).map(() => []);
|
|
9886
|
+
let buckets = Array(INITIAL_SIZE$1).fill(null).map(() => []);
|
|
9860
9887
|
let itemCount = 0;
|
|
9861
9888
|
steps.push({
|
|
9862
9889
|
operation: "done",
|
|
@@ -9864,7 +9891,7 @@ function generateHashSteps(keys) {
|
|
|
9864
9891
|
hashValue: 0,
|
|
9865
9892
|
bucketIndex: -1,
|
|
9866
9893
|
buckets: buckets.map((b) => [...b]),
|
|
9867
|
-
description: `Initialize hash table with ${INITIAL_SIZE} buckets (chaining)`,
|
|
9894
|
+
description: `Initialize hash table with ${INITIAL_SIZE$1} buckets (chaining)`,
|
|
9868
9895
|
codeLine: -1
|
|
9869
9896
|
});
|
|
9870
9897
|
for (const key of keys) {
|
|
@@ -10198,14 +10225,14 @@ const HashTableVisualizerComponent = ({
|
|
|
10198
10225
|
] })
|
|
10199
10226
|
}
|
|
10200
10227
|
),
|
|
10201
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
10228
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$j })
|
|
10202
10229
|
] })
|
|
10203
10230
|
]
|
|
10204
10231
|
}
|
|
10205
10232
|
);
|
|
10206
10233
|
};
|
|
10207
10234
|
const HashTableVisualizer = React.memo(HashTableVisualizerComponent);
|
|
10208
|
-
const OPERATIONS$
|
|
10235
|
+
const OPERATIONS$d = [
|
|
10209
10236
|
{ op: "addFirst", value: 10 },
|
|
10210
10237
|
{ op: "addLast", value: 20 },
|
|
10211
10238
|
{ op: "addLast", value: 30 },
|
|
@@ -10237,25 +10264,25 @@ const LINKEDLIST_CODE = [
|
|
|
10237
10264
|
" node = node.next",
|
|
10238
10265
|
" return node.value"
|
|
10239
10266
|
];
|
|
10240
|
-
const LEGEND_ITEMS$
|
|
10267
|
+
const LEGEND_ITEMS$i = [
|
|
10241
10268
|
{ color: "bg-white", label: "Node", border: "#d1d5db" },
|
|
10242
10269
|
{ color: "bg-blue-500", label: "Active" }
|
|
10243
10270
|
];
|
|
10244
|
-
let nodeIdCounter$
|
|
10271
|
+
let nodeIdCounter$2 = 0;
|
|
10245
10272
|
function generateLinkedListSteps() {
|
|
10246
10273
|
var _a, _b, _c;
|
|
10247
10274
|
const steps = [];
|
|
10248
10275
|
let nodes = [];
|
|
10249
|
-
nodeIdCounter$
|
|
10276
|
+
nodeIdCounter$2 = 0;
|
|
10250
10277
|
steps.push({
|
|
10251
10278
|
operation: "init",
|
|
10252
10279
|
nodes: [],
|
|
10253
10280
|
description: "Initialize empty LinkedList (doubly-linked). O(1) for add/remove at ends, O(n) for indexed access.",
|
|
10254
10281
|
codeLine: -1
|
|
10255
10282
|
});
|
|
10256
|
-
for (const { op, value, index } of OPERATIONS$
|
|
10283
|
+
for (const { op, value, index } of OPERATIONS$d) {
|
|
10257
10284
|
if (op === "addFirst" && value !== void 0) {
|
|
10258
|
-
const newNode = { value, id: nodeIdCounter$
|
|
10285
|
+
const newNode = { value, id: nodeIdCounter$2++ };
|
|
10259
10286
|
nodes = [newNode, ...nodes];
|
|
10260
10287
|
steps.push({
|
|
10261
10288
|
operation: "addFirst",
|
|
@@ -10267,7 +10294,7 @@ function generateLinkedListSteps() {
|
|
|
10267
10294
|
highlightNode: newNode.id
|
|
10268
10295
|
});
|
|
10269
10296
|
} else if (op === "addLast" && value !== void 0) {
|
|
10270
|
-
const newNode = { value, id: nodeIdCounter$
|
|
10297
|
+
const newNode = { value, id: nodeIdCounter$2++ };
|
|
10271
10298
|
nodes = [...nodes, newNode];
|
|
10272
10299
|
steps.push({
|
|
10273
10300
|
operation: "addLast",
|
|
@@ -10483,7 +10510,7 @@ const LinkedListVisualizerComponent = ({
|
|
|
10483
10510
|
accentColor: "blue"
|
|
10484
10511
|
}
|
|
10485
10512
|
),
|
|
10486
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
10513
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$i })
|
|
10487
10514
|
] })
|
|
10488
10515
|
]
|
|
10489
10516
|
}
|
|
@@ -10491,7 +10518,7 @@ const LinkedListVisualizerComponent = ({
|
|
|
10491
10518
|
};
|
|
10492
10519
|
const LinkedListVisualizer = React.memo(LinkedListVisualizerComponent);
|
|
10493
10520
|
const BUCKET_COUNT = 6;
|
|
10494
|
-
const OPERATIONS$
|
|
10521
|
+
const OPERATIONS$c = [
|
|
10495
10522
|
{ op: "put", key: "A", value: 10 },
|
|
10496
10523
|
{ op: "put", key: "B", value: 20 },
|
|
10497
10524
|
{ op: "put", key: "C", value: 30 },
|
|
@@ -10527,7 +10554,7 @@ const LINKEDHASHMAP_CODE = [
|
|
|
10527
10554
|
" }",
|
|
10528
10555
|
"}"
|
|
10529
10556
|
];
|
|
10530
|
-
const LEGEND_ITEMS$
|
|
10557
|
+
const LEGEND_ITEMS$h = [
|
|
10531
10558
|
{ color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
|
|
10532
10559
|
{ color: "bg-blue-500", label: "Insert/Update" },
|
|
10533
10560
|
{ color: "bg-green-400", label: "Found" },
|
|
@@ -10556,7 +10583,7 @@ function generateLinkedHashMapSteps(accessOrder = true) {
|
|
|
10556
10583
|
codeLine: 0,
|
|
10557
10584
|
accessOrder
|
|
10558
10585
|
});
|
|
10559
|
-
for (const { op, key, value } of OPERATIONS$
|
|
10586
|
+
for (const { op, key, value } of OPERATIONS$c) {
|
|
10560
10587
|
const hash = simpleHash$2(key);
|
|
10561
10588
|
const index = hash % BUCKET_COUNT;
|
|
10562
10589
|
if (op === "put") {
|
|
@@ -10992,7 +11019,7 @@ const LinkedHashMapVisualizerComponent = ({
|
|
|
10992
11019
|
accentColor: "orange"
|
|
10993
11020
|
}
|
|
10994
11021
|
),
|
|
10995
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
11022
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$h })
|
|
10996
11023
|
] })
|
|
10997
11024
|
]
|
|
10998
11025
|
}
|
|
@@ -11000,7 +11027,7 @@ const LinkedHashMapVisualizerComponent = ({
|
|
|
11000
11027
|
};
|
|
11001
11028
|
const LinkedHashMapVisualizer = React.memo(LinkedHashMapVisualizerComponent);
|
|
11002
11029
|
const INITIAL_CAPACITY$1 = 4;
|
|
11003
|
-
const OPERATIONS$
|
|
11030
|
+
const OPERATIONS$b = [
|
|
11004
11031
|
{ op: "add", value: 10 },
|
|
11005
11032
|
{ op: "add", value: 20 },
|
|
11006
11033
|
{ op: "add", value: 30 },
|
|
@@ -11031,7 +11058,7 @@ const ARRAYLIST_CODE = [
|
|
|
11031
11058
|
" shift elements left",
|
|
11032
11059
|
" size--"
|
|
11033
11060
|
];
|
|
11034
|
-
const LEGEND_ITEMS$
|
|
11061
|
+
const LEGEND_ITEMS$g = [
|
|
11035
11062
|
{ color: "bg-white border-gray-300", label: "Used", border: "#d1d5db" },
|
|
11036
11063
|
{ color: "bg-gray-100", label: "Empty" },
|
|
11037
11064
|
{ color: "bg-orange-500", label: "Active" },
|
|
@@ -11050,7 +11077,7 @@ function generateArrayListSteps() {
|
|
|
11050
11077
|
description: `Initialize ArrayList with capacity ${capacity}. Auto-grows when full (amortized O(1) add).`,
|
|
11051
11078
|
codeLine: -1
|
|
11052
11079
|
});
|
|
11053
|
-
for (const { op, value, index } of OPERATIONS$
|
|
11080
|
+
for (const { op, value, index } of OPERATIONS$b) {
|
|
11054
11081
|
if (op === "add" && value !== void 0) {
|
|
11055
11082
|
if (size === capacity) {
|
|
11056
11083
|
const oldCapacity = capacity;
|
|
@@ -11371,7 +11398,7 @@ const ArrayListVisualizerComponent = ({
|
|
|
11371
11398
|
accentColor: "orange"
|
|
11372
11399
|
}
|
|
11373
11400
|
),
|
|
11374
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
11401
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$g })
|
|
11375
11402
|
] })
|
|
11376
11403
|
]
|
|
11377
11404
|
}
|
|
@@ -11379,7 +11406,7 @@ const ArrayListVisualizerComponent = ({
|
|
|
11379
11406
|
};
|
|
11380
11407
|
const ArrayListVisualizer = React.memo(ArrayListVisualizerComponent);
|
|
11381
11408
|
const INITIAL_CAPACITY = 8;
|
|
11382
|
-
const OPERATIONS$
|
|
11409
|
+
const OPERATIONS$a = [
|
|
11383
11410
|
{ op: "addLast", value: 10 },
|
|
11384
11411
|
{ op: "addLast", value: 20 },
|
|
11385
11412
|
{ op: "addFirst", value: 5 },
|
|
@@ -11425,7 +11452,7 @@ const ARRAYDEQUE_CODE = [
|
|
|
11425
11452
|
" }",
|
|
11426
11453
|
"}"
|
|
11427
11454
|
];
|
|
11428
|
-
const LEGEND_ITEMS$
|
|
11455
|
+
const LEGEND_ITEMS$f = [
|
|
11429
11456
|
{ color: "bg-teal-500", label: "Head pointer" },
|
|
11430
11457
|
{ color: "bg-violet-500", label: "Tail pointer" },
|
|
11431
11458
|
{ color: "bg-green-400", label: "Added element" },
|
|
@@ -11449,7 +11476,7 @@ function generateArrayDequeSteps() {
|
|
|
11449
11476
|
codeLine: 0,
|
|
11450
11477
|
variables: { head, tail, capacity, size: 0 }
|
|
11451
11478
|
});
|
|
11452
|
-
for (const { op, value } of OPERATIONS$
|
|
11479
|
+
for (const { op, value } of OPERATIONS$a) {
|
|
11453
11480
|
if (op === "addFirst" && value !== void 0) {
|
|
11454
11481
|
if (getSize() === capacity - 1) {
|
|
11455
11482
|
const oldCapacity = capacity;
|
|
@@ -11831,14 +11858,14 @@ const ArrayDequeVisualizerComponent = ({
|
|
|
11831
11858
|
accentColor: "teal"
|
|
11832
11859
|
}
|
|
11833
11860
|
),
|
|
11834
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
11861
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$f })
|
|
11835
11862
|
] })
|
|
11836
11863
|
]
|
|
11837
11864
|
}
|
|
11838
11865
|
);
|
|
11839
11866
|
};
|
|
11840
11867
|
const ArrayDequeVisualizer = React.memo(ArrayDequeVisualizerComponent);
|
|
11841
|
-
const OPERATIONS$
|
|
11868
|
+
const OPERATIONS$9 = [
|
|
11842
11869
|
{ op: "add", value: 50 },
|
|
11843
11870
|
{ op: "add", value: 30 },
|
|
11844
11871
|
{ op: "add", value: 70 },
|
|
@@ -11869,7 +11896,7 @@ const TREESET_CODE = [
|
|
|
11869
11896
|
" node = node.right",
|
|
11870
11897
|
" else: return // duplicate"
|
|
11871
11898
|
];
|
|
11872
|
-
const LEGEND_ITEMS$
|
|
11899
|
+
const LEGEND_ITEMS$e = [
|
|
11873
11900
|
{ color: "bg-yellow-100", label: "Path", border: "#facc15" },
|
|
11874
11901
|
{ color: "bg-blue-500", label: "Current/Insert" },
|
|
11875
11902
|
{ color: "bg-green-500", label: "Found" },
|
|
@@ -11918,7 +11945,7 @@ function generateTreeSetSteps() {
|
|
|
11918
11945
|
description: "Initialize empty TreeSet (Binary Search Tree). Values are stored in sorted order.",
|
|
11919
11946
|
codeLine: -1
|
|
11920
11947
|
});
|
|
11921
|
-
for (const { op, value } of OPERATIONS$
|
|
11948
|
+
for (const { op, value } of OPERATIONS$9) {
|
|
11922
11949
|
if (op === "add") {
|
|
11923
11950
|
const path = tree ? findPath(tree, value) : [];
|
|
11924
11951
|
if (tree) {
|
|
@@ -12298,7 +12325,7 @@ const TreeSetVisualizerComponent = ({
|
|
|
12298
12325
|
accentColor: "green"
|
|
12299
12326
|
}
|
|
12300
12327
|
),
|
|
12301
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
12328
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$e })
|
|
12302
12329
|
] })
|
|
12303
12330
|
]
|
|
12304
12331
|
}
|
|
@@ -12314,7 +12341,7 @@ const DAYS_OF_WEEK = [
|
|
|
12314
12341
|
"SATURDAY",
|
|
12315
12342
|
"SUNDAY"
|
|
12316
12343
|
];
|
|
12317
|
-
const OPERATIONS$
|
|
12344
|
+
const OPERATIONS$8 = [
|
|
12318
12345
|
{ op: "add", value: "MONDAY" },
|
|
12319
12346
|
{ op: "add", value: "WEDNESDAY" },
|
|
12320
12347
|
{ op: "add", value: "FRIDAY" },
|
|
@@ -12347,7 +12374,7 @@ const ENUMSET_CODE = [
|
|
|
12347
12374
|
" }",
|
|
12348
12375
|
"}"
|
|
12349
12376
|
];
|
|
12350
|
-
const LEGEND_ITEMS$
|
|
12377
|
+
const LEGEND_ITEMS$d = [
|
|
12351
12378
|
{ color: "bg-green-500", label: "Bit set (1)" },
|
|
12352
12379
|
{ color: "bg-gray-200", label: "Bit clear (0)" },
|
|
12353
12380
|
{ color: "bg-blue-500", label: "Current operation" },
|
|
@@ -12366,7 +12393,7 @@ function generateEnumSetSteps() {
|
|
|
12366
12393
|
codeLine: 1,
|
|
12367
12394
|
variables: { elements: "0b0000000" }
|
|
12368
12395
|
});
|
|
12369
|
-
for (const { op, value } of OPERATIONS$
|
|
12396
|
+
for (const { op, value } of OPERATIONS$8) {
|
|
12370
12397
|
const bitPos = getBitPosition(value);
|
|
12371
12398
|
const bitMaskForValue = 1 << bitPos;
|
|
12372
12399
|
const previousBitmask = bitmask;
|
|
@@ -12667,14 +12694,14 @@ const EnumSetVisualizerComponent = ({
|
|
|
12667
12694
|
accentColor: "lime"
|
|
12668
12695
|
}
|
|
12669
12696
|
),
|
|
12670
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
12697
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$d })
|
|
12671
12698
|
] })
|
|
12672
12699
|
]
|
|
12673
12700
|
}
|
|
12674
12701
|
);
|
|
12675
12702
|
};
|
|
12676
12703
|
const EnumSetVisualizer = React.memo(EnumSetVisualizerComponent);
|
|
12677
|
-
const OPERATIONS$
|
|
12704
|
+
const OPERATIONS$7 = [
|
|
12678
12705
|
{ op: "offer", value: 50 },
|
|
12679
12706
|
{ op: "offer", value: 30 },
|
|
12680
12707
|
{ op: "offer", value: 70 },
|
|
@@ -12703,7 +12730,7 @@ const HEAP_CODE = [
|
|
|
12703
12730
|
" siftDown(0)",
|
|
12704
12731
|
" return result"
|
|
12705
12732
|
];
|
|
12706
|
-
const LEGEND_ITEMS$
|
|
12733
|
+
const LEGEND_ITEMS$c = [
|
|
12707
12734
|
{ color: "bg-purple-100", label: "Root (min)", border: "#c4b5fd" },
|
|
12708
12735
|
{ color: "bg-purple-500", label: "Active" },
|
|
12709
12736
|
{ color: "bg-purple-200", label: "Sift Path", border: "#a78bfa" },
|
|
@@ -12719,7 +12746,7 @@ function generateHeapSteps() {
|
|
|
12719
12746
|
description: "Initialize min-heap PriorityQueue. Parent is always smaller than children. O(log n) insert/remove.",
|
|
12720
12747
|
codeLine: -1
|
|
12721
12748
|
});
|
|
12722
|
-
for (const { op, value } of OPERATIONS$
|
|
12749
|
+
for (const { op, value } of OPERATIONS$7) {
|
|
12723
12750
|
if (op === "offer" && value !== void 0) {
|
|
12724
12751
|
heap.push(value);
|
|
12725
12752
|
let idx = heap.length - 1;
|
|
@@ -13225,7 +13252,7 @@ const PriorityQueueVisualizerComponent = ({ showControls = true, showCode = true
|
|
|
13225
13252
|
accentColor: "purple"
|
|
13226
13253
|
}
|
|
13227
13254
|
),
|
|
13228
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
13255
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$c })
|
|
13229
13256
|
] })
|
|
13230
13257
|
]
|
|
13231
13258
|
}
|
|
@@ -13235,7 +13262,7 @@ const PriorityQueueVisualizer = React.memo(
|
|
|
13235
13262
|
PriorityQueueVisualizerComponent
|
|
13236
13263
|
);
|
|
13237
13264
|
const SEGMENT_COUNT = 4;
|
|
13238
|
-
const OPERATIONS$
|
|
13265
|
+
const OPERATIONS$6 = [
|
|
13239
13266
|
{ thread: "T1", op: "put", key: "Alice", value: 100 },
|
|
13240
13267
|
{ thread: "T2", op: "put", key: "Bob", value: 200 },
|
|
13241
13268
|
{ thread: "T1", op: "put", key: "Charlie", value: 150 },
|
|
@@ -13258,7 +13285,7 @@ const CHM_CODE = [
|
|
|
13258
13285
|
" // Volatile reads ensure visibility",
|
|
13259
13286
|
" return bucket.get(key)"
|
|
13260
13287
|
];
|
|
13261
|
-
const LEGEND_ITEMS$
|
|
13288
|
+
const LEGEND_ITEMS$b = [
|
|
13262
13289
|
{ color: "bg-blue-50", label: "Active segment", border: "#60a5fa" },
|
|
13263
13290
|
{ color: "bg-red-50", label: "Locked", border: "#f87171" },
|
|
13264
13291
|
{ color: "bg-gray-50", label: "Unlocked", border: "#d1d5db" }
|
|
@@ -13285,7 +13312,7 @@ function generateConcurrentSteps() {
|
|
|
13285
13312
|
codeLine: -1,
|
|
13286
13313
|
activeThreads: []
|
|
13287
13314
|
});
|
|
13288
|
-
for (const { thread, op, key, value } of OPERATIONS$
|
|
13315
|
+
for (const { thread, op, key, value } of OPERATIONS$6) {
|
|
13289
13316
|
const hash = simpleHash$1(key);
|
|
13290
13317
|
const segmentId = hash % SEGMENT_COUNT;
|
|
13291
13318
|
if (op === "put") {
|
|
@@ -13614,7 +13641,7 @@ const ConcurrentHashMapVisualizerComponent = ({ showControls = true, showCode =
|
|
|
13614
13641
|
accentColor: "red"
|
|
13615
13642
|
}
|
|
13616
13643
|
),
|
|
13617
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
13644
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$b })
|
|
13618
13645
|
] })
|
|
13619
13646
|
]
|
|
13620
13647
|
}
|
|
@@ -13624,7 +13651,7 @@ const ConcurrentHashMapVisualizer = React.memo(
|
|
|
13624
13651
|
ConcurrentHashMapVisualizerComponent
|
|
13625
13652
|
);
|
|
13626
13653
|
const CAPACITY = 3;
|
|
13627
|
-
const OPERATIONS$
|
|
13654
|
+
const OPERATIONS$5 = [
|
|
13628
13655
|
{ thread: "P1", role: "producer", value: "Task-A" },
|
|
13629
13656
|
{ thread: "P2", role: "producer", value: "Task-B" },
|
|
13630
13657
|
{ thread: "C1", role: "consumer" },
|
|
@@ -13657,7 +13684,7 @@ const BQ_CODE = [
|
|
|
13657
13684
|
" notFull.signal()",
|
|
13658
13685
|
" lock.unlock()"
|
|
13659
13686
|
];
|
|
13660
|
-
const LEGEND_ITEMS$
|
|
13687
|
+
const LEGEND_ITEMS$a = [
|
|
13661
13688
|
{ color: "bg-green-100", label: "Producer" },
|
|
13662
13689
|
{ color: "bg-blue-100", label: "Consumer" },
|
|
13663
13690
|
{ color: "bg-red-100", label: "Blocked", border: "#fca5a5" }
|
|
@@ -13679,7 +13706,7 @@ function generateBlockingQueueSteps() {
|
|
|
13679
13706
|
blockedProducers: [],
|
|
13680
13707
|
blockedConsumers: []
|
|
13681
13708
|
});
|
|
13682
|
-
for (const { thread, role, value } of OPERATIONS$
|
|
13709
|
+
for (const { thread, role, value } of OPERATIONS$5) {
|
|
13683
13710
|
if (role === "producer") {
|
|
13684
13711
|
if (queue.length >= CAPACITY) {
|
|
13685
13712
|
blockedProducers.push(thread);
|
|
@@ -14012,7 +14039,7 @@ const BlockingQueueVisualizerComponent = ({ showControls = true, showCode = true
|
|
|
14012
14039
|
accentColor: "cyan"
|
|
14013
14040
|
}
|
|
14014
14041
|
),
|
|
14015
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
14042
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$a })
|
|
14016
14043
|
] })
|
|
14017
14044
|
]
|
|
14018
14045
|
}
|
|
@@ -14021,7 +14048,7 @@ const BlockingQueueVisualizerComponent = ({ showControls = true, showCode = true
|
|
|
14021
14048
|
const BlockingQueueVisualizer = React.memo(
|
|
14022
14049
|
BlockingQueueVisualizerComponent
|
|
14023
14050
|
);
|
|
14024
|
-
const OPERATIONS$
|
|
14051
|
+
const OPERATIONS$4 = [
|
|
14025
14052
|
{ thread: "R1", op: "read" },
|
|
14026
14053
|
{ thread: "R2", op: "read" },
|
|
14027
14054
|
{ thread: "W1", op: "write", value: "D" },
|
|
@@ -14043,7 +14070,7 @@ const COW_CODE = [
|
|
|
14043
14070
|
" array = newArray // Atomic swap",
|
|
14044
14071
|
" lock.unlock()"
|
|
14045
14072
|
];
|
|
14046
|
-
const LEGEND_ITEMS$
|
|
14073
|
+
const LEGEND_ITEMS$9 = [
|
|
14047
14074
|
{ color: "bg-blue-500", label: "Reader" },
|
|
14048
14075
|
{ color: "bg-orange-500", label: "Writer" },
|
|
14049
14076
|
{ color: "bg-green-500", label: "New element" }
|
|
@@ -14061,7 +14088,7 @@ function generateCOWSteps() {
|
|
|
14061
14088
|
codeLine: -1,
|
|
14062
14089
|
activeReaders: []
|
|
14063
14090
|
});
|
|
14064
|
-
for (const { thread, op, value } of OPERATIONS$
|
|
14091
|
+
for (const { thread, op, value } of OPERATIONS$4) {
|
|
14065
14092
|
if (op === "read") {
|
|
14066
14093
|
if (!activeReaders.includes(thread)) {
|
|
14067
14094
|
activeReaders.push(thread);
|
|
@@ -14355,7 +14382,7 @@ const CopyOnWriteVisualizerComponent = ({
|
|
|
14355
14382
|
accentColor: "lime"
|
|
14356
14383
|
}
|
|
14357
14384
|
),
|
|
14358
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
14385
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$9 })
|
|
14359
14386
|
] })
|
|
14360
14387
|
]
|
|
14361
14388
|
}
|
|
@@ -14378,7 +14405,7 @@ const IMMUTABLE_CODE = [
|
|
|
14378
14405
|
"var newList = new ArrayList<>(list)",
|
|
14379
14406
|
'newList.add("D") // Works!'
|
|
14380
14407
|
];
|
|
14381
|
-
const LEGEND_ITEMS$
|
|
14408
|
+
const LEGEND_ITEMS$8 = [
|
|
14382
14409
|
{ color: "bg-violet-100", label: "Immutable", border: "#c4b5fd" },
|
|
14383
14410
|
{ color: "bg-green-100", label: "Mutable copy", border: "#86efac" },
|
|
14384
14411
|
{ color: "bg-red-100", label: "Error", border: "#fca5a5" }
|
|
@@ -14656,7 +14683,7 @@ const ImmutableCollectionsVisualizerComponent = ({ showControls = true, showCode
|
|
|
14656
14683
|
accentColor: "violet"
|
|
14657
14684
|
}
|
|
14658
14685
|
),
|
|
14659
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
14686
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$8 })
|
|
14660
14687
|
] })
|
|
14661
14688
|
]
|
|
14662
14689
|
}
|
|
@@ -14687,7 +14714,7 @@ const BLOOM_FILTER_CODE = [
|
|
|
14687
14714
|
" return False # definitely not",
|
|
14688
14715
|
" return True # probably yes"
|
|
14689
14716
|
];
|
|
14690
|
-
const LEGEND_ITEMS$
|
|
14717
|
+
const LEGEND_ITEMS$7 = [
|
|
14691
14718
|
{ color: "bg-gray-200", label: "Bit = 0" },
|
|
14692
14719
|
{ color: "bg-indigo-500", label: "Bit = 1" },
|
|
14693
14720
|
{ color: "bg-yellow-400", label: "Currently checking", border: "#fbbf24" },
|
|
@@ -15066,7 +15093,7 @@ const BloomFilterVisualizerComponent = ({
|
|
|
15066
15093
|
accentColor: "purple"
|
|
15067
15094
|
}
|
|
15068
15095
|
),
|
|
15069
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
15096
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$7 })
|
|
15070
15097
|
] })
|
|
15071
15098
|
]
|
|
15072
15099
|
}
|
|
@@ -15074,7 +15101,7 @@ const BloomFilterVisualizerComponent = ({
|
|
|
15074
15101
|
};
|
|
15075
15102
|
const BloomFilterVisualizer = React.memo(BloomFilterVisualizerComponent);
|
|
15076
15103
|
const ORDER = 3;
|
|
15077
|
-
const OPERATIONS$
|
|
15104
|
+
const OPERATIONS$3 = [
|
|
15078
15105
|
{ op: "insert", key: 10 },
|
|
15079
15106
|
{ op: "insert", key: 20 },
|
|
15080
15107
|
{ op: "insert", key: 5 },
|
|
@@ -15105,7 +15132,7 @@ const BTREE_CODE = [
|
|
|
15105
15132
|
" split root, create new root",
|
|
15106
15133
|
" insertNonFull(root, key)"
|
|
15107
15134
|
];
|
|
15108
|
-
const LEGEND_ITEMS$
|
|
15135
|
+
const LEGEND_ITEMS$6 = [
|
|
15109
15136
|
{ color: "bg-gray-100", label: "Node", border: "#d1d5db" },
|
|
15110
15137
|
{ color: "bg-yellow-200", label: "Current node", border: "#fbbf24" },
|
|
15111
15138
|
{ color: "bg-blue-500", label: "Comparing key" },
|
|
@@ -15113,11 +15140,11 @@ const LEGEND_ITEMS$4 = [
|
|
|
15113
15140
|
{ color: "bg-red-400", label: "Not found" },
|
|
15114
15141
|
{ color: "bg-purple-400", label: "Splitting" }
|
|
15115
15142
|
];
|
|
15116
|
-
let nodeIdCounter = 0;
|
|
15117
|
-
function generateNodeId() {
|
|
15118
|
-
return `node_${nodeIdCounter++}`;
|
|
15143
|
+
let nodeIdCounter$1 = 0;
|
|
15144
|
+
function generateNodeId$1() {
|
|
15145
|
+
return `node_${nodeIdCounter$1++}`;
|
|
15119
15146
|
}
|
|
15120
|
-
function cloneNodes$
|
|
15147
|
+
function cloneNodes$2(nodes) {
|
|
15121
15148
|
const cloned = /* @__PURE__ */ new Map();
|
|
15122
15149
|
nodes.forEach((node, id) => {
|
|
15123
15150
|
cloned.set(id, {
|
|
@@ -15129,11 +15156,11 @@ function cloneNodes$1(nodes) {
|
|
|
15129
15156
|
return cloned;
|
|
15130
15157
|
}
|
|
15131
15158
|
function generateBTreeSteps() {
|
|
15132
|
-
nodeIdCounter = 0;
|
|
15159
|
+
nodeIdCounter$1 = 0;
|
|
15133
15160
|
const steps = [];
|
|
15134
15161
|
let nodes = /* @__PURE__ */ new Map();
|
|
15135
15162
|
let rootId = "";
|
|
15136
|
-
const initialRoot = generateNodeId();
|
|
15163
|
+
const initialRoot = generateNodeId$1();
|
|
15137
15164
|
rootId = initialRoot;
|
|
15138
15165
|
nodes.set(initialRoot, {
|
|
15139
15166
|
id: initialRoot,
|
|
@@ -15143,18 +15170,18 @@ function generateBTreeSteps() {
|
|
|
15143
15170
|
});
|
|
15144
15171
|
steps.push({
|
|
15145
15172
|
operation: "init",
|
|
15146
|
-
nodes: cloneNodes$
|
|
15173
|
+
nodes: cloneNodes$2(nodes),
|
|
15147
15174
|
rootId,
|
|
15148
15175
|
description: `Initialize empty B-Tree of order ${ORDER} (max ${ORDER - 1} keys per node)`,
|
|
15149
15176
|
codeLine: 0,
|
|
15150
15177
|
variables: { order: ORDER, maxKeys: ORDER - 1 }
|
|
15151
15178
|
});
|
|
15152
|
-
for (const { op, key } of OPERATIONS$
|
|
15179
|
+
for (const { op, key } of OPERATIONS$3) {
|
|
15153
15180
|
if (op === "insert") {
|
|
15154
15181
|
const root = nodes.get(rootId);
|
|
15155
15182
|
steps.push({
|
|
15156
15183
|
operation: "insert",
|
|
15157
|
-
nodes: cloneNodes$
|
|
15184
|
+
nodes: cloneNodes$2(nodes),
|
|
15158
15185
|
rootId,
|
|
15159
15186
|
targetKey: key,
|
|
15160
15187
|
currentNodeId: rootId,
|
|
@@ -15171,7 +15198,7 @@ function generateBTreeSteps() {
|
|
|
15171
15198
|
insertNode2.keys.sort((a, b) => a - b);
|
|
15172
15199
|
steps.push({
|
|
15173
15200
|
operation: "insert",
|
|
15174
|
-
nodes: cloneNodes$
|
|
15201
|
+
nodes: cloneNodes$2(nodes),
|
|
15175
15202
|
rootId,
|
|
15176
15203
|
targetKey: key,
|
|
15177
15204
|
currentNodeId: rootId,
|
|
@@ -15187,7 +15214,7 @@ function generateBTreeSteps() {
|
|
|
15187
15214
|
const rightKeys = insertNode2.keys.slice(medianIndex + 1);
|
|
15188
15215
|
steps.push({
|
|
15189
15216
|
operation: "split",
|
|
15190
|
-
nodes: cloneNodes$
|
|
15217
|
+
nodes: cloneNodes$2(nodes),
|
|
15191
15218
|
rootId,
|
|
15192
15219
|
currentNodeId: rootId,
|
|
15193
15220
|
description: `Node full (${insertNode2.keys.length} keys)! Split: median=${medianKey}, left=[${leftKeys.join(",")}], right=[${rightKeys.join(",")}]`,
|
|
@@ -15200,9 +15227,9 @@ function generateBTreeSteps() {
|
|
|
15200
15227
|
rightKeys
|
|
15201
15228
|
}
|
|
15202
15229
|
});
|
|
15203
|
-
const leftId = generateNodeId();
|
|
15204
|
-
const rightId = generateNodeId();
|
|
15205
|
-
const newRootId = generateNodeId();
|
|
15230
|
+
const leftId = generateNodeId$1();
|
|
15231
|
+
const rightId = generateNodeId$1();
|
|
15232
|
+
const newRootId = generateNodeId$1();
|
|
15206
15233
|
nodes.set(leftId, {
|
|
15207
15234
|
id: leftId,
|
|
15208
15235
|
keys: leftKeys,
|
|
@@ -15225,7 +15252,7 @@ function generateBTreeSteps() {
|
|
|
15225
15252
|
rootId = newRootId;
|
|
15226
15253
|
steps.push({
|
|
15227
15254
|
operation: "split",
|
|
15228
|
-
nodes: cloneNodes$
|
|
15255
|
+
nodes: cloneNodes$2(nodes),
|
|
15229
15256
|
rootId,
|
|
15230
15257
|
description: `Split complete! New root with median ${medianKey}`,
|
|
15231
15258
|
codeLine: 17,
|
|
@@ -15235,7 +15262,7 @@ function generateBTreeSteps() {
|
|
|
15235
15262
|
} else {
|
|
15236
15263
|
steps.push({
|
|
15237
15264
|
operation: "search",
|
|
15238
|
-
nodes: cloneNodes$
|
|
15265
|
+
nodes: cloneNodes$2(nodes),
|
|
15239
15266
|
rootId,
|
|
15240
15267
|
targetKey: key,
|
|
15241
15268
|
currentNodeId: rootId,
|
|
@@ -15248,7 +15275,7 @@ function generateBTreeSteps() {
|
|
|
15248
15275
|
for (let i = 0; i < node.keys.length; i++) {
|
|
15249
15276
|
steps.push({
|
|
15250
15277
|
operation: "compare",
|
|
15251
|
-
nodes: cloneNodes$
|
|
15278
|
+
nodes: cloneNodes$2(nodes),
|
|
15252
15279
|
rootId,
|
|
15253
15280
|
targetKey: key,
|
|
15254
15281
|
currentNodeId: nodeId,
|
|
@@ -15260,7 +15287,7 @@ function generateBTreeSteps() {
|
|
|
15260
15287
|
if (key === node.keys[i]) {
|
|
15261
15288
|
steps.push({
|
|
15262
15289
|
operation: "found",
|
|
15263
|
-
nodes: cloneNodes$
|
|
15290
|
+
nodes: cloneNodes$2(nodes),
|
|
15264
15291
|
rootId,
|
|
15265
15292
|
targetKey: key,
|
|
15266
15293
|
currentNodeId: nodeId,
|
|
@@ -15275,7 +15302,7 @@ function generateBTreeSteps() {
|
|
|
15275
15302
|
if (node.isLeaf) {
|
|
15276
15303
|
steps.push({
|
|
15277
15304
|
operation: "notFound",
|
|
15278
|
-
nodes: cloneNodes$
|
|
15305
|
+
nodes: cloneNodes$2(nodes),
|
|
15279
15306
|
rootId,
|
|
15280
15307
|
targetKey: key,
|
|
15281
15308
|
currentNodeId: nodeId,
|
|
@@ -15291,7 +15318,7 @@ function generateBTreeSteps() {
|
|
|
15291
15318
|
if (node.isLeaf) {
|
|
15292
15319
|
steps.push({
|
|
15293
15320
|
operation: "notFound",
|
|
15294
|
-
nodes: cloneNodes$
|
|
15321
|
+
nodes: cloneNodes$2(nodes),
|
|
15295
15322
|
rootId,
|
|
15296
15323
|
targetKey: key,
|
|
15297
15324
|
currentNodeId: nodeId,
|
|
@@ -15310,7 +15337,7 @@ function generateBTreeSteps() {
|
|
|
15310
15337
|
nodes.forEach((node) => allKeys.push(...node.keys));
|
|
15311
15338
|
steps.push({
|
|
15312
15339
|
operation: "done",
|
|
15313
|
-
nodes: cloneNodes$
|
|
15340
|
+
nodes: cloneNodes$2(nodes),
|
|
15314
15341
|
rootId,
|
|
15315
15342
|
description: `✓ Done! B-Tree contains ${allKeys.length} keys across ${nodes.size} nodes.`,
|
|
15316
15343
|
codeLine: -1,
|
|
@@ -15495,7 +15522,7 @@ const BTreeVisualizerComponent = ({
|
|
|
15495
15522
|
] }),
|
|
15496
15523
|
/* @__PURE__ */ jsxs("div", { className: "mb-4 p-2 bg-blue-50 rounded-lg border border-blue-200", children: [
|
|
15497
15524
|
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-blue-800 mb-1", children: "Operations" }),
|
|
15498
|
-
/* @__PURE__ */ jsx("div", { className: "text-xs text-blue-700 font-mono", children: OPERATIONS$
|
|
15525
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs text-blue-700 font-mono", children: OPERATIONS$3.map((op, i) => /* @__PURE__ */ jsxs("span", { className: "mr-2", children: [
|
|
15499
15526
|
op.op,
|
|
15500
15527
|
"(",
|
|
15501
15528
|
op.key,
|
|
@@ -15540,13 +15567,899 @@ const BTreeVisualizerComponent = ({
|
|
|
15540
15567
|
accentColor: "green"
|
|
15541
15568
|
}
|
|
15542
15569
|
),
|
|
15543
|
-
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$
|
|
15570
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$6 })
|
|
15544
15571
|
] })
|
|
15545
15572
|
]
|
|
15546
15573
|
}
|
|
15547
15574
|
);
|
|
15548
15575
|
};
|
|
15549
15576
|
const BTreeVisualizer = React.memo(BTreeVisualizerComponent);
|
|
15577
|
+
const OPERATIONS$2 = [
|
|
15578
|
+
{ op: "insert", word: "cat" },
|
|
15579
|
+
{ op: "insert", word: "car" },
|
|
15580
|
+
{ op: "insert", word: "card" },
|
|
15581
|
+
{ op: "insert", word: "care" },
|
|
15582
|
+
{ op: "insert", word: "dog" },
|
|
15583
|
+
{ op: "search", word: "car" },
|
|
15584
|
+
{ op: "search", word: "cab" },
|
|
15585
|
+
{ op: "prefix", word: "car" }
|
|
15586
|
+
];
|
|
15587
|
+
const TRIE_CODE = [
|
|
15588
|
+
"class TrieNode:",
|
|
15589
|
+
" children = {} # char -> node",
|
|
15590
|
+
" isEndOfWord = false",
|
|
15591
|
+
"",
|
|
15592
|
+
"function insert(word):",
|
|
15593
|
+
" node = root",
|
|
15594
|
+
" for char in word:",
|
|
15595
|
+
" if char not in node.children:",
|
|
15596
|
+
" node.children[char] = TrieNode()",
|
|
15597
|
+
" node = node.children[char]",
|
|
15598
|
+
" node.isEndOfWord = true",
|
|
15599
|
+
"",
|
|
15600
|
+
"function search(word):",
|
|
15601
|
+
" node = root",
|
|
15602
|
+
" for char in word:",
|
|
15603
|
+
" if char not in node.children:",
|
|
15604
|
+
" return false",
|
|
15605
|
+
" node = node.children[char]",
|
|
15606
|
+
" return node.isEndOfWord"
|
|
15607
|
+
];
|
|
15608
|
+
const LEGEND_ITEMS$5 = [
|
|
15609
|
+
{ color: "bg-gray-100", label: "Node", border: "#d1d5db" },
|
|
15610
|
+
{ color: "bg-blue-200", label: "Current", border: "#60a5fa" },
|
|
15611
|
+
{ color: "bg-green-400", label: "End of word" },
|
|
15612
|
+
{ color: "bg-yellow-200", label: "Path", border: "#fbbf24" },
|
|
15613
|
+
{ color: "bg-purple-400", label: "Match found" },
|
|
15614
|
+
{ color: "bg-red-400", label: "Not found" }
|
|
15615
|
+
];
|
|
15616
|
+
let nodeIdCounter = 0;
|
|
15617
|
+
function generateNodeId() {
|
|
15618
|
+
return `trie_${nodeIdCounter++}`;
|
|
15619
|
+
}
|
|
15620
|
+
function cloneNodes$1(nodes) {
|
|
15621
|
+
const cloned = /* @__PURE__ */ new Map();
|
|
15622
|
+
nodes.forEach((node, id) => {
|
|
15623
|
+
cloned.set(id, {
|
|
15624
|
+
...node,
|
|
15625
|
+
children: new Map(node.children)
|
|
15626
|
+
});
|
|
15627
|
+
});
|
|
15628
|
+
return cloned;
|
|
15629
|
+
}
|
|
15630
|
+
function generateTrieSteps() {
|
|
15631
|
+
nodeIdCounter = 0;
|
|
15632
|
+
const steps = [];
|
|
15633
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
15634
|
+
const rootId = generateNodeId();
|
|
15635
|
+
nodes.set(rootId, {
|
|
15636
|
+
id: rootId,
|
|
15637
|
+
char: "",
|
|
15638
|
+
children: /* @__PURE__ */ new Map(),
|
|
15639
|
+
isEndOfWord: false
|
|
15640
|
+
});
|
|
15641
|
+
steps.push({
|
|
15642
|
+
operation: "init",
|
|
15643
|
+
nodes: cloneNodes$1(nodes),
|
|
15644
|
+
rootId,
|
|
15645
|
+
description: "Initialize empty Trie with root node. Each node stores a character and links to children.",
|
|
15646
|
+
codeLine: 0
|
|
15647
|
+
});
|
|
15648
|
+
for (const { op, word } of OPERATIONS$2) {
|
|
15649
|
+
if (op === "insert") {
|
|
15650
|
+
steps.push({
|
|
15651
|
+
operation: "insert",
|
|
15652
|
+
nodes: cloneNodes$1(nodes),
|
|
15653
|
+
rootId,
|
|
15654
|
+
word,
|
|
15655
|
+
currentNodeId: rootId,
|
|
15656
|
+
description: `insert("${word}"): Start at root, traverse/create nodes for each character`,
|
|
15657
|
+
codeLine: 4,
|
|
15658
|
+
variables: { word: `"${word}"` }
|
|
15659
|
+
});
|
|
15660
|
+
let currentId = rootId;
|
|
15661
|
+
const path = [rootId];
|
|
15662
|
+
for (let i = 0; i < word.length; i++) {
|
|
15663
|
+
const char = word[i];
|
|
15664
|
+
const currentNode = nodes.get(currentId);
|
|
15665
|
+
if (currentNode.children.has(char)) {
|
|
15666
|
+
const nextId = currentNode.children.get(char);
|
|
15667
|
+
path.push(nextId);
|
|
15668
|
+
steps.push({
|
|
15669
|
+
operation: "traverse",
|
|
15670
|
+
nodes: cloneNodes$1(nodes),
|
|
15671
|
+
rootId,
|
|
15672
|
+
word,
|
|
15673
|
+
currentNodeId: nextId,
|
|
15674
|
+
highlightChar: char,
|
|
15675
|
+
highlightPath: [...path],
|
|
15676
|
+
description: `'${char}' exists → traverse to child node`,
|
|
15677
|
+
codeLine: 9,
|
|
15678
|
+
variables: { char: `'${char}'`, i, word: `"${word}"` }
|
|
15679
|
+
});
|
|
15680
|
+
currentId = nextId;
|
|
15681
|
+
} else {
|
|
15682
|
+
const newId = generateNodeId();
|
|
15683
|
+
nodes.set(newId, {
|
|
15684
|
+
id: newId,
|
|
15685
|
+
char,
|
|
15686
|
+
children: /* @__PURE__ */ new Map(),
|
|
15687
|
+
isEndOfWord: false
|
|
15688
|
+
});
|
|
15689
|
+
currentNode.children.set(char, newId);
|
|
15690
|
+
path.push(newId);
|
|
15691
|
+
steps.push({
|
|
15692
|
+
operation: "create",
|
|
15693
|
+
nodes: cloneNodes$1(nodes),
|
|
15694
|
+
rootId,
|
|
15695
|
+
word,
|
|
15696
|
+
currentNodeId: newId,
|
|
15697
|
+
highlightChar: char,
|
|
15698
|
+
highlightPath: [...path],
|
|
15699
|
+
description: `'${char}' not found → create new node`,
|
|
15700
|
+
codeLine: 8,
|
|
15701
|
+
variables: { char: `'${char}'`, i, word: `"${word}"` }
|
|
15702
|
+
});
|
|
15703
|
+
currentId = newId;
|
|
15704
|
+
}
|
|
15705
|
+
}
|
|
15706
|
+
const endNode = nodes.get(currentId);
|
|
15707
|
+
endNode.isEndOfWord = true;
|
|
15708
|
+
endNode.word = word;
|
|
15709
|
+
steps.push({
|
|
15710
|
+
operation: "markEnd",
|
|
15711
|
+
nodes: cloneNodes$1(nodes),
|
|
15712
|
+
rootId,
|
|
15713
|
+
word,
|
|
15714
|
+
currentNodeId: currentId,
|
|
15715
|
+
highlightPath: path,
|
|
15716
|
+
description: `Mark node as end of word "${word}"`,
|
|
15717
|
+
codeLine: 10,
|
|
15718
|
+
variables: { word: `"${word}"`, isEndOfWord: "true" }
|
|
15719
|
+
});
|
|
15720
|
+
} else if (op === "search") {
|
|
15721
|
+
steps.push({
|
|
15722
|
+
operation: "search",
|
|
15723
|
+
nodes: cloneNodes$1(nodes),
|
|
15724
|
+
rootId,
|
|
15725
|
+
word,
|
|
15726
|
+
currentNodeId: rootId,
|
|
15727
|
+
description: `search("${word}"): Check if word exists in Trie`,
|
|
15728
|
+
codeLine: 12,
|
|
15729
|
+
variables: { word: `"${word}"` }
|
|
15730
|
+
});
|
|
15731
|
+
let currentId = rootId;
|
|
15732
|
+
const path = [rootId];
|
|
15733
|
+
let found = true;
|
|
15734
|
+
for (let i = 0; i < word.length; i++) {
|
|
15735
|
+
const char = word[i];
|
|
15736
|
+
const currentNode = nodes.get(currentId);
|
|
15737
|
+
if (currentNode.children.has(char)) {
|
|
15738
|
+
const nextId = currentNode.children.get(char);
|
|
15739
|
+
path.push(nextId);
|
|
15740
|
+
steps.push({
|
|
15741
|
+
operation: "traverse",
|
|
15742
|
+
nodes: cloneNodes$1(nodes),
|
|
15743
|
+
rootId,
|
|
15744
|
+
word,
|
|
15745
|
+
currentNodeId: nextId,
|
|
15746
|
+
highlightChar: char,
|
|
15747
|
+
highlightPath: [...path],
|
|
15748
|
+
description: `'${char}' found → continue searching`,
|
|
15749
|
+
codeLine: 17,
|
|
15750
|
+
variables: { char: `'${char}'`, i, word: `"${word}"` }
|
|
15751
|
+
});
|
|
15752
|
+
currentId = nextId;
|
|
15753
|
+
} else {
|
|
15754
|
+
found = false;
|
|
15755
|
+
steps.push({
|
|
15756
|
+
operation: "notFound",
|
|
15757
|
+
nodes: cloneNodes$1(nodes),
|
|
15758
|
+
rootId,
|
|
15759
|
+
word,
|
|
15760
|
+
currentNodeId: currentId,
|
|
15761
|
+
highlightChar: char,
|
|
15762
|
+
highlightPath: path,
|
|
15763
|
+
description: `'${char}' not found → word "${word}" does not exist`,
|
|
15764
|
+
codeLine: 16,
|
|
15765
|
+
variables: { char: `'${char}'`, result: "false" }
|
|
15766
|
+
});
|
|
15767
|
+
break;
|
|
15768
|
+
}
|
|
15769
|
+
}
|
|
15770
|
+
if (found) {
|
|
15771
|
+
const endNode = nodes.get(currentId);
|
|
15772
|
+
if (endNode.isEndOfWord) {
|
|
15773
|
+
steps.push({
|
|
15774
|
+
operation: "found",
|
|
15775
|
+
nodes: cloneNodes$1(nodes),
|
|
15776
|
+
rootId,
|
|
15777
|
+
word,
|
|
15778
|
+
currentNodeId: currentId,
|
|
15779
|
+
highlightPath: path,
|
|
15780
|
+
description: `✓ Found! "${word}" exists in Trie`,
|
|
15781
|
+
codeLine: 18,
|
|
15782
|
+
variables: { word: `"${word}"`, result: "true" }
|
|
15783
|
+
});
|
|
15784
|
+
} else {
|
|
15785
|
+
steps.push({
|
|
15786
|
+
operation: "notFound",
|
|
15787
|
+
nodes: cloneNodes$1(nodes),
|
|
15788
|
+
rootId,
|
|
15789
|
+
word,
|
|
15790
|
+
currentNodeId: currentId,
|
|
15791
|
+
highlightPath: path,
|
|
15792
|
+
description: `Path exists but not marked as word end → "${word}" is only a prefix`,
|
|
15793
|
+
codeLine: 18,
|
|
15794
|
+
variables: { word: `"${word}"`, result: "false" }
|
|
15795
|
+
});
|
|
15796
|
+
}
|
|
15797
|
+
}
|
|
15798
|
+
} else if (op === "prefix") {
|
|
15799
|
+
steps.push({
|
|
15800
|
+
operation: "prefix",
|
|
15801
|
+
nodes: cloneNodes$1(nodes),
|
|
15802
|
+
rootId,
|
|
15803
|
+
prefix: word,
|
|
15804
|
+
currentNodeId: rootId,
|
|
15805
|
+
description: `startsWith("${word}"): Find all words with this prefix`,
|
|
15806
|
+
codeLine: 12,
|
|
15807
|
+
variables: { prefix: `"${word}"` }
|
|
15808
|
+
});
|
|
15809
|
+
let currentId = rootId;
|
|
15810
|
+
const path = [rootId];
|
|
15811
|
+
let prefixExists = true;
|
|
15812
|
+
for (let i = 0; i < word.length; i++) {
|
|
15813
|
+
const char = word[i];
|
|
15814
|
+
const currentNode = nodes.get(currentId);
|
|
15815
|
+
if (currentNode.children.has(char)) {
|
|
15816
|
+
const nextId = currentNode.children.get(char);
|
|
15817
|
+
path.push(nextId);
|
|
15818
|
+
currentId = nextId;
|
|
15819
|
+
} else {
|
|
15820
|
+
prefixExists = false;
|
|
15821
|
+
break;
|
|
15822
|
+
}
|
|
15823
|
+
}
|
|
15824
|
+
if (prefixExists) {
|
|
15825
|
+
const matchedWords = [];
|
|
15826
|
+
const collectWords = (nodeId) => {
|
|
15827
|
+
const node = nodes.get(nodeId);
|
|
15828
|
+
if (node.isEndOfWord && node.word) {
|
|
15829
|
+
matchedWords.push(node.word);
|
|
15830
|
+
}
|
|
15831
|
+
node.children.forEach((childId) => collectWords(childId));
|
|
15832
|
+
};
|
|
15833
|
+
collectWords(currentId);
|
|
15834
|
+
steps.push({
|
|
15835
|
+
operation: "found",
|
|
15836
|
+
nodes: cloneNodes$1(nodes),
|
|
15837
|
+
rootId,
|
|
15838
|
+
prefix: word,
|
|
15839
|
+
currentNodeId: currentId,
|
|
15840
|
+
highlightPath: path,
|
|
15841
|
+
matchedWords,
|
|
15842
|
+
description: `Found ${matchedWords.length} word(s) with prefix "${word}": ${matchedWords.join(", ")}`,
|
|
15843
|
+
codeLine: 18,
|
|
15844
|
+
variables: { prefix: `"${word}"`, count: matchedWords.length }
|
|
15845
|
+
});
|
|
15846
|
+
} else {
|
|
15847
|
+
steps.push({
|
|
15848
|
+
operation: "notFound",
|
|
15849
|
+
nodes: cloneNodes$1(nodes),
|
|
15850
|
+
rootId,
|
|
15851
|
+
prefix: word,
|
|
15852
|
+
highlightPath: path,
|
|
15853
|
+
description: `No words found with prefix "${word}"`,
|
|
15854
|
+
codeLine: 16,
|
|
15855
|
+
variables: { prefix: `"${word}"`, count: 0 }
|
|
15856
|
+
});
|
|
15857
|
+
}
|
|
15858
|
+
}
|
|
15859
|
+
}
|
|
15860
|
+
const allWords = [];
|
|
15861
|
+
const collectAllWords = (nodeId) => {
|
|
15862
|
+
const node = nodes.get(nodeId);
|
|
15863
|
+
if (node.isEndOfWord && node.word) {
|
|
15864
|
+
allWords.push(node.word);
|
|
15865
|
+
}
|
|
15866
|
+
node.children.forEach((childId) => collectAllWords(childId));
|
|
15867
|
+
};
|
|
15868
|
+
collectAllWords(rootId);
|
|
15869
|
+
steps.push({
|
|
15870
|
+
operation: "done",
|
|
15871
|
+
nodes: cloneNodes$1(nodes),
|
|
15872
|
+
rootId,
|
|
15873
|
+
matchedWords: allWords,
|
|
15874
|
+
description: `✓ Done! Trie contains ${allWords.length} words: ${allWords.join(", ")}`,
|
|
15875
|
+
codeLine: -1
|
|
15876
|
+
});
|
|
15877
|
+
return steps;
|
|
15878
|
+
}
|
|
15879
|
+
const TrieNodeComponent = ({ nodeId, nodes, currentNodeId, highlightPath, depth }) => {
|
|
15880
|
+
const node = nodes.get(nodeId);
|
|
15881
|
+
if (!node) return null;
|
|
15882
|
+
const isCurrent = currentNodeId === nodeId;
|
|
15883
|
+
const isInPath = highlightPath == null ? void 0 : highlightPath.includes(nodeId);
|
|
15884
|
+
const children = Array.from(node.children.entries());
|
|
15885
|
+
let nodeStyle = "border-gray-300 bg-gray-50";
|
|
15886
|
+
if (isCurrent) {
|
|
15887
|
+
nodeStyle = "border-blue-400 bg-blue-100 ring-2 ring-blue-300";
|
|
15888
|
+
} else if (isInPath) {
|
|
15889
|
+
nodeStyle = "border-yellow-400 bg-yellow-100";
|
|
15890
|
+
}
|
|
15891
|
+
if (node.isEndOfWord) {
|
|
15892
|
+
nodeStyle += " ring-2 ring-green-400";
|
|
15893
|
+
}
|
|
15894
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
|
|
15895
|
+
/* @__PURE__ */ jsx(
|
|
15896
|
+
"div",
|
|
15897
|
+
{
|
|
15898
|
+
className: `
|
|
15899
|
+
w-10 h-10 rounded-lg border-2 flex items-center justify-center
|
|
15900
|
+
text-sm font-bold transition-all duration-300
|
|
15901
|
+
${nodeStyle}
|
|
15902
|
+
`,
|
|
15903
|
+
children: node.char || "∅"
|
|
15904
|
+
}
|
|
15905
|
+
),
|
|
15906
|
+
node.isEndOfWord && /* @__PURE__ */ jsx("div", { className: "text-[9px] text-green-600 font-medium mt-0.5", children: node.word }),
|
|
15907
|
+
children.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15908
|
+
/* @__PURE__ */ jsx("div", { className: "w-0.5 h-4 bg-gray-300" }),
|
|
15909
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2", children: children.map(([char, childId]) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
|
|
15910
|
+
/* @__PURE__ */ jsx("div", { className: "text-[10px] text-gray-500 mb-1", children: char }),
|
|
15911
|
+
/* @__PURE__ */ jsx(
|
|
15912
|
+
TrieNodeComponent,
|
|
15913
|
+
{
|
|
15914
|
+
nodeId: childId,
|
|
15915
|
+
nodes,
|
|
15916
|
+
currentNodeId,
|
|
15917
|
+
highlightPath,
|
|
15918
|
+
depth: depth + 1
|
|
15919
|
+
}
|
|
15920
|
+
)
|
|
15921
|
+
] }, childId)) })
|
|
15922
|
+
] })
|
|
15923
|
+
] });
|
|
15924
|
+
};
|
|
15925
|
+
const TrieVisualizerComponent = ({
|
|
15926
|
+
showControls = true,
|
|
15927
|
+
showCode = true,
|
|
15928
|
+
className = ""
|
|
15929
|
+
}) => {
|
|
15930
|
+
const VISUALIZER_ID = "trie-visualizer";
|
|
15931
|
+
const { copyUrlToClipboard } = useUrlState({ prefix: "trie", scrollToId: VISUALIZER_ID });
|
|
15932
|
+
const generateSteps = useMemo(() => generateTrieSteps, []);
|
|
15933
|
+
const {
|
|
15934
|
+
steps,
|
|
15935
|
+
currentStep,
|
|
15936
|
+
currentStepData,
|
|
15937
|
+
isPlaying,
|
|
15938
|
+
speed,
|
|
15939
|
+
setSpeed,
|
|
15940
|
+
handlePlayPause,
|
|
15941
|
+
handleStep,
|
|
15942
|
+
handleStepBack,
|
|
15943
|
+
handleReset
|
|
15944
|
+
} = useVisualizerPlayback({
|
|
15945
|
+
generateSteps
|
|
15946
|
+
});
|
|
15947
|
+
const stepData = currentStepData || {
|
|
15948
|
+
operation: "init",
|
|
15949
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
15950
|
+
rootId: "",
|
|
15951
|
+
description: ""
|
|
15952
|
+
};
|
|
15953
|
+
const getStatusVariant = () => {
|
|
15954
|
+
if (stepData.operation === "notFound") return "error";
|
|
15955
|
+
if (stepData.operation === "found" || stepData.operation === "markEnd") return "success";
|
|
15956
|
+
if (stepData.operation === "done") return "warning";
|
|
15957
|
+
return "default";
|
|
15958
|
+
};
|
|
15959
|
+
const handleShare = useCallback(async () => {
|
|
15960
|
+
return copyUrlToClipboard({ step: currentStep });
|
|
15961
|
+
}, [copyUrlToClipboard, currentStep]);
|
|
15962
|
+
return /* @__PURE__ */ jsxs(
|
|
15963
|
+
"div",
|
|
15964
|
+
{
|
|
15965
|
+
id: VISUALIZER_ID,
|
|
15966
|
+
className: `bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden ${className}`,
|
|
15967
|
+
children: [
|
|
15968
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-gradient-to-r from-teal-50 to-cyan-50 border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
|
|
15969
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
15970
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900", children: "Trie (Prefix Tree)" }),
|
|
15971
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
15972
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-teal-100 text-teal-700 rounded", children: "Insert: O(m)" }),
|
|
15973
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-cyan-100 text-cyan-700 rounded", children: "Search: O(m)" })
|
|
15974
|
+
] })
|
|
15975
|
+
] }),
|
|
15976
|
+
/* @__PURE__ */ jsx(ShareButton, { onShare: handleShare })
|
|
15977
|
+
] }) }),
|
|
15978
|
+
/* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: `flex gap-4 ${showCode ? "flex-col lg:flex-row" : ""}`, children: [
|
|
15979
|
+
/* @__PURE__ */ jsxs(VisualizationArea, { minHeight: 400, className: showCode ? "flex-1" : "w-full", children: [
|
|
15980
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4 p-3 bg-gradient-to-r from-teal-50 to-cyan-50 rounded-lg border border-teal-200", children: [
|
|
15981
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-teal-800 mb-2", children: "🌲 Trie - Prefix Tree" }),
|
|
15982
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs text-teal-700 space-y-1", children: [
|
|
15983
|
+
/* @__PURE__ */ jsx("div", { children: "• Each node represents a character" }),
|
|
15984
|
+
/* @__PURE__ */ jsx("div", { children: "• Words share common prefixes (space efficient)" }),
|
|
15985
|
+
/* @__PURE__ */ jsx("div", { children: "• Perfect for autocomplete & spell checking" }),
|
|
15986
|
+
/* @__PURE__ */ jsx("div", { children: "• m = word length, n = number of words" })
|
|
15987
|
+
] })
|
|
15988
|
+
] }),
|
|
15989
|
+
/* @__PURE__ */ jsx("div", { className: "flex justify-center overflow-x-auto py-4", children: stepData.nodes.size > 0 && /* @__PURE__ */ jsx(
|
|
15990
|
+
TrieNodeComponent,
|
|
15991
|
+
{
|
|
15992
|
+
nodeId: stepData.rootId,
|
|
15993
|
+
nodes: stepData.nodes,
|
|
15994
|
+
currentNodeId: stepData.currentNodeId,
|
|
15995
|
+
highlightPath: stepData.highlightPath,
|
|
15996
|
+
depth: 0
|
|
15997
|
+
}
|
|
15998
|
+
) }),
|
|
15999
|
+
stepData.matchedWords && stepData.matchedWords.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-4 p-3 bg-purple-50 rounded-lg border border-purple-200", children: [
|
|
16000
|
+
/* @__PURE__ */ jsx("div", { className: "text-xs font-medium text-purple-800 mb-2", children: "Words found:" }),
|
|
16001
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: stepData.matchedWords.map((word) => /* @__PURE__ */ jsx(
|
|
16002
|
+
"span",
|
|
16003
|
+
{
|
|
16004
|
+
className: "px-2 py-1 bg-purple-100 text-purple-700 text-sm rounded font-mono",
|
|
16005
|
+
children: word
|
|
16006
|
+
},
|
|
16007
|
+
word
|
|
16008
|
+
)) })
|
|
16009
|
+
] }),
|
|
16010
|
+
/* @__PURE__ */ jsx(
|
|
16011
|
+
StatusPanel,
|
|
16012
|
+
{
|
|
16013
|
+
description: stepData.description,
|
|
16014
|
+
currentStep,
|
|
16015
|
+
totalSteps: steps.length,
|
|
16016
|
+
variant: getStatusVariant()
|
|
16017
|
+
}
|
|
16018
|
+
)
|
|
16019
|
+
] }),
|
|
16020
|
+
showCode && /* @__PURE__ */ jsxs("div", { className: "w-full lg:w-56 flex-shrink-0 space-y-2", children: [
|
|
16021
|
+
/* @__PURE__ */ jsx(
|
|
16022
|
+
CodePanel,
|
|
16023
|
+
{
|
|
16024
|
+
code: TRIE_CODE,
|
|
16025
|
+
activeLine: (currentStepData == null ? void 0 : currentStepData.codeLine) ?? -1,
|
|
16026
|
+
variables: currentStepData == null ? void 0 : currentStepData.variables
|
|
16027
|
+
}
|
|
16028
|
+
),
|
|
16029
|
+
/* @__PURE__ */ jsx(HelpPanel, {})
|
|
16030
|
+
] })
|
|
16031
|
+
] }) }),
|
|
16032
|
+
showControls && /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
|
|
16033
|
+
/* @__PURE__ */ jsx(
|
|
16034
|
+
ControlPanel,
|
|
16035
|
+
{
|
|
16036
|
+
isPlaying,
|
|
16037
|
+
currentStep,
|
|
16038
|
+
totalSteps: steps.length,
|
|
16039
|
+
speed,
|
|
16040
|
+
onPlayPause: handlePlayPause,
|
|
16041
|
+
onStep: handleStep,
|
|
16042
|
+
onStepBack: handleStepBack,
|
|
16043
|
+
onReset: handleReset,
|
|
16044
|
+
onSpeedChange: setSpeed,
|
|
16045
|
+
accentColor: "teal"
|
|
16046
|
+
}
|
|
16047
|
+
),
|
|
16048
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$5 })
|
|
16049
|
+
] })
|
|
16050
|
+
]
|
|
16051
|
+
}
|
|
16052
|
+
);
|
|
16053
|
+
};
|
|
16054
|
+
const TrieVisualizer = React.memo(TrieVisualizerComponent);
|
|
16055
|
+
const INITIAL_SIZE = 8;
|
|
16056
|
+
const OPERATIONS$1 = [
|
|
16057
|
+
{ op: "union", a: 0, b: 1 },
|
|
16058
|
+
{ op: "union", a: 2, b: 3 },
|
|
16059
|
+
{ op: "union", a: 4, b: 5 },
|
|
16060
|
+
{ op: "union", a: 6, b: 7 },
|
|
16061
|
+
{ op: "union", a: 0, b: 2 },
|
|
16062
|
+
{ op: "union", a: 4, b: 6 },
|
|
16063
|
+
{ op: "find", a: 3 },
|
|
16064
|
+
{ op: "connected", a: 1, b: 3 },
|
|
16065
|
+
{ op: "connected", a: 0, b: 5 },
|
|
16066
|
+
{ op: "union", a: 0, b: 4 },
|
|
16067
|
+
{ op: "connected", a: 3, b: 7 }
|
|
16068
|
+
];
|
|
16069
|
+
const UF_CODE = [
|
|
16070
|
+
"class UnionFind:",
|
|
16071
|
+
" parent[] = [0, 1, ..., n-1]",
|
|
16072
|
+
" rank[] = [0, 0, ..., 0]",
|
|
16073
|
+
"",
|
|
16074
|
+
"function find(x):",
|
|
16075
|
+
" if parent[x] != x:",
|
|
16076
|
+
" parent[x] = find(parent[x])",
|
|
16077
|
+
" # path compression",
|
|
16078
|
+
" return parent[x]",
|
|
16079
|
+
"",
|
|
16080
|
+
"function union(x, y):",
|
|
16081
|
+
" rootX = find(x)",
|
|
16082
|
+
" rootY = find(y)",
|
|
16083
|
+
" if rootX == rootY: return",
|
|
16084
|
+
" # union by rank",
|
|
16085
|
+
" if rank[rootX] < rank[rootY]:",
|
|
16086
|
+
" parent[rootX] = rootY",
|
|
16087
|
+
" else if rank[rootX] > rank[rootY]:",
|
|
16088
|
+
" parent[rootY] = rootX",
|
|
16089
|
+
" else:",
|
|
16090
|
+
" parent[rootY] = rootX",
|
|
16091
|
+
" rank[rootX] += 1"
|
|
16092
|
+
];
|
|
16093
|
+
const LEGEND_ITEMS$4 = [
|
|
16094
|
+
{ color: "bg-gray-100", label: "Element", border: "#d1d5db" },
|
|
16095
|
+
{ color: "bg-blue-200", label: "Current", border: "#60a5fa" },
|
|
16096
|
+
{ color: "bg-yellow-200", label: "Path to root", border: "#fbbf24" },
|
|
16097
|
+
{ color: "bg-green-400", label: "Root / Same set" },
|
|
16098
|
+
{ color: "bg-red-400", label: "Different sets" },
|
|
16099
|
+
{ color: "bg-purple-400", label: "Union edge" }
|
|
16100
|
+
];
|
|
16101
|
+
function cloneElements(elements) {
|
|
16102
|
+
return elements.map((el) => ({ ...el }));
|
|
16103
|
+
}
|
|
16104
|
+
function generateUnionFindSteps() {
|
|
16105
|
+
const steps = [];
|
|
16106
|
+
const elements = Array.from({ length: INITIAL_SIZE }, (_, i) => ({
|
|
16107
|
+
id: i,
|
|
16108
|
+
parent: i,
|
|
16109
|
+
rank: 0
|
|
16110
|
+
}));
|
|
16111
|
+
steps.push({
|
|
16112
|
+
operation: "init",
|
|
16113
|
+
elements: cloneElements(elements),
|
|
16114
|
+
description: `Initialize ${INITIAL_SIZE} elements. Each element is its own parent (self-loop).`,
|
|
16115
|
+
codeLine: 1,
|
|
16116
|
+
variables: { n: INITIAL_SIZE }
|
|
16117
|
+
});
|
|
16118
|
+
const find = (x, trackPath = false) => {
|
|
16119
|
+
const path = [x];
|
|
16120
|
+
let current = x;
|
|
16121
|
+
while (elements[current].parent !== current) {
|
|
16122
|
+
current = elements[current].parent;
|
|
16123
|
+
path.push(current);
|
|
16124
|
+
}
|
|
16125
|
+
if (trackPath && path.length > 2) {
|
|
16126
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
16127
|
+
elements[path[i]].parent = current;
|
|
16128
|
+
}
|
|
16129
|
+
}
|
|
16130
|
+
return { root: current, path };
|
|
16131
|
+
};
|
|
16132
|
+
for (const operation of OPERATIONS$1) {
|
|
16133
|
+
if (operation.op === "find") {
|
|
16134
|
+
const x = operation.a;
|
|
16135
|
+
steps.push({
|
|
16136
|
+
operation: "find",
|
|
16137
|
+
elements: cloneElements(elements),
|
|
16138
|
+
highlightElements: [x],
|
|
16139
|
+
description: `find(${x}): Find the root of element ${x}`,
|
|
16140
|
+
codeLine: 4,
|
|
16141
|
+
variables: { x }
|
|
16142
|
+
});
|
|
16143
|
+
const { root, path } = find(x);
|
|
16144
|
+
steps.push({
|
|
16145
|
+
operation: "find",
|
|
16146
|
+
elements: cloneElements(elements),
|
|
16147
|
+
highlightElements: [root],
|
|
16148
|
+
pathToRoot: path,
|
|
16149
|
+
description: `Path: ${path.join(" → ")}. Root is ${root}`,
|
|
16150
|
+
codeLine: 8,
|
|
16151
|
+
variables: { x, root }
|
|
16152
|
+
});
|
|
16153
|
+
if (path.length > 2) {
|
|
16154
|
+
find(x, true);
|
|
16155
|
+
steps.push({
|
|
16156
|
+
operation: "pathCompress",
|
|
16157
|
+
elements: cloneElements(elements),
|
|
16158
|
+
highlightElements: path.slice(0, -1),
|
|
16159
|
+
pathToRoot: path,
|
|
16160
|
+
description: `Path compression: All nodes on path now point directly to root ${root}`,
|
|
16161
|
+
codeLine: 6,
|
|
16162
|
+
variables: { x, root }
|
|
16163
|
+
});
|
|
16164
|
+
}
|
|
16165
|
+
} else if (operation.op === "union") {
|
|
16166
|
+
const x = operation.a;
|
|
16167
|
+
const y = operation.b;
|
|
16168
|
+
steps.push({
|
|
16169
|
+
operation: "union",
|
|
16170
|
+
elements: cloneElements(elements),
|
|
16171
|
+
highlightElements: [x, y],
|
|
16172
|
+
description: `union(${x}, ${y}): Merge sets containing ${x} and ${y}`,
|
|
16173
|
+
codeLine: 10,
|
|
16174
|
+
variables: { x, y }
|
|
16175
|
+
});
|
|
16176
|
+
const { root: rootX, path: pathX } = find(x);
|
|
16177
|
+
const { root: rootY, path: pathY } = find(y);
|
|
16178
|
+
steps.push({
|
|
16179
|
+
operation: "find",
|
|
16180
|
+
elements: cloneElements(elements),
|
|
16181
|
+
highlightElements: [rootX, rootY],
|
|
16182
|
+
pathToRoot: [...pathX, ...pathY],
|
|
16183
|
+
description: `find(${x}) = ${rootX}, find(${y}) = ${rootY}`,
|
|
16184
|
+
codeLine: 11,
|
|
16185
|
+
variables: { x, y, rootX, rootY }
|
|
16186
|
+
});
|
|
16187
|
+
if (rootX === rootY) {
|
|
16188
|
+
steps.push({
|
|
16189
|
+
operation: "sameSet",
|
|
16190
|
+
elements: cloneElements(elements),
|
|
16191
|
+
highlightElements: [rootX],
|
|
16192
|
+
description: `Already in same set! No union needed.`,
|
|
16193
|
+
codeLine: 13,
|
|
16194
|
+
variables: { rootX, rootY, "same": "true" }
|
|
16195
|
+
});
|
|
16196
|
+
} else {
|
|
16197
|
+
const rankX = elements[rootX].rank;
|
|
16198
|
+
const rankY = elements[rootY].rank;
|
|
16199
|
+
let newRoot;
|
|
16200
|
+
if (rankX < rankY) {
|
|
16201
|
+
elements[rootX].parent = rootY;
|
|
16202
|
+
newRoot = rootY;
|
|
16203
|
+
} else if (rankX > rankY) {
|
|
16204
|
+
elements[rootY].parent = rootX;
|
|
16205
|
+
newRoot = rootX;
|
|
16206
|
+
} else {
|
|
16207
|
+
elements[rootY].parent = rootX;
|
|
16208
|
+
elements[rootX].rank++;
|
|
16209
|
+
newRoot = rootX;
|
|
16210
|
+
}
|
|
16211
|
+
steps.push({
|
|
16212
|
+
operation: "unionByRank",
|
|
16213
|
+
elements: cloneElements(elements),
|
|
16214
|
+
highlightElements: [newRoot],
|
|
16215
|
+
highlightEdge: [rootX, rootY],
|
|
16216
|
+
description: `Union by rank: rank[${rootX}]=${rankX}, rank[${rootY}]=${rankY}. ${newRoot} becomes root.`,
|
|
16217
|
+
codeLine: rankX === rankY ? 20 : rankX < rankY ? 16 : 18,
|
|
16218
|
+
variables: { rootX, rootY, rankX, rankY, newRoot }
|
|
16219
|
+
});
|
|
16220
|
+
}
|
|
16221
|
+
} else if (operation.op === "connected") {
|
|
16222
|
+
const x = operation.a;
|
|
16223
|
+
const y = operation.b;
|
|
16224
|
+
steps.push({
|
|
16225
|
+
operation: "find",
|
|
16226
|
+
elements: cloneElements(elements),
|
|
16227
|
+
highlightElements: [x, y],
|
|
16228
|
+
description: `connected(${x}, ${y}): Check if ${x} and ${y} are in the same set`,
|
|
16229
|
+
codeLine: 4,
|
|
16230
|
+
variables: { x, y }
|
|
16231
|
+
});
|
|
16232
|
+
const { root: rootX, path: pathX } = find(x);
|
|
16233
|
+
const { root: rootY, path: pathY } = find(y);
|
|
16234
|
+
const connected = rootX === rootY;
|
|
16235
|
+
steps.push({
|
|
16236
|
+
operation: "sameSet",
|
|
16237
|
+
elements: cloneElements(elements),
|
|
16238
|
+
highlightElements: connected ? [rootX] : [rootX, rootY],
|
|
16239
|
+
pathToRoot: [...pathX, ...pathY],
|
|
16240
|
+
description: connected ? `✓ Yes! Both ${x} and ${y} have root ${rootX}` : `✗ No! ${x} has root ${rootX}, ${y} has root ${rootY}`,
|
|
16241
|
+
codeLine: 13,
|
|
16242
|
+
variables: { x, y, rootX, rootY, connected: connected ? "true" : "false" },
|
|
16243
|
+
queryResult: connected
|
|
16244
|
+
});
|
|
16245
|
+
}
|
|
16246
|
+
}
|
|
16247
|
+
const roots = /* @__PURE__ */ new Set();
|
|
16248
|
+
for (let i = 0; i < INITIAL_SIZE; i++) {
|
|
16249
|
+
roots.add(find(i).root);
|
|
16250
|
+
}
|
|
16251
|
+
steps.push({
|
|
16252
|
+
operation: "done",
|
|
16253
|
+
elements: cloneElements(elements),
|
|
16254
|
+
description: `✓ Done! ${roots.size} distinct set(s) remain.`,
|
|
16255
|
+
codeLine: -1,
|
|
16256
|
+
variables: { sets: roots.size }
|
|
16257
|
+
});
|
|
16258
|
+
return steps;
|
|
16259
|
+
}
|
|
16260
|
+
const UnionFindVisualizerComponent = ({
|
|
16261
|
+
showControls = true,
|
|
16262
|
+
showCode = true,
|
|
16263
|
+
className = ""
|
|
16264
|
+
}) => {
|
|
16265
|
+
const VISUALIZER_ID = "unionfind-visualizer";
|
|
16266
|
+
const { copyUrlToClipboard } = useUrlState({ prefix: "uf", scrollToId: VISUALIZER_ID });
|
|
16267
|
+
const generateSteps = useMemo(() => generateUnionFindSteps, []);
|
|
16268
|
+
const {
|
|
16269
|
+
steps,
|
|
16270
|
+
currentStep,
|
|
16271
|
+
currentStepData,
|
|
16272
|
+
isPlaying,
|
|
16273
|
+
speed,
|
|
16274
|
+
setSpeed,
|
|
16275
|
+
handlePlayPause,
|
|
16276
|
+
handleStep,
|
|
16277
|
+
handleStepBack,
|
|
16278
|
+
handleReset
|
|
16279
|
+
} = useVisualizerPlayback({
|
|
16280
|
+
generateSteps
|
|
16281
|
+
});
|
|
16282
|
+
const stepData = currentStepData || {
|
|
16283
|
+
operation: "init",
|
|
16284
|
+
elements: [],
|
|
16285
|
+
description: ""
|
|
16286
|
+
};
|
|
16287
|
+
const groups = useMemo(() => {
|
|
16288
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
16289
|
+
for (const el of stepData.elements) {
|
|
16290
|
+
let current = el.id;
|
|
16291
|
+
while (stepData.elements[current].parent !== current) {
|
|
16292
|
+
current = stepData.elements[current].parent;
|
|
16293
|
+
}
|
|
16294
|
+
const root = current;
|
|
16295
|
+
if (!groupMap.has(root)) {
|
|
16296
|
+
groupMap.set(root, []);
|
|
16297
|
+
}
|
|
16298
|
+
groupMap.get(root).push(el.id);
|
|
16299
|
+
}
|
|
16300
|
+
return groupMap;
|
|
16301
|
+
}, [stepData.elements]);
|
|
16302
|
+
const getElementStyle = (id) => {
|
|
16303
|
+
var _a, _b, _c;
|
|
16304
|
+
const isHighlighted = (_a = stepData.highlightElements) == null ? void 0 : _a.includes(id);
|
|
16305
|
+
const isInPath = (_b = stepData.pathToRoot) == null ? void 0 : _b.includes(id);
|
|
16306
|
+
const isRoot = ((_c = stepData.elements[id]) == null ? void 0 : _c.parent) === id;
|
|
16307
|
+
if (isHighlighted && isRoot) return "border-green-500 bg-green-100 ring-2 ring-green-300";
|
|
16308
|
+
if (isHighlighted) return "border-blue-400 bg-blue-100 ring-2 ring-blue-300";
|
|
16309
|
+
if (isInPath) return "border-yellow-400 bg-yellow-100";
|
|
16310
|
+
if (isRoot) return "border-green-300 bg-green-50";
|
|
16311
|
+
return "border-gray-300 bg-gray-50";
|
|
16312
|
+
};
|
|
16313
|
+
const getStatusVariant = () => {
|
|
16314
|
+
if (stepData.queryResult === false) return "error";
|
|
16315
|
+
if (stepData.queryResult === true) return "success";
|
|
16316
|
+
if (stepData.operation === "done") return "warning";
|
|
16317
|
+
if (stepData.operation === "pathCompress" || stepData.operation === "unionByRank") return "success";
|
|
16318
|
+
return "default";
|
|
16319
|
+
};
|
|
16320
|
+
const handleShare = useCallback(async () => {
|
|
16321
|
+
return copyUrlToClipboard({ step: currentStep });
|
|
16322
|
+
}, [copyUrlToClipboard, currentStep]);
|
|
16323
|
+
const groupColors = ["bg-blue-50", "bg-green-50", "bg-yellow-50", "bg-purple-50", "bg-pink-50", "bg-cyan-50", "bg-orange-50", "bg-rose-50"];
|
|
16324
|
+
return /* @__PURE__ */ jsxs(
|
|
16325
|
+
"div",
|
|
16326
|
+
{
|
|
16327
|
+
id: VISUALIZER_ID,
|
|
16328
|
+
className: `bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden ${className}`,
|
|
16329
|
+
children: [
|
|
16330
|
+
/* @__PURE__ */ jsx("div", { className: "px-4 py-3 bg-gradient-to-r from-violet-50 to-purple-50 border-b border-gray-200", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
|
|
16331
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
16332
|
+
/* @__PURE__ */ jsx("h3", { className: "font-semibold text-gray-900", children: "Union-Find (Disjoint Set)" }),
|
|
16333
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
16334
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-violet-100 text-violet-700 rounded", children: "Find: O(α(n))" }),
|
|
16335
|
+
/* @__PURE__ */ jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-purple-100 text-purple-700 rounded", children: "Union: O(α(n))" })
|
|
16336
|
+
] })
|
|
16337
|
+
] }),
|
|
16338
|
+
/* @__PURE__ */ jsx(ShareButton, { onShare: handleShare })
|
|
16339
|
+
] }) }),
|
|
16340
|
+
/* @__PURE__ */ jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: `flex gap-4 ${showCode ? "flex-col lg:flex-row" : ""}`, children: [
|
|
16341
|
+
/* @__PURE__ */ jsxs(VisualizationArea, { minHeight: 400, className: showCode ? "flex-1" : "w-full", children: [
|
|
16342
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4 p-3 bg-gradient-to-r from-violet-50 to-purple-50 rounded-lg border border-violet-200", children: [
|
|
16343
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-semibold text-violet-800 mb-2", children: "🔗 Union-Find / Disjoint Set Union (DSU)" }),
|
|
16344
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs text-violet-700 space-y-1", children: [
|
|
16345
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
16346
|
+
"• ",
|
|
16347
|
+
/* @__PURE__ */ jsx("strong", { children: "Path Compression" }),
|
|
16348
|
+
": Flattens tree on find()"
|
|
16349
|
+
] }),
|
|
16350
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
16351
|
+
"• ",
|
|
16352
|
+
/* @__PURE__ */ jsx("strong", { children: "Union by Rank" }),
|
|
16353
|
+
": Attach smaller tree to larger"
|
|
16354
|
+
] }),
|
|
16355
|
+
/* @__PURE__ */ jsx("div", { children: "• α(n) = inverse Ackermann, practically ≤ 4" }),
|
|
16356
|
+
/* @__PURE__ */ jsx("div", { children: "• Used in Kruskal's MST, cycle detection, networks" })
|
|
16357
|
+
] })
|
|
16358
|
+
] }),
|
|
16359
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
16360
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm font-medium text-gray-700 mb-2", children: "Elements (parent pointers shown as arrows)" }),
|
|
16361
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-3 justify-center", children: stepData.elements.map((el) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center", children: [
|
|
16362
|
+
/* @__PURE__ */ jsxs(
|
|
16363
|
+
"div",
|
|
16364
|
+
{
|
|
16365
|
+
className: `
|
|
16366
|
+
w-12 h-12 rounded-lg border-2 flex flex-col items-center justify-center
|
|
16367
|
+
text-sm font-bold transition-all duration-300
|
|
16368
|
+
${getElementStyle(el.id)}
|
|
16369
|
+
`,
|
|
16370
|
+
children: [
|
|
16371
|
+
/* @__PURE__ */ jsx("span", { children: el.id }),
|
|
16372
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[9px] text-gray-500", children: [
|
|
16373
|
+
"r:",
|
|
16374
|
+
el.rank
|
|
16375
|
+
] })
|
|
16376
|
+
]
|
|
16377
|
+
}
|
|
16378
|
+
),
|
|
16379
|
+
el.parent !== el.id && /* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-500 mt-1", children: [
|
|
16380
|
+
"↑ ",
|
|
16381
|
+
el.parent
|
|
16382
|
+
] }),
|
|
16383
|
+
el.parent === el.id && /* @__PURE__ */ jsx("div", { className: "text-xs text-green-600 mt-1 font-medium", children: "root" })
|
|
16384
|
+
] }, el.id)) })
|
|
16385
|
+
] }),
|
|
16386
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-4", children: [
|
|
16387
|
+
/* @__PURE__ */ jsxs("div", { className: "text-sm font-medium text-gray-700 mb-2", children: [
|
|
16388
|
+
"Connected Components (",
|
|
16389
|
+
groups.size,
|
|
16390
|
+
" set",
|
|
16391
|
+
groups.size !== 1 ? "s" : "",
|
|
16392
|
+
")"
|
|
16393
|
+
] }),
|
|
16394
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: Array.from(groups.entries()).map(([root, members], idx) => /* @__PURE__ */ jsxs(
|
|
16395
|
+
"div",
|
|
16396
|
+
{
|
|
16397
|
+
className: `px-3 py-2 rounded-lg border ${groupColors[idx % groupColors.length]} border-gray-300`,
|
|
16398
|
+
children: [
|
|
16399
|
+
/* @__PURE__ */ jsxs("div", { className: "text-xs text-gray-600 mb-1", children: [
|
|
16400
|
+
"Root: ",
|
|
16401
|
+
root
|
|
16402
|
+
] }),
|
|
16403
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-1", children: members.sort((a, b) => a - b).map((m) => /* @__PURE__ */ jsx(
|
|
16404
|
+
"span",
|
|
16405
|
+
{
|
|
16406
|
+
className: `w-6 h-6 rounded flex items-center justify-center text-xs font-medium
|
|
16407
|
+
${m === root ? "bg-green-200 text-green-800" : "bg-white text-gray-700 border border-gray-300"}
|
|
16408
|
+
`,
|
|
16409
|
+
children: m
|
|
16410
|
+
},
|
|
16411
|
+
m
|
|
16412
|
+
)) })
|
|
16413
|
+
]
|
|
16414
|
+
},
|
|
16415
|
+
root
|
|
16416
|
+
)) })
|
|
16417
|
+
] }),
|
|
16418
|
+
/* @__PURE__ */ jsx(
|
|
16419
|
+
StatusPanel,
|
|
16420
|
+
{
|
|
16421
|
+
description: stepData.description,
|
|
16422
|
+
currentStep,
|
|
16423
|
+
totalSteps: steps.length,
|
|
16424
|
+
variant: getStatusVariant()
|
|
16425
|
+
}
|
|
16426
|
+
)
|
|
16427
|
+
] }),
|
|
16428
|
+
showCode && /* @__PURE__ */ jsxs("div", { className: "w-full lg:w-56 flex-shrink-0 space-y-2", children: [
|
|
16429
|
+
/* @__PURE__ */ jsx(
|
|
16430
|
+
CodePanel,
|
|
16431
|
+
{
|
|
16432
|
+
code: UF_CODE,
|
|
16433
|
+
activeLine: (currentStepData == null ? void 0 : currentStepData.codeLine) ?? -1,
|
|
16434
|
+
variables: currentStepData == null ? void 0 : currentStepData.variables
|
|
16435
|
+
}
|
|
16436
|
+
),
|
|
16437
|
+
/* @__PURE__ */ jsx(HelpPanel, {})
|
|
16438
|
+
] })
|
|
16439
|
+
] }) }),
|
|
16440
|
+
showControls && /* @__PURE__ */ jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
|
|
16441
|
+
/* @__PURE__ */ jsx(
|
|
16442
|
+
ControlPanel,
|
|
16443
|
+
{
|
|
16444
|
+
isPlaying,
|
|
16445
|
+
currentStep,
|
|
16446
|
+
totalSteps: steps.length,
|
|
16447
|
+
speed,
|
|
16448
|
+
onPlayPause: handlePlayPause,
|
|
16449
|
+
onStep: handleStep,
|
|
16450
|
+
onStepBack: handleStepBack,
|
|
16451
|
+
onReset: handleReset,
|
|
16452
|
+
onSpeedChange: setSpeed,
|
|
16453
|
+
accentColor: "violet"
|
|
16454
|
+
}
|
|
16455
|
+
),
|
|
16456
|
+
/* @__PURE__ */ jsx(Legend, { items: LEGEND_ITEMS$4 })
|
|
16457
|
+
] })
|
|
16458
|
+
]
|
|
16459
|
+
}
|
|
16460
|
+
);
|
|
16461
|
+
};
|
|
16462
|
+
const UnionFindVisualizer = React.memo(UnionFindVisualizerComponent);
|
|
15550
16463
|
const VIRTUAL_NODES_PER_SERVER = 3;
|
|
15551
16464
|
const OPERATIONS = [
|
|
15552
16465
|
{ op: "addServer", server: "Server-A" },
|
|
@@ -17711,6 +18624,8 @@ export {
|
|
|
17711
18624
|
StepHistory,
|
|
17712
18625
|
TreeSetInterviewVisualizer,
|
|
17713
18626
|
TreeSetVisualizer,
|
|
18627
|
+
TrieVisualizer,
|
|
18628
|
+
UnionFindVisualizer,
|
|
17714
18629
|
VisualizationArea,
|
|
17715
18630
|
useInterviewMode,
|
|
17716
18631
|
useUrlState,
|