@tomaszjarosz/react-visualizers 0.2.13 → 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 +288 -45
- package/dist/index.cjs +1272 -204
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1272 -204
- package/dist/index.js.map +1 -1
- package/dist/react-visualizers.css +53 -0
- package/package.json +6 -1
package/dist/index.cjs
CHANGED
|
@@ -503,17 +503,20 @@ const ControlPanel = ({
|
|
|
503
503
|
extraControls
|
|
504
504
|
}) => {
|
|
505
505
|
const colors = ACCENT_COLORS$4[accentColor];
|
|
506
|
-
|
|
507
|
-
|
|
506
|
+
const speedLabelId = React.useId();
|
|
507
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap gap-3", role: "toolbar", "aria-label": "Playback controls", children: [
|
|
508
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", role: "group", "aria-label": "Navigation", children: [
|
|
508
509
|
isPlaying && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
509
510
|
"span",
|
|
510
511
|
{
|
|
511
512
|
className: `flex items-center gap-1 text-xs ${colors.playing} font-medium`,
|
|
513
|
+
"aria-live": "polite",
|
|
512
514
|
children: [
|
|
513
515
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
514
516
|
"span",
|
|
515
517
|
{
|
|
516
|
-
className: `w-2 h-2 ${colors.playingDot} rounded-full animate-pulse
|
|
518
|
+
className: `w-2 h-2 ${colors.playingDot} rounded-full animate-pulse`,
|
|
519
|
+
"aria-hidden": "true"
|
|
517
520
|
}
|
|
518
521
|
),
|
|
519
522
|
"Playing"
|
|
@@ -526,7 +529,9 @@ const ControlPanel = ({
|
|
|
526
529
|
onClick: onPlayPause,
|
|
527
530
|
className: `p-2 text-white rounded-lg transition-colors ${isPlaying ? colors.buttonActive : colors.button}`,
|
|
528
531
|
title: "Play/Pause (P)",
|
|
529
|
-
|
|
532
|
+
"aria-label": isPlaying ? "Pause" : "Play",
|
|
533
|
+
"aria-pressed": isPlaying,
|
|
534
|
+
children: isPlaying ? /* @__PURE__ */ jsxRuntime.jsx(Pause, { className: "w-4 h-4", "aria-hidden": "true" }) : /* @__PURE__ */ jsxRuntime.jsx(Play, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
530
535
|
}
|
|
531
536
|
),
|
|
532
537
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -536,7 +541,8 @@ const ControlPanel = ({
|
|
|
536
541
|
disabled: isPlaying || currentStep <= 0,
|
|
537
542
|
className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50",
|
|
538
543
|
title: "Step Back ([)",
|
|
539
|
-
|
|
544
|
+
"aria-label": "Step back",
|
|
545
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(SkipBack, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
540
546
|
}
|
|
541
547
|
),
|
|
542
548
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -546,7 +552,8 @@ const ControlPanel = ({
|
|
|
546
552
|
disabled: isPlaying || currentStep >= totalSteps - 1,
|
|
547
553
|
className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors disabled:opacity-50",
|
|
548
554
|
title: "Step Forward (])",
|
|
549
|
-
|
|
555
|
+
"aria-label": "Step forward",
|
|
556
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(SkipForward, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
550
557
|
}
|
|
551
558
|
),
|
|
552
559
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -555,7 +562,8 @@ const ControlPanel = ({
|
|
|
555
562
|
onClick: onReset,
|
|
556
563
|
className: "p-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors",
|
|
557
564
|
title: "Reset (R)",
|
|
558
|
-
|
|
565
|
+
"aria-label": "Reset",
|
|
566
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(RotateCcw, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
559
567
|
}
|
|
560
568
|
),
|
|
561
569
|
showShuffle && onShuffle && /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -565,13 +573,14 @@ const ControlPanel = ({
|
|
|
565
573
|
disabled: isPlaying,
|
|
566
574
|
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"}`,
|
|
567
575
|
title: shuffleLabel || "Shuffle",
|
|
568
|
-
|
|
576
|
+
"aria-label": shuffleLabel || "Shuffle",
|
|
577
|
+
children: shuffleLabel || /* @__PURE__ */ jsxRuntime.jsx(Shuffle, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
569
578
|
}
|
|
570
579
|
)
|
|
571
580
|
] }),
|
|
572
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
581
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", role: "group", "aria-label": "Speed control", children: [
|
|
573
582
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
|
|
574
|
-
/* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-xs text-gray-500", children: "Speed" }),
|
|
583
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { id: speedLabelId, className: "text-xs text-gray-500", children: "Speed" }),
|
|
575
584
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
576
585
|
"input",
|
|
577
586
|
{
|
|
@@ -580,7 +589,11 @@ const ControlPanel = ({
|
|
|
580
589
|
max: "100",
|
|
581
590
|
value: speed,
|
|
582
591
|
onChange: (e) => onSpeedChange(Number(e.target.value)),
|
|
583
|
-
className: "w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer"
|
|
592
|
+
className: "w-24 h-1 bg-gray-300 rounded-lg appearance-none cursor-pointer",
|
|
593
|
+
"aria-labelledby": speedLabelId,
|
|
594
|
+
"aria-valuemin": 1,
|
|
595
|
+
"aria-valuemax": 100,
|
|
596
|
+
"aria-valuenow": speed
|
|
584
597
|
}
|
|
585
598
|
)
|
|
586
599
|
] }),
|
|
@@ -1448,11 +1461,11 @@ const InterviewModePanel = ({
|
|
|
1448
1461
|
}) => {
|
|
1449
1462
|
const colors = ACCENT_COLORS[accentColor];
|
|
1450
1463
|
if (isComplete) {
|
|
1451
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-6 bg-gradient-to-br from-gray-50 to-white rounded-xl border border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
1452
|
-
/* @__PURE__ */ jsxRuntime.jsx(Trophy, { className: "w-16 h-16 mx-auto mb-4 text-yellow-500" }),
|
|
1464
|
+
return /* @__PURE__ */ jsxRuntime.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__ */ jsxRuntime.jsxs("div", { className: "text-center", children: [
|
|
1465
|
+
/* @__PURE__ */ jsxRuntime.jsx(Trophy, { className: "w-16 h-16 mx-auto mb-4 text-yellow-500", "aria-hidden": "true" }),
|
|
1453
1466
|
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-2xl font-bold text-gray-900 mb-2", children: "Interview Complete!" }),
|
|
1454
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", children: [
|
|
1455
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-5xl font-bold text-gray-900 mb-1", children: [
|
|
1467
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-6", "aria-live": "polite", children: [
|
|
1468
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-5xl font-bold text-gray-900 mb-1", "aria-label": `Score: ${score.percentage} percent`, children: [
|
|
1456
1469
|
score.percentage,
|
|
1457
1470
|
"%"
|
|
1458
1471
|
] }),
|
|
@@ -1468,7 +1481,7 @@ const InterviewModePanel = ({
|
|
|
1468
1481
|
${score.percentage >= 80 ? "bg-green-100 text-green-700" : ""}
|
|
1469
1482
|
${score.percentage >= 60 && score.percentage < 80 ? "bg-yellow-100 text-yellow-700" : ""}
|
|
1470
1483
|
${score.percentage < 60 ? "bg-red-100 text-red-700" : ""}
|
|
1471
|
-
`, children: [
|
|
1484
|
+
`, role: "status", children: [
|
|
1472
1485
|
score.percentage >= 80 && "🎉 Excellent! Ready for interviews!",
|
|
1473
1486
|
score.percentage >= 60 && score.percentage < 80 && "👍 Good job! Keep practicing!",
|
|
1474
1487
|
score.percentage < 60 && "📚 Review the concepts and try again!"
|
|
@@ -1477,13 +1490,14 @@ const InterviewModePanel = ({
|
|
|
1477
1490
|
"button",
|
|
1478
1491
|
{
|
|
1479
1492
|
onClick: onRestart,
|
|
1493
|
+
"aria-label": "Restart quiz",
|
|
1480
1494
|
className: `
|
|
1481
1495
|
flex items-center gap-2 mx-auto px-6 py-3 rounded-lg
|
|
1482
1496
|
text-white font-medium transition-colors
|
|
1483
1497
|
${colors.button}
|
|
1484
1498
|
`,
|
|
1485
1499
|
children: [
|
|
1486
|
-
/* @__PURE__ */ jsxRuntime.jsx(RotateCcw, { className: "w-5 h-5" }),
|
|
1500
|
+
/* @__PURE__ */ jsxRuntime.jsx(RotateCcw, { className: "w-5 h-5", "aria-hidden": "true" }),
|
|
1487
1501
|
"Try Again"
|
|
1488
1502
|
]
|
|
1489
1503
|
}
|
|
@@ -1523,7 +1537,7 @@ const InterviewModePanel = ({
|
|
|
1523
1537
|
] }),
|
|
1524
1538
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-4", children: [
|
|
1525
1539
|
/* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-lg font-semibold text-gray-900 mb-4", children: currentQuestion.question }),
|
|
1526
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-4", children: currentQuestion.options.map((option, index) => {
|
|
1540
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-2 mb-4", role: "radiogroup", "aria-label": "Answer options", children: currentQuestion.options.map((option, index) => {
|
|
1527
1541
|
const isSelected = selectedAnswer === index;
|
|
1528
1542
|
const isCorrect = index === currentQuestion.correctAnswer;
|
|
1529
1543
|
const showResult = showExplanation;
|
|
@@ -1542,6 +1556,9 @@ const InterviewModePanel = ({
|
|
|
1542
1556
|
{
|
|
1543
1557
|
onClick: () => !isAnswered && onSelectAnswer(index),
|
|
1544
1558
|
disabled: isAnswered,
|
|
1559
|
+
role: "radio",
|
|
1560
|
+
"aria-checked": isSelected,
|
|
1561
|
+
"aria-disabled": isAnswered,
|
|
1545
1562
|
className: `
|
|
1546
1563
|
w-full p-3 rounded-lg border-2 text-left transition-all
|
|
1547
1564
|
flex items-center gap-3
|
|
@@ -1549,24 +1566,31 @@ const InterviewModePanel = ({
|
|
|
1549
1566
|
${isAnswered ? "cursor-default" : "cursor-pointer"}
|
|
1550
1567
|
`,
|
|
1551
1568
|
children: [
|
|
1552
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1569
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1570
|
+
"span",
|
|
1571
|
+
{
|
|
1572
|
+
className: `
|
|
1573
|
+
w-6 h-6 rounded-full flex items-center justify-center text-xs font-bold
|
|
1574
|
+
${showResult && isCorrect ? "bg-green-500 text-white" : ""}
|
|
1575
|
+
${showResult && isSelected && !isCorrect ? "bg-red-500 text-white" : ""}
|
|
1576
|
+
${!showResult ? "bg-gray-200 text-gray-600" : ""}
|
|
1577
|
+
`,
|
|
1578
|
+
"aria-hidden": "true",
|
|
1579
|
+
children: [
|
|
1580
|
+
showResult && isCorrect && /* @__PURE__ */ jsxRuntime.jsx(CircleCheckBig, { className: "w-4 h-4" }),
|
|
1581
|
+
showResult && isSelected && !isCorrect && /* @__PURE__ */ jsxRuntime.jsx(CircleX, { className: "w-4 h-4" }),
|
|
1582
|
+
!showResult && String.fromCharCode(65 + index)
|
|
1583
|
+
]
|
|
1584
|
+
}
|
|
1585
|
+
),
|
|
1562
1586
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: `flex-1 ${showResult && isCorrect ? "font-medium text-green-700" : ""}`, children: option })
|
|
1563
1587
|
]
|
|
1564
1588
|
},
|
|
1565
1589
|
index
|
|
1566
1590
|
);
|
|
1567
1591
|
}) }),
|
|
1568
|
-
currentQuestion.hint && !showExplanation && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: showHint ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-yellow-50 rounded-lg border border-yellow-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1569
|
-
/* @__PURE__ */ jsxRuntime.jsx(Lightbulb, { className: "w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5" }),
|
|
1592
|
+
currentQuestion.hint && !showExplanation && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4", children: showHint ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-3 bg-yellow-50 rounded-lg border border-yellow-200", role: "note", "aria-label": "Hint", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-2", children: [
|
|
1593
|
+
/* @__PURE__ */ jsxRuntime.jsx(Lightbulb, { className: "w-5 h-5 text-yellow-600 flex-shrink-0 mt-0.5", "aria-hidden": "true" }),
|
|
1570
1594
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1571
1595
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-yellow-800 mb-1", children: "Hint" }),
|
|
1572
1596
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-yellow-700", children: currentQuestion.hint })
|
|
@@ -1576,8 +1600,9 @@ const InterviewModePanel = ({
|
|
|
1576
1600
|
{
|
|
1577
1601
|
onClick: onUseHint,
|
|
1578
1602
|
className: "flex items-center gap-2 text-sm text-yellow-600 hover:text-yellow-700",
|
|
1603
|
+
"aria-label": "Show hint for this question",
|
|
1579
1604
|
children: [
|
|
1580
|
-
/* @__PURE__ */ jsxRuntime.jsx(Lightbulb, { className: "w-4 h-4" }),
|
|
1605
|
+
/* @__PURE__ */ jsxRuntime.jsx(Lightbulb, { className: "w-4 h-4", "aria-hidden": "true" }),
|
|
1581
1606
|
"Show hint"
|
|
1582
1607
|
]
|
|
1583
1608
|
}
|
|
@@ -1593,19 +1618,20 @@ const InterviewModePanel = ({
|
|
|
1593
1618
|
] })
|
|
1594
1619
|
] }) })
|
|
1595
1620
|
] }),
|
|
1596
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
1621
|
+
/* @__PURE__ */ jsxRuntime.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: [
|
|
1597
1622
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1598
1623
|
"button",
|
|
1599
1624
|
{
|
|
1600
1625
|
onClick: onPreviousQuestion,
|
|
1601
1626
|
disabled: currentQuestionIndex === 0,
|
|
1627
|
+
"aria-label": "Previous question",
|
|
1602
1628
|
className: `
|
|
1603
1629
|
flex items-center gap-1 px-3 py-2 rounded-lg text-sm font-medium
|
|
1604
1630
|
transition-colors
|
|
1605
1631
|
${currentQuestionIndex === 0 ? "text-gray-300 cursor-not-allowed" : "text-gray-600 hover:bg-gray-200"}
|
|
1606
1632
|
`,
|
|
1607
1633
|
children: [
|
|
1608
|
-
/* @__PURE__ */ jsxRuntime.jsx(ChevronLeft, { className: "w-4 h-4" }),
|
|
1634
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronLeft, { className: "w-4 h-4", "aria-hidden": "true" }),
|
|
1609
1635
|
"Previous"
|
|
1610
1636
|
]
|
|
1611
1637
|
}
|
|
@@ -1615,6 +1641,7 @@ const InterviewModePanel = ({
|
|
|
1615
1641
|
{
|
|
1616
1642
|
onClick: onNextQuestion,
|
|
1617
1643
|
disabled: currentQuestionIndex === totalQuestions - 1,
|
|
1644
|
+
"aria-label": currentQuestionIndex === totalQuestions - 1 ? "Finish quiz" : "Next question",
|
|
1618
1645
|
className: `
|
|
1619
1646
|
flex items-center gap-1 px-4 py-2 rounded-lg text-sm font-medium
|
|
1620
1647
|
transition-colors text-white
|
|
@@ -1622,7 +1649,7 @@ const InterviewModePanel = ({
|
|
|
1622
1649
|
`,
|
|
1623
1650
|
children: [
|
|
1624
1651
|
currentQuestionIndex === totalQuestions - 1 ? "Finish" : "Next",
|
|
1625
|
-
/* @__PURE__ */ jsxRuntime.jsx(ChevronRight, { className: "w-4 h-4" })
|
|
1652
|
+
/* @__PURE__ */ jsxRuntime.jsx(ChevronRight, { className: "w-4 h-4", "aria-hidden": "true" })
|
|
1626
1653
|
]
|
|
1627
1654
|
}
|
|
1628
1655
|
)
|
|
@@ -1637,11 +1664,46 @@ const ALGORITHM_NAMES$1 = {
|
|
|
1637
1664
|
merge: "MergeSort"
|
|
1638
1665
|
};
|
|
1639
1666
|
const ALGORITHM_COMPLEXITIES$1 = {
|
|
1640
|
-
bubble: {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1667
|
+
bubble: {
|
|
1668
|
+
time: "O(n²)",
|
|
1669
|
+
best: "O(n)",
|
|
1670
|
+
average: "O(n²)",
|
|
1671
|
+
worst: "O(n²)",
|
|
1672
|
+
space: "O(1)",
|
|
1673
|
+
stable: true
|
|
1674
|
+
},
|
|
1675
|
+
selection: {
|
|
1676
|
+
time: "O(n²)",
|
|
1677
|
+
best: "O(n²)",
|
|
1678
|
+
average: "O(n²)",
|
|
1679
|
+
worst: "O(n²)",
|
|
1680
|
+
space: "O(1)",
|
|
1681
|
+
stable: false
|
|
1682
|
+
},
|
|
1683
|
+
insertion: {
|
|
1684
|
+
time: "O(n²)",
|
|
1685
|
+
best: "O(n)",
|
|
1686
|
+
average: "O(n²)",
|
|
1687
|
+
worst: "O(n²)",
|
|
1688
|
+
space: "O(1)",
|
|
1689
|
+
stable: true
|
|
1690
|
+
},
|
|
1691
|
+
quick: {
|
|
1692
|
+
time: "O(n log n)",
|
|
1693
|
+
best: "O(n log n)",
|
|
1694
|
+
average: "O(n log n)",
|
|
1695
|
+
worst: "O(n²)",
|
|
1696
|
+
space: "O(log n)",
|
|
1697
|
+
stable: false
|
|
1698
|
+
},
|
|
1699
|
+
merge: {
|
|
1700
|
+
time: "O(n log n)",
|
|
1701
|
+
best: "O(n log n)",
|
|
1702
|
+
average: "O(n log n)",
|
|
1703
|
+
worst: "O(n log n)",
|
|
1704
|
+
space: "O(n)",
|
|
1705
|
+
stable: true
|
|
1706
|
+
}
|
|
1645
1707
|
};
|
|
1646
1708
|
const ALGORITHM_CODE$1 = {
|
|
1647
1709
|
bubble: [
|
|
@@ -1696,7 +1758,7 @@ const BINARY_SEARCH_CODE = [
|
|
|
1696
1758
|
" right = mid - 1",
|
|
1697
1759
|
" return -1 // Not found"
|
|
1698
1760
|
];
|
|
1699
|
-
const LEGEND_ITEMS$
|
|
1761
|
+
const LEGEND_ITEMS$A = [
|
|
1700
1762
|
{ color: "bg-blue-100", label: "Search Space" },
|
|
1701
1763
|
{ color: "bg-gray-200", label: "Eliminated" },
|
|
1702
1764
|
{ color: "bg-purple-500", label: "Mid" },
|
|
@@ -2096,7 +2158,7 @@ const BinarySearchVisualizerComponent = ({
|
|
|
2096
2158
|
accentColor: "green"
|
|
2097
2159
|
}
|
|
2098
2160
|
),
|
|
2099
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
2161
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$A })
|
|
2100
2162
|
] })
|
|
2101
2163
|
]
|
|
2102
2164
|
}
|
|
@@ -2161,7 +2223,7 @@ const ALGORITHM_CODE = {
|
|
|
2161
2223
|
" else arr[k++] = right[j++]"
|
|
2162
2224
|
]
|
|
2163
2225
|
};
|
|
2164
|
-
const LEGEND_ITEMS$
|
|
2226
|
+
const LEGEND_ITEMS$z = [
|
|
2165
2227
|
{ color: "bg-blue-500", label: "Default" },
|
|
2166
2228
|
{ color: "bg-yellow-400", label: "Comparing" },
|
|
2167
2229
|
{ color: "bg-red-500", label: "Swapping" },
|
|
@@ -2642,6 +2704,7 @@ const SortingVisualizerComponent = ({
|
|
|
2642
2704
|
const [customArray, setCustomArray] = React.useState(null);
|
|
2643
2705
|
const [historyCollapsed, setHistoryCollapsed] = React.useState(true);
|
|
2644
2706
|
const [urlStateLoaded, setUrlStateLoaded] = React.useState(false);
|
|
2707
|
+
const [showComplexityTable, setShowComplexityTable] = React.useState(false);
|
|
2645
2708
|
const playingRef = React.useRef(false);
|
|
2646
2709
|
const timeoutRef = React.useRef(null);
|
|
2647
2710
|
const VISUALIZER_ID = "sorting-visualizer";
|
|
@@ -2946,6 +3009,63 @@ const SortingVisualizerComponent = ({
|
|
|
2946
3009
|
/* @__PURE__ */ jsxRuntime.jsx(HelpPanel, {})
|
|
2947
3010
|
] })
|
|
2948
3011
|
] }) }),
|
|
3012
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 pb-2", children: [
|
|
3013
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
3014
|
+
"button",
|
|
3015
|
+
{
|
|
3016
|
+
onClick: () => setShowComplexityTable(!showComplexityTable),
|
|
3017
|
+
className: "flex items-center gap-2 text-sm text-indigo-600 hover:text-indigo-800 font-medium transition-colors",
|
|
3018
|
+
children: [
|
|
3019
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3020
|
+
"svg",
|
|
3021
|
+
{
|
|
3022
|
+
className: `w-4 h-4 transition-transform ${showComplexityTable ? "rotate-90" : ""}`,
|
|
3023
|
+
fill: "none",
|
|
3024
|
+
viewBox: "0 0 24 24",
|
|
3025
|
+
stroke: "currentColor",
|
|
3026
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 5l7 7-7 7" })
|
|
3027
|
+
}
|
|
3028
|
+
),
|
|
3029
|
+
"Algorithm Complexity Comparison"
|
|
3030
|
+
]
|
|
3031
|
+
}
|
|
3032
|
+
),
|
|
3033
|
+
showComplexityTable && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-3 p-4 bg-gradient-to-r from-indigo-50 to-blue-50 rounded-xl border border-indigo-200", children: [
|
|
3034
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: "w-full text-sm", children: [
|
|
3035
|
+
/* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: "text-left text-indigo-900", children: [
|
|
3036
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pr-4 font-semibold", children: "Algorithm" }),
|
|
3037
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Best" }),
|
|
3038
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Average" }),
|
|
3039
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Worst" }),
|
|
3040
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 px-3 font-semibold text-center", children: "Space" }),
|
|
3041
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { className: "pb-2 pl-3 font-semibold text-center", children: "Stable" })
|
|
3042
|
+
] }) }),
|
|
3043
|
+
/* @__PURE__ */ jsxRuntime.jsx("tbody", { className: "text-gray-700", children: Object.keys(ALGORITHM_NAMES$1).map((alg) => {
|
|
3044
|
+
const comp = ALGORITHM_COMPLEXITIES$1[alg];
|
|
3045
|
+
const isCurrentAlgorithm = alg === algorithm;
|
|
3046
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3047
|
+
"tr",
|
|
3048
|
+
{
|
|
3049
|
+
className: `border-t border-indigo-100 ${isCurrentAlgorithm ? "bg-indigo-100/50 font-medium" : ""}`,
|
|
3050
|
+
children: [
|
|
3051
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 pr-4", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: isCurrentAlgorithm ? "text-indigo-700" : "", children: ALGORITHM_NAMES$1[alg] }) }),
|
|
3052
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.best.includes("log") ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"}`, children: comp.best }) }),
|
|
3053
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.average.includes("log") ? "bg-green-100 text-green-700" : "bg-yellow-100 text-yellow-700"}`, children: comp.average }) }),
|
|
3054
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.worst.includes("log") ? "bg-green-100 text-green-700" : "bg-red-100 text-red-700"}`, children: comp.worst }) }),
|
|
3055
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 px-3 text-center", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: `px-2 py-0.5 rounded text-xs ${comp.space === "O(1)" ? "bg-green-100 text-green-700" : "bg-purple-100 text-purple-700"}`, children: comp.space }) }),
|
|
3056
|
+
/* @__PURE__ */ jsxRuntime.jsx("td", { className: "py-2 pl-3 text-center", children: comp.stable ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-green-600", children: "✓" }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-red-400", children: "✗" }) })
|
|
3057
|
+
]
|
|
3058
|
+
},
|
|
3059
|
+
alg
|
|
3060
|
+
);
|
|
3061
|
+
}) })
|
|
3062
|
+
] }) }),
|
|
3063
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "mt-3 text-xs text-indigo-600", children: [
|
|
3064
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Stable:" }),
|
|
3065
|
+
" A sorting algorithm is stable if elements with equal keys maintain their relative order."
|
|
3066
|
+
] })
|
|
3067
|
+
] })
|
|
3068
|
+
] }),
|
|
2949
3069
|
showControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
|
|
2950
3070
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2951
3071
|
ControlPanel,
|
|
@@ -2965,7 +3085,7 @@ const SortingVisualizerComponent = ({
|
|
|
2965
3085
|
extraControls: sizeControl
|
|
2966
3086
|
}
|
|
2967
3087
|
),
|
|
2968
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
3088
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$z })
|
|
2969
3089
|
] })
|
|
2970
3090
|
]
|
|
2971
3091
|
}
|
|
@@ -3511,7 +3631,7 @@ const DIJKSTRA_CODE = [
|
|
|
3511
3631
|
" dist[v] = dist[u]+w",
|
|
3512
3632
|
" pq.add((dist[v], v))"
|
|
3513
3633
|
];
|
|
3514
|
-
const LEGEND_ITEMS$
|
|
3634
|
+
const LEGEND_ITEMS$y = [
|
|
3515
3635
|
{ color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
|
|
3516
3636
|
{ color: "bg-yellow-400", label: "Current", border: "#ca8a04" },
|
|
3517
3637
|
{ color: "bg-green-400", label: "Visited", border: "#16a34a" }
|
|
@@ -3872,7 +3992,7 @@ const DijkstraVisualizerComponent = ({
|
|
|
3872
3992
|
accentColor: "orange"
|
|
3873
3993
|
}
|
|
3874
3994
|
),
|
|
3875
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
3995
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$y })
|
|
3876
3996
|
] })
|
|
3877
3997
|
]
|
|
3878
3998
|
}
|
|
@@ -3896,7 +4016,7 @@ const KNAPSACK_CODE = [
|
|
|
3896
4016
|
" take = dp[i-1][w-weight] + value",
|
|
3897
4017
|
" dp[i][w] = max(skip, take)"
|
|
3898
4018
|
];
|
|
3899
|
-
const LEGEND_ITEMS$
|
|
4019
|
+
const LEGEND_ITEMS$x = [
|
|
3900
4020
|
{ color: "bg-gray-50", label: "Not computed", border: "#d1d5db" },
|
|
3901
4021
|
{ color: "bg-blue-100", label: "Computed" },
|
|
3902
4022
|
{ color: "bg-green-400", label: "Take item" },
|
|
@@ -4192,7 +4312,7 @@ const DPVisualizerComponent = ({
|
|
|
4192
4312
|
accentColor: "teal"
|
|
4193
4313
|
}
|
|
4194
4314
|
),
|
|
4195
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
4315
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$x })
|
|
4196
4316
|
] })
|
|
4197
4317
|
]
|
|
4198
4318
|
}
|
|
@@ -4231,7 +4351,7 @@ const BFS_CODE = [
|
|
|
4231
4351
|
" visited[neighbor] = true",
|
|
4232
4352
|
" queue.enqueue(neighbor)"
|
|
4233
4353
|
];
|
|
4234
|
-
const LEGEND_ITEMS$
|
|
4354
|
+
const LEGEND_ITEMS$w = [
|
|
4235
4355
|
{ color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
|
|
4236
4356
|
{ color: "bg-yellow-400", label: "Current", border: "#ca8a04" },
|
|
4237
4357
|
{ color: "bg-green-400", label: "Visited", border: "#16a34a" }
|
|
@@ -4658,7 +4778,7 @@ const GraphVisualizerComponent = ({
|
|
|
4658
4778
|
accentColor: "purple"
|
|
4659
4779
|
}
|
|
4660
4780
|
),
|
|
4661
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
4781
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$w })
|
|
4662
4782
|
] })
|
|
4663
4783
|
]
|
|
4664
4784
|
}
|
|
@@ -4666,7 +4786,7 @@ const GraphVisualizerComponent = ({
|
|
|
4666
4786
|
};
|
|
4667
4787
|
const GraphVisualizer = React.memo(GraphVisualizerComponent);
|
|
4668
4788
|
const BUCKET_COUNT$2 = 8;
|
|
4669
|
-
const OPERATIONS$
|
|
4789
|
+
const OPERATIONS$j = [
|
|
4670
4790
|
{ op: "put", key: "Alice", value: 25 },
|
|
4671
4791
|
{ op: "put", key: "Bob", value: 30 },
|
|
4672
4792
|
{ op: "put", key: "Charlie", value: 35 },
|
|
@@ -4698,7 +4818,7 @@ const HASHMAP_CODE = [
|
|
|
4698
4818
|
" return entry.value",
|
|
4699
4819
|
" return null"
|
|
4700
4820
|
];
|
|
4701
|
-
const LEGEND_ITEMS$
|
|
4821
|
+
const LEGEND_ITEMS$v = [
|
|
4702
4822
|
{ color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
|
|
4703
4823
|
{ color: "bg-blue-500", label: "Insert/Update" },
|
|
4704
4824
|
{ color: "bg-green-400", label: "Found" },
|
|
@@ -4723,7 +4843,7 @@ function generateHashMapSteps$1() {
|
|
|
4723
4843
|
description: `Initialize HashMap with ${BUCKET_COUNT$2} buckets. Each bucket is a linked list for collision handling.`,
|
|
4724
4844
|
codeLine: -1
|
|
4725
4845
|
});
|
|
4726
|
-
for (const { op, key, value } of OPERATIONS$
|
|
4846
|
+
for (const { op, key, value } of OPERATIONS$j) {
|
|
4727
4847
|
const hash = simpleHash$7(key);
|
|
4728
4848
|
const index = hash % BUCKET_COUNT$2;
|
|
4729
4849
|
if (op === "put") {
|
|
@@ -5010,7 +5130,7 @@ const HashMapVisualizerComponent = ({
|
|
|
5010
5130
|
accentColor: "indigo"
|
|
5011
5131
|
}
|
|
5012
5132
|
),
|
|
5013
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
5133
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$v })
|
|
5014
5134
|
] })
|
|
5015
5135
|
]
|
|
5016
5136
|
}
|
|
@@ -5018,7 +5138,7 @@ const HashMapVisualizerComponent = ({
|
|
|
5018
5138
|
};
|
|
5019
5139
|
const HashMapVisualizer = React.memo(HashMapVisualizerComponent);
|
|
5020
5140
|
const BUCKET_COUNT$1 = 8;
|
|
5021
|
-
const OPERATIONS$
|
|
5141
|
+
const OPERATIONS$i = [
|
|
5022
5142
|
{ op: "put", key: "Alice", value: 25 },
|
|
5023
5143
|
{ op: "put", key: "Bob", value: 30 },
|
|
5024
5144
|
{ op: "put", key: "Charlie", value: 35 },
|
|
@@ -5027,7 +5147,7 @@ const OPERATIONS$g = [
|
|
|
5027
5147
|
{ op: "put", key: "Alice", value: 26 },
|
|
5028
5148
|
{ op: "get", key: "Frank" }
|
|
5029
5149
|
];
|
|
5030
|
-
const LEGEND_ITEMS$
|
|
5150
|
+
const LEGEND_ITEMS$u = [
|
|
5031
5151
|
{ color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
|
|
5032
5152
|
{ color: "bg-blue-500", label: "Insert/Update" },
|
|
5033
5153
|
{ color: "bg-green-400", label: "Found" },
|
|
@@ -5156,7 +5276,7 @@ function generateHashMapSteps() {
|
|
|
5156
5276
|
buckets: buckets.map((b) => ({ entries: [...b.entries] })),
|
|
5157
5277
|
description: `Initialize HashMap with ${BUCKET_COUNT$1} buckets`
|
|
5158
5278
|
});
|
|
5159
|
-
for (const { op, key, value } of OPERATIONS$
|
|
5279
|
+
for (const { op, key, value } of OPERATIONS$i) {
|
|
5160
5280
|
const hash = simpleHash$6(key);
|
|
5161
5281
|
const index = hash % BUCKET_COUNT$1;
|
|
5162
5282
|
if (op === "put") {
|
|
@@ -5397,7 +5517,7 @@ const HashMapInterviewVisualizerComponent = ({
|
|
|
5397
5517
|
accentColor: "indigo"
|
|
5398
5518
|
}
|
|
5399
5519
|
),
|
|
5400
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
5520
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$u })
|
|
5401
5521
|
] })
|
|
5402
5522
|
]
|
|
5403
5523
|
}
|
|
@@ -5405,7 +5525,7 @@ const HashMapInterviewVisualizerComponent = ({
|
|
|
5405
5525
|
};
|
|
5406
5526
|
const HashMapInterviewVisualizer = React.memo(HashMapInterviewVisualizerComponent);
|
|
5407
5527
|
const INITIAL_DATA = [10, 20, 30, 40, 50];
|
|
5408
|
-
const OPERATIONS$
|
|
5528
|
+
const OPERATIONS$h = [
|
|
5409
5529
|
{ op: "get", index: 2 },
|
|
5410
5530
|
{ op: "addLast", value: 60 },
|
|
5411
5531
|
{ op: "addFirst", value: 5 },
|
|
@@ -5414,7 +5534,7 @@ const OPERATIONS$f = [
|
|
|
5414
5534
|
{ op: "removeLast" },
|
|
5415
5535
|
{ op: "removeMiddle", index: 2 }
|
|
5416
5536
|
];
|
|
5417
|
-
const LEGEND_ITEMS$
|
|
5537
|
+
const LEGEND_ITEMS$t = [
|
|
5418
5538
|
{ color: "bg-blue-500", label: "ArrayList" },
|
|
5419
5539
|
{ color: "bg-green-500", label: "LinkedList" },
|
|
5420
5540
|
{ color: "bg-yellow-400", label: "Current element" },
|
|
@@ -5440,7 +5560,7 @@ function generateListComparisonSteps() {
|
|
|
5440
5560
|
description: `Initialize both lists with [${INITIAL_DATA.join(", ")}]`,
|
|
5441
5561
|
phase: "complete"
|
|
5442
5562
|
});
|
|
5443
|
-
for (const { op, index, value } of OPERATIONS$
|
|
5563
|
+
for (const { op, index, value } of OPERATIONS$h) {
|
|
5444
5564
|
switch (op) {
|
|
5445
5565
|
case "get": {
|
|
5446
5566
|
const idx = index;
|
|
@@ -5942,14 +6062,14 @@ const ListComparisonVisualizerComponent = ({
|
|
|
5942
6062
|
accentColor: "blue"
|
|
5943
6063
|
}
|
|
5944
6064
|
),
|
|
5945
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
6065
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$t })
|
|
5946
6066
|
] })
|
|
5947
6067
|
]
|
|
5948
6068
|
}
|
|
5949
6069
|
);
|
|
5950
6070
|
};
|
|
5951
6071
|
const ListComparisonVisualizer = React.memo(ListComparisonVisualizerComponent);
|
|
5952
|
-
const OPERATIONS$
|
|
6072
|
+
const OPERATIONS$g = [
|
|
5953
6073
|
{ op: "add", value: 50 },
|
|
5954
6074
|
{ op: "add", value: 30 },
|
|
5955
6075
|
{ op: "add", value: 70 },
|
|
@@ -5960,7 +6080,7 @@ const OPERATIONS$e = [
|
|
|
5960
6080
|
{ op: "contains", value: 40 },
|
|
5961
6081
|
{ op: "contains", value: 55 }
|
|
5962
6082
|
];
|
|
5963
|
-
const LEGEND_ITEMS$
|
|
6083
|
+
const LEGEND_ITEMS$s = [
|
|
5964
6084
|
{ color: "bg-yellow-100", label: "Path", border: "#facc15" },
|
|
5965
6085
|
{ color: "bg-green-500", label: "Current/Found" },
|
|
5966
6086
|
{ color: "bg-red-500", label: "Not found" }
|
|
@@ -6124,7 +6244,7 @@ function generateTreeSetSteps$1() {
|
|
|
6124
6244
|
path: [],
|
|
6125
6245
|
description: "Initialize empty TreeSet (Binary Search Tree)"
|
|
6126
6246
|
});
|
|
6127
|
-
for (const { op, value } of OPERATIONS$
|
|
6247
|
+
for (const { op, value } of OPERATIONS$g) {
|
|
6128
6248
|
if (op === "add") {
|
|
6129
6249
|
const path = tree ? findPath$1(tree, value) : [];
|
|
6130
6250
|
if (tree) {
|
|
@@ -6423,7 +6543,7 @@ const TreeSetInterviewVisualizerComponent = ({
|
|
|
6423
6543
|
accentColor: "green"
|
|
6424
6544
|
}
|
|
6425
6545
|
),
|
|
6426
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
6546
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$s })
|
|
6427
6547
|
] })
|
|
6428
6548
|
]
|
|
6429
6549
|
}
|
|
@@ -6431,7 +6551,7 @@ const TreeSetInterviewVisualizerComponent = ({
|
|
|
6431
6551
|
};
|
|
6432
6552
|
const TreeSetInterviewVisualizer = React.memo(TreeSetInterviewVisualizerComponent);
|
|
6433
6553
|
const INITIAL_ARRAY = [64, 34, 25, 12, 22, 11, 90];
|
|
6434
|
-
const LEGEND_ITEMS$
|
|
6554
|
+
const LEGEND_ITEMS$r = [
|
|
6435
6555
|
{ color: "bg-yellow-400", label: "Comparing" },
|
|
6436
6556
|
{ color: "bg-red-400", label: "Swapping" },
|
|
6437
6557
|
{ color: "bg-green-400", label: "Sorted" },
|
|
@@ -6754,7 +6874,7 @@ const SortingInterviewVisualizerComponent = ({
|
|
|
6754
6874
|
accentColor: "orange"
|
|
6755
6875
|
}
|
|
6756
6876
|
),
|
|
6757
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
6877
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$r })
|
|
6758
6878
|
] })
|
|
6759
6879
|
]
|
|
6760
6880
|
}
|
|
@@ -6779,7 +6899,7 @@ const EDGES$1 = [
|
|
|
6779
6899
|
{ from: "C", to: "G" },
|
|
6780
6900
|
{ from: "E", to: "F" }
|
|
6781
6901
|
];
|
|
6782
|
-
const LEGEND_ITEMS$
|
|
6902
|
+
const LEGEND_ITEMS$q = [
|
|
6783
6903
|
{ color: "bg-blue-500", label: "Current" },
|
|
6784
6904
|
{ color: "bg-green-400", label: "Visited" },
|
|
6785
6905
|
{ color: "bg-yellow-100", label: "In Queue/Stack", border: "#facc15" }
|
|
@@ -7201,7 +7321,7 @@ const GraphInterviewVisualizerComponent = ({
|
|
|
7201
7321
|
accentColor: "blue"
|
|
7202
7322
|
}
|
|
7203
7323
|
),
|
|
7204
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
7324
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$q })
|
|
7205
7325
|
] })
|
|
7206
7326
|
]
|
|
7207
7327
|
}
|
|
@@ -7210,7 +7330,7 @@ const GraphInterviewVisualizerComponent = ({
|
|
|
7210
7330
|
const GraphInterviewVisualizer = React.memo(GraphInterviewVisualizerComponent);
|
|
7211
7331
|
const BIT_ARRAY_SIZE$1 = 16;
|
|
7212
7332
|
const K_HASH_FUNCTIONS = 3;
|
|
7213
|
-
const OPERATIONS$
|
|
7333
|
+
const OPERATIONS$f = [
|
|
7214
7334
|
{ op: "add", element: "apple" },
|
|
7215
7335
|
{ op: "add", element: "banana" },
|
|
7216
7336
|
{ op: "add", element: "cherry" },
|
|
@@ -7218,7 +7338,7 @@ const OPERATIONS$d = [
|
|
|
7218
7338
|
{ op: "query", element: "grape" },
|
|
7219
7339
|
{ op: "query", element: "banana" }
|
|
7220
7340
|
];
|
|
7221
|
-
const LEGEND_ITEMS$
|
|
7341
|
+
const LEGEND_ITEMS$p = [
|
|
7222
7342
|
{ color: "bg-gray-200", label: "Bit = 0" },
|
|
7223
7343
|
{ color: "bg-purple-500", label: "Bit = 1" },
|
|
7224
7344
|
{ color: "bg-pink-400", label: "Current hash position" }
|
|
@@ -7376,7 +7496,7 @@ function generateBloomFilterSteps$1() {
|
|
|
7376
7496
|
bitArray: [...bitArray],
|
|
7377
7497
|
description: `Initialize Bloom Filter: ${BIT_ARRAY_SIZE$1} bits, ${K_HASH_FUNCTIONS} hash functions`
|
|
7378
7498
|
});
|
|
7379
|
-
for (const { op, element } of OPERATIONS$
|
|
7499
|
+
for (const { op, element } of OPERATIONS$f) {
|
|
7380
7500
|
const positions = getHashPositions(element);
|
|
7381
7501
|
if (op === "add") {
|
|
7382
7502
|
steps.push({
|
|
@@ -7566,7 +7686,7 @@ const BloomFilterInterviewVisualizerComponent = ({
|
|
|
7566
7686
|
accentColor: "purple"
|
|
7567
7687
|
}
|
|
7568
7688
|
),
|
|
7569
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
7689
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$p })
|
|
7570
7690
|
] })
|
|
7571
7691
|
]
|
|
7572
7692
|
}
|
|
@@ -7574,7 +7694,7 @@ const BloomFilterInterviewVisualizerComponent = ({
|
|
|
7574
7694
|
};
|
|
7575
7695
|
const BloomFilterInterviewVisualizer = React.memo(BloomFilterInterviewVisualizerComponent);
|
|
7576
7696
|
const ORDER$1 = 3;
|
|
7577
|
-
const OPERATIONS$
|
|
7697
|
+
const OPERATIONS$e = [
|
|
7578
7698
|
{ op: "insert", value: 10 },
|
|
7579
7699
|
{ op: "insert", value: 20 },
|
|
7580
7700
|
{ op: "insert", value: 5 },
|
|
@@ -7583,7 +7703,7 @@ const OPERATIONS$c = [
|
|
|
7583
7703
|
{ op: "search", value: 15 },
|
|
7584
7704
|
{ op: "search", value: 12 }
|
|
7585
7705
|
];
|
|
7586
|
-
const LEGEND_ITEMS$
|
|
7706
|
+
const LEGEND_ITEMS$o = [
|
|
7587
7707
|
{ color: "bg-green-400", label: "Current node" },
|
|
7588
7708
|
{ color: "bg-yellow-100", label: "Search path", border: "#facc15" },
|
|
7589
7709
|
{ color: "bg-blue-400", label: "Found/Inserted" }
|
|
@@ -7775,7 +7895,7 @@ function generateBTreeSteps$1() {
|
|
|
7775
7895
|
}
|
|
7776
7896
|
return { node };
|
|
7777
7897
|
}
|
|
7778
|
-
for (const { op, value } of OPERATIONS$
|
|
7898
|
+
for (const { op, value } of OPERATIONS$e) {
|
|
7779
7899
|
if (op === "insert") {
|
|
7780
7900
|
steps.push({
|
|
7781
7901
|
operation: "insert",
|
|
@@ -8010,14 +8130,14 @@ const BTreeInterviewVisualizerComponent = ({
|
|
|
8010
8130
|
accentColor: "green"
|
|
8011
8131
|
}
|
|
8012
8132
|
),
|
|
8013
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
8133
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$o })
|
|
8014
8134
|
] })
|
|
8015
8135
|
]
|
|
8016
8136
|
}
|
|
8017
8137
|
);
|
|
8018
8138
|
};
|
|
8019
8139
|
const BTreeInterviewVisualizer = React.memo(BTreeInterviewVisualizerComponent);
|
|
8020
|
-
const LEGEND_ITEMS$
|
|
8140
|
+
const LEGEND_ITEMS$n = [
|
|
8021
8141
|
{ color: "bg-blue-100", label: "Unvisited", border: "#60a5fa" },
|
|
8022
8142
|
{ color: "bg-yellow-400", label: "Current" },
|
|
8023
8143
|
{ color: "bg-green-400", label: "Visited" }
|
|
@@ -8411,14 +8531,14 @@ const DijkstraInterviewVisualizerComponent = ({
|
|
|
8411
8531
|
accentColor: "orange"
|
|
8412
8532
|
}
|
|
8413
8533
|
),
|
|
8414
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
8534
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$n })
|
|
8415
8535
|
] })
|
|
8416
8536
|
]
|
|
8417
8537
|
}
|
|
8418
8538
|
);
|
|
8419
8539
|
};
|
|
8420
8540
|
const DijkstraInterviewVisualizer = React.memo(DijkstraInterviewVisualizerComponent);
|
|
8421
|
-
const LEGEND_ITEMS$
|
|
8541
|
+
const LEGEND_ITEMS$m = [
|
|
8422
8542
|
{ color: "bg-gray-50", label: "Not computed", border: "#d1d5db" },
|
|
8423
8543
|
{ color: "bg-blue-100", label: "Computed" },
|
|
8424
8544
|
{ color: "bg-green-400", label: "Take item" },
|
|
@@ -8805,14 +8925,14 @@ const DPInterviewVisualizerComponent = ({
|
|
|
8805
8925
|
accentColor: "cyan"
|
|
8806
8926
|
}
|
|
8807
8927
|
),
|
|
8808
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
8928
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$m })
|
|
8809
8929
|
] })
|
|
8810
8930
|
]
|
|
8811
8931
|
}
|
|
8812
8932
|
);
|
|
8813
8933
|
};
|
|
8814
8934
|
const DPInterviewVisualizer = React.memo(DPInterviewVisualizerComponent);
|
|
8815
|
-
const LEGEND_ITEMS$
|
|
8935
|
+
const LEGEND_ITEMS$l = [
|
|
8816
8936
|
{ color: "bg-blue-500", label: "Server A" },
|
|
8817
8937
|
{ color: "bg-green-500", label: "Server B" },
|
|
8818
8938
|
{ color: "bg-purple-500", label: "Server C" },
|
|
@@ -9255,14 +9375,14 @@ const ConsistentHashingInterviewVisualizerComponent = ({
|
|
|
9255
9375
|
accentColor: "cyan"
|
|
9256
9376
|
}
|
|
9257
9377
|
),
|
|
9258
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
9378
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$l })
|
|
9259
9379
|
] })
|
|
9260
9380
|
]
|
|
9261
9381
|
}
|
|
9262
9382
|
);
|
|
9263
9383
|
};
|
|
9264
9384
|
const ConsistentHashingInterviewVisualizer = React.memo(ConsistentHashingInterviewVisualizerComponent);
|
|
9265
|
-
const LEGEND_ITEMS$
|
|
9385
|
+
const LEGEND_ITEMS$k = [
|
|
9266
9386
|
{ color: "bg-gray-400", label: "Follower" },
|
|
9267
9387
|
{ color: "bg-yellow-500", label: "Candidate" },
|
|
9268
9388
|
{ color: "bg-green-500", label: "Leader" },
|
|
@@ -9419,7 +9539,7 @@ const STATE_COLORS$1 = {
|
|
|
9419
9539
|
candidate: "bg-yellow-500",
|
|
9420
9540
|
leader: "bg-green-500"
|
|
9421
9541
|
};
|
|
9422
|
-
function cloneNodes$
|
|
9542
|
+
function cloneNodes$3(nodes) {
|
|
9423
9543
|
return nodes.map((n) => ({
|
|
9424
9544
|
...n,
|
|
9425
9545
|
log: n.log.map((e) => ({ ...e }))
|
|
@@ -9437,7 +9557,7 @@ function generateRaftSteps$1() {
|
|
|
9437
9557
|
}));
|
|
9438
9558
|
steps.push({
|
|
9439
9559
|
operation: "init",
|
|
9440
|
-
nodes: cloneNodes$
|
|
9560
|
+
nodes: cloneNodes$3(nodes),
|
|
9441
9561
|
description: `Initialize ${NODE_IDS$1.length} nodes as followers (term 0)`
|
|
9442
9562
|
});
|
|
9443
9563
|
const candidateId = "N1";
|
|
@@ -9447,7 +9567,7 @@ function generateRaftSteps$1() {
|
|
|
9447
9567
|
candidateNode.votedFor = candidateId;
|
|
9448
9568
|
steps.push({
|
|
9449
9569
|
operation: "timeout",
|
|
9450
|
-
nodes: cloneNodes$
|
|
9570
|
+
nodes: cloneNodes$3(nodes),
|
|
9451
9571
|
description: `${candidateId} timeout! Becomes candidate, term=1`,
|
|
9452
9572
|
highlightNode: candidateId
|
|
9453
9573
|
});
|
|
@@ -9459,7 +9579,7 @@ function generateRaftSteps$1() {
|
|
|
9459
9579
|
votes++;
|
|
9460
9580
|
steps.push({
|
|
9461
9581
|
operation: "vote",
|
|
9462
|
-
nodes: cloneNodes$
|
|
9582
|
+
nodes: cloneNodes$3(nodes),
|
|
9463
9583
|
description: `${node.id} votes for ${candidateId} (${votes}/${NODE_IDS$1.length})`,
|
|
9464
9584
|
highlightNode: node.id,
|
|
9465
9585
|
highlightEdge: { from: node.id, to: candidateId, type: "vote" }
|
|
@@ -9468,7 +9588,7 @@ function generateRaftSteps$1() {
|
|
|
9468
9588
|
candidateNode.state = "leader";
|
|
9469
9589
|
steps.push({
|
|
9470
9590
|
operation: "becomeLeader",
|
|
9471
|
-
nodes: cloneNodes$
|
|
9591
|
+
nodes: cloneNodes$3(nodes),
|
|
9472
9592
|
description: `${candidateId} wins election! (${votes} votes)`,
|
|
9473
9593
|
highlightNode: candidateId
|
|
9474
9594
|
});
|
|
@@ -9478,7 +9598,7 @@ function generateRaftSteps$1() {
|
|
|
9478
9598
|
}
|
|
9479
9599
|
steps.push({
|
|
9480
9600
|
operation: "heartbeat",
|
|
9481
|
-
nodes: cloneNodes$
|
|
9601
|
+
nodes: cloneNodes$3(nodes),
|
|
9482
9602
|
description: `Leader ${candidateId} sends heartbeats`,
|
|
9483
9603
|
highlightNode: candidateId,
|
|
9484
9604
|
messageType: "Heartbeat"
|
|
@@ -9488,7 +9608,7 @@ function generateRaftSteps$1() {
|
|
|
9488
9608
|
leader.log.push({ term: 1, command, committed: false });
|
|
9489
9609
|
steps.push({
|
|
9490
9610
|
operation: "replicate",
|
|
9491
|
-
nodes: cloneNodes$
|
|
9611
|
+
nodes: cloneNodes$3(nodes),
|
|
9492
9612
|
description: `Client request: "${command}"`,
|
|
9493
9613
|
highlightNode: leader.id
|
|
9494
9614
|
});
|
|
@@ -9508,7 +9628,7 @@ function generateRaftSteps$1() {
|
|
|
9508
9628
|
}
|
|
9509
9629
|
steps.push({
|
|
9510
9630
|
operation: "commit",
|
|
9511
|
-
nodes: cloneNodes$
|
|
9631
|
+
nodes: cloneNodes$3(nodes),
|
|
9512
9632
|
description: `Entry committed! (${acks} acks)`,
|
|
9513
9633
|
highlightNode: leader.id
|
|
9514
9634
|
});
|
|
@@ -9518,7 +9638,7 @@ function generateRaftSteps$1() {
|
|
|
9518
9638
|
}
|
|
9519
9639
|
steps.push({
|
|
9520
9640
|
operation: "done",
|
|
9521
|
-
nodes: cloneNodes$
|
|
9641
|
+
nodes: cloneNodes$3(nodes),
|
|
9522
9642
|
description: `✓ Consensus achieved! Leader: ${leader.id}`
|
|
9523
9643
|
});
|
|
9524
9644
|
return steps;
|
|
@@ -9722,14 +9842,14 @@ const RaftInterviewVisualizerComponent = ({
|
|
|
9722
9842
|
accentColor: "orange"
|
|
9723
9843
|
}
|
|
9724
9844
|
),
|
|
9725
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
9845
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$k })
|
|
9726
9846
|
] })
|
|
9727
9847
|
]
|
|
9728
9848
|
}
|
|
9729
9849
|
);
|
|
9730
9850
|
};
|
|
9731
9851
|
const RaftInterviewVisualizer = React.memo(RaftInterviewVisualizerComponent);
|
|
9732
|
-
const INITIAL_SIZE = 7;
|
|
9852
|
+
const INITIAL_SIZE$1 = 7;
|
|
9733
9853
|
const LOAD_FACTOR_THRESHOLD = 0.7;
|
|
9734
9854
|
const SAMPLE_KEYS = [
|
|
9735
9855
|
"apple",
|
|
@@ -9750,7 +9870,7 @@ const HASH_TABLE_CODE = [
|
|
|
9750
9870
|
" if loadFactor > threshold:",
|
|
9751
9871
|
" rehash(newSize = size * 2)"
|
|
9752
9872
|
];
|
|
9753
|
-
const LEGEND_ITEMS$
|
|
9873
|
+
const LEGEND_ITEMS$j = [
|
|
9754
9874
|
{ color: "bg-gray-50", label: "Empty", border: "#d1d5db" },
|
|
9755
9875
|
{ color: "bg-yellow-50", label: "Hashing", border: "#facc15" },
|
|
9756
9876
|
{ color: "bg-red-50", label: "Collision", border: "#f87171" },
|
|
@@ -9765,7 +9885,7 @@ function simpleHash$3(key, size) {
|
|
|
9765
9885
|
}
|
|
9766
9886
|
function generateHashSteps(keys) {
|
|
9767
9887
|
const steps = [];
|
|
9768
|
-
let buckets = Array(INITIAL_SIZE).fill(null).map(() => []);
|
|
9888
|
+
let buckets = Array(INITIAL_SIZE$1).fill(null).map(() => []);
|
|
9769
9889
|
let itemCount = 0;
|
|
9770
9890
|
steps.push({
|
|
9771
9891
|
operation: "done",
|
|
@@ -9773,7 +9893,7 @@ function generateHashSteps(keys) {
|
|
|
9773
9893
|
hashValue: 0,
|
|
9774
9894
|
bucketIndex: -1,
|
|
9775
9895
|
buckets: buckets.map((b) => [...b]),
|
|
9776
|
-
description: `Initialize hash table with ${INITIAL_SIZE} buckets (chaining)`,
|
|
9896
|
+
description: `Initialize hash table with ${INITIAL_SIZE$1} buckets (chaining)`,
|
|
9777
9897
|
codeLine: -1
|
|
9778
9898
|
});
|
|
9779
9899
|
for (const key of keys) {
|
|
@@ -10107,14 +10227,14 @@ const HashTableVisualizerComponent = ({
|
|
|
10107
10227
|
] })
|
|
10108
10228
|
}
|
|
10109
10229
|
),
|
|
10110
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
10230
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$j })
|
|
10111
10231
|
] })
|
|
10112
10232
|
]
|
|
10113
10233
|
}
|
|
10114
10234
|
);
|
|
10115
10235
|
};
|
|
10116
10236
|
const HashTableVisualizer = React.memo(HashTableVisualizerComponent);
|
|
10117
|
-
const OPERATIONS$
|
|
10237
|
+
const OPERATIONS$d = [
|
|
10118
10238
|
{ op: "addFirst", value: 10 },
|
|
10119
10239
|
{ op: "addLast", value: 20 },
|
|
10120
10240
|
{ op: "addLast", value: 30 },
|
|
@@ -10146,25 +10266,25 @@ const LINKEDLIST_CODE = [
|
|
|
10146
10266
|
" node = node.next",
|
|
10147
10267
|
" return node.value"
|
|
10148
10268
|
];
|
|
10149
|
-
const LEGEND_ITEMS$
|
|
10269
|
+
const LEGEND_ITEMS$i = [
|
|
10150
10270
|
{ color: "bg-white", label: "Node", border: "#d1d5db" },
|
|
10151
10271
|
{ color: "bg-blue-500", label: "Active" }
|
|
10152
10272
|
];
|
|
10153
|
-
let nodeIdCounter$
|
|
10273
|
+
let nodeIdCounter$2 = 0;
|
|
10154
10274
|
function generateLinkedListSteps() {
|
|
10155
10275
|
var _a, _b, _c;
|
|
10156
10276
|
const steps = [];
|
|
10157
10277
|
let nodes = [];
|
|
10158
|
-
nodeIdCounter$
|
|
10278
|
+
nodeIdCounter$2 = 0;
|
|
10159
10279
|
steps.push({
|
|
10160
10280
|
operation: "init",
|
|
10161
10281
|
nodes: [],
|
|
10162
10282
|
description: "Initialize empty LinkedList (doubly-linked). O(1) for add/remove at ends, O(n) for indexed access.",
|
|
10163
10283
|
codeLine: -1
|
|
10164
10284
|
});
|
|
10165
|
-
for (const { op, value, index } of OPERATIONS$
|
|
10285
|
+
for (const { op, value, index } of OPERATIONS$d) {
|
|
10166
10286
|
if (op === "addFirst" && value !== void 0) {
|
|
10167
|
-
const newNode = { value, id: nodeIdCounter$
|
|
10287
|
+
const newNode = { value, id: nodeIdCounter$2++ };
|
|
10168
10288
|
nodes = [newNode, ...nodes];
|
|
10169
10289
|
steps.push({
|
|
10170
10290
|
operation: "addFirst",
|
|
@@ -10176,7 +10296,7 @@ function generateLinkedListSteps() {
|
|
|
10176
10296
|
highlightNode: newNode.id
|
|
10177
10297
|
});
|
|
10178
10298
|
} else if (op === "addLast" && value !== void 0) {
|
|
10179
|
-
const newNode = { value, id: nodeIdCounter$
|
|
10299
|
+
const newNode = { value, id: nodeIdCounter$2++ };
|
|
10180
10300
|
nodes = [...nodes, newNode];
|
|
10181
10301
|
steps.push({
|
|
10182
10302
|
operation: "addLast",
|
|
@@ -10392,7 +10512,7 @@ const LinkedListVisualizerComponent = ({
|
|
|
10392
10512
|
accentColor: "blue"
|
|
10393
10513
|
}
|
|
10394
10514
|
),
|
|
10395
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
10515
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$i })
|
|
10396
10516
|
] })
|
|
10397
10517
|
]
|
|
10398
10518
|
}
|
|
@@ -10400,7 +10520,7 @@ const LinkedListVisualizerComponent = ({
|
|
|
10400
10520
|
};
|
|
10401
10521
|
const LinkedListVisualizer = React.memo(LinkedListVisualizerComponent);
|
|
10402
10522
|
const BUCKET_COUNT = 6;
|
|
10403
|
-
const OPERATIONS$
|
|
10523
|
+
const OPERATIONS$c = [
|
|
10404
10524
|
{ op: "put", key: "A", value: 10 },
|
|
10405
10525
|
{ op: "put", key: "B", value: 20 },
|
|
10406
10526
|
{ op: "put", key: "C", value: 30 },
|
|
@@ -10436,7 +10556,7 @@ const LINKEDHASHMAP_CODE = [
|
|
|
10436
10556
|
" }",
|
|
10437
10557
|
"}"
|
|
10438
10558
|
];
|
|
10439
|
-
const LEGEND_ITEMS$
|
|
10559
|
+
const LEGEND_ITEMS$h = [
|
|
10440
10560
|
{ color: "bg-blue-50", label: "Current bucket", border: "#60a5fa" },
|
|
10441
10561
|
{ color: "bg-blue-500", label: "Insert/Update" },
|
|
10442
10562
|
{ color: "bg-green-400", label: "Found" },
|
|
@@ -10465,7 +10585,7 @@ function generateLinkedHashMapSteps(accessOrder = true) {
|
|
|
10465
10585
|
codeLine: 0,
|
|
10466
10586
|
accessOrder
|
|
10467
10587
|
});
|
|
10468
|
-
for (const { op, key, value } of OPERATIONS$
|
|
10588
|
+
for (const { op, key, value } of OPERATIONS$c) {
|
|
10469
10589
|
const hash = simpleHash$2(key);
|
|
10470
10590
|
const index = hash % BUCKET_COUNT;
|
|
10471
10591
|
if (op === "put") {
|
|
@@ -10901,7 +11021,7 @@ const LinkedHashMapVisualizerComponent = ({
|
|
|
10901
11021
|
accentColor: "orange"
|
|
10902
11022
|
}
|
|
10903
11023
|
),
|
|
10904
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
11024
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$h })
|
|
10905
11025
|
] })
|
|
10906
11026
|
]
|
|
10907
11027
|
}
|
|
@@ -10909,7 +11029,7 @@ const LinkedHashMapVisualizerComponent = ({
|
|
|
10909
11029
|
};
|
|
10910
11030
|
const LinkedHashMapVisualizer = React.memo(LinkedHashMapVisualizerComponent);
|
|
10911
11031
|
const INITIAL_CAPACITY$1 = 4;
|
|
10912
|
-
const OPERATIONS$
|
|
11032
|
+
const OPERATIONS$b = [
|
|
10913
11033
|
{ op: "add", value: 10 },
|
|
10914
11034
|
{ op: "add", value: 20 },
|
|
10915
11035
|
{ op: "add", value: 30 },
|
|
@@ -10940,7 +11060,7 @@ const ARRAYLIST_CODE = [
|
|
|
10940
11060
|
" shift elements left",
|
|
10941
11061
|
" size--"
|
|
10942
11062
|
];
|
|
10943
|
-
const LEGEND_ITEMS$
|
|
11063
|
+
const LEGEND_ITEMS$g = [
|
|
10944
11064
|
{ color: "bg-white border-gray-300", label: "Used", border: "#d1d5db" },
|
|
10945
11065
|
{ color: "bg-gray-100", label: "Empty" },
|
|
10946
11066
|
{ color: "bg-orange-500", label: "Active" },
|
|
@@ -10959,7 +11079,7 @@ function generateArrayListSteps() {
|
|
|
10959
11079
|
description: `Initialize ArrayList with capacity ${capacity}. Auto-grows when full (amortized O(1) add).`,
|
|
10960
11080
|
codeLine: -1
|
|
10961
11081
|
});
|
|
10962
|
-
for (const { op, value, index } of OPERATIONS$
|
|
11082
|
+
for (const { op, value, index } of OPERATIONS$b) {
|
|
10963
11083
|
if (op === "add" && value !== void 0) {
|
|
10964
11084
|
if (size === capacity) {
|
|
10965
11085
|
const oldCapacity = capacity;
|
|
@@ -11280,7 +11400,7 @@ const ArrayListVisualizerComponent = ({
|
|
|
11280
11400
|
accentColor: "orange"
|
|
11281
11401
|
}
|
|
11282
11402
|
),
|
|
11283
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
11403
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$g })
|
|
11284
11404
|
] })
|
|
11285
11405
|
]
|
|
11286
11406
|
}
|
|
@@ -11288,7 +11408,7 @@ const ArrayListVisualizerComponent = ({
|
|
|
11288
11408
|
};
|
|
11289
11409
|
const ArrayListVisualizer = React.memo(ArrayListVisualizerComponent);
|
|
11290
11410
|
const INITIAL_CAPACITY = 8;
|
|
11291
|
-
const OPERATIONS$
|
|
11411
|
+
const OPERATIONS$a = [
|
|
11292
11412
|
{ op: "addLast", value: 10 },
|
|
11293
11413
|
{ op: "addLast", value: 20 },
|
|
11294
11414
|
{ op: "addFirst", value: 5 },
|
|
@@ -11334,7 +11454,7 @@ const ARRAYDEQUE_CODE = [
|
|
|
11334
11454
|
" }",
|
|
11335
11455
|
"}"
|
|
11336
11456
|
];
|
|
11337
|
-
const LEGEND_ITEMS$
|
|
11457
|
+
const LEGEND_ITEMS$f = [
|
|
11338
11458
|
{ color: "bg-teal-500", label: "Head pointer" },
|
|
11339
11459
|
{ color: "bg-violet-500", label: "Tail pointer" },
|
|
11340
11460
|
{ color: "bg-green-400", label: "Added element" },
|
|
@@ -11358,7 +11478,7 @@ function generateArrayDequeSteps() {
|
|
|
11358
11478
|
codeLine: 0,
|
|
11359
11479
|
variables: { head, tail, capacity, size: 0 }
|
|
11360
11480
|
});
|
|
11361
|
-
for (const { op, value } of OPERATIONS$
|
|
11481
|
+
for (const { op, value } of OPERATIONS$a) {
|
|
11362
11482
|
if (op === "addFirst" && value !== void 0) {
|
|
11363
11483
|
if (getSize() === capacity - 1) {
|
|
11364
11484
|
const oldCapacity = capacity;
|
|
@@ -11740,14 +11860,14 @@ const ArrayDequeVisualizerComponent = ({
|
|
|
11740
11860
|
accentColor: "teal"
|
|
11741
11861
|
}
|
|
11742
11862
|
),
|
|
11743
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
11863
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$f })
|
|
11744
11864
|
] })
|
|
11745
11865
|
]
|
|
11746
11866
|
}
|
|
11747
11867
|
);
|
|
11748
11868
|
};
|
|
11749
11869
|
const ArrayDequeVisualizer = React.memo(ArrayDequeVisualizerComponent);
|
|
11750
|
-
const OPERATIONS$
|
|
11870
|
+
const OPERATIONS$9 = [
|
|
11751
11871
|
{ op: "add", value: 50 },
|
|
11752
11872
|
{ op: "add", value: 30 },
|
|
11753
11873
|
{ op: "add", value: 70 },
|
|
@@ -11778,7 +11898,7 @@ const TREESET_CODE = [
|
|
|
11778
11898
|
" node = node.right",
|
|
11779
11899
|
" else: return // duplicate"
|
|
11780
11900
|
];
|
|
11781
|
-
const LEGEND_ITEMS$
|
|
11901
|
+
const LEGEND_ITEMS$e = [
|
|
11782
11902
|
{ color: "bg-yellow-100", label: "Path", border: "#facc15" },
|
|
11783
11903
|
{ color: "bg-blue-500", label: "Current/Insert" },
|
|
11784
11904
|
{ color: "bg-green-500", label: "Found" },
|
|
@@ -11827,7 +11947,7 @@ function generateTreeSetSteps() {
|
|
|
11827
11947
|
description: "Initialize empty TreeSet (Binary Search Tree). Values are stored in sorted order.",
|
|
11828
11948
|
codeLine: -1
|
|
11829
11949
|
});
|
|
11830
|
-
for (const { op, value } of OPERATIONS$
|
|
11950
|
+
for (const { op, value } of OPERATIONS$9) {
|
|
11831
11951
|
if (op === "add") {
|
|
11832
11952
|
const path = tree ? findPath(tree, value) : [];
|
|
11833
11953
|
if (tree) {
|
|
@@ -12207,7 +12327,7 @@ const TreeSetVisualizerComponent = ({
|
|
|
12207
12327
|
accentColor: "green"
|
|
12208
12328
|
}
|
|
12209
12329
|
),
|
|
12210
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
12330
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$e })
|
|
12211
12331
|
] })
|
|
12212
12332
|
]
|
|
12213
12333
|
}
|
|
@@ -12223,7 +12343,7 @@ const DAYS_OF_WEEK = [
|
|
|
12223
12343
|
"SATURDAY",
|
|
12224
12344
|
"SUNDAY"
|
|
12225
12345
|
];
|
|
12226
|
-
const OPERATIONS$
|
|
12346
|
+
const OPERATIONS$8 = [
|
|
12227
12347
|
{ op: "add", value: "MONDAY" },
|
|
12228
12348
|
{ op: "add", value: "WEDNESDAY" },
|
|
12229
12349
|
{ op: "add", value: "FRIDAY" },
|
|
@@ -12256,7 +12376,7 @@ const ENUMSET_CODE = [
|
|
|
12256
12376
|
" }",
|
|
12257
12377
|
"}"
|
|
12258
12378
|
];
|
|
12259
|
-
const LEGEND_ITEMS$
|
|
12379
|
+
const LEGEND_ITEMS$d = [
|
|
12260
12380
|
{ color: "bg-green-500", label: "Bit set (1)" },
|
|
12261
12381
|
{ color: "bg-gray-200", label: "Bit clear (0)" },
|
|
12262
12382
|
{ color: "bg-blue-500", label: "Current operation" },
|
|
@@ -12275,7 +12395,7 @@ function generateEnumSetSteps() {
|
|
|
12275
12395
|
codeLine: 1,
|
|
12276
12396
|
variables: { elements: "0b0000000" }
|
|
12277
12397
|
});
|
|
12278
|
-
for (const { op, value } of OPERATIONS$
|
|
12398
|
+
for (const { op, value } of OPERATIONS$8) {
|
|
12279
12399
|
const bitPos = getBitPosition(value);
|
|
12280
12400
|
const bitMaskForValue = 1 << bitPos;
|
|
12281
12401
|
const previousBitmask = bitmask;
|
|
@@ -12576,14 +12696,14 @@ const EnumSetVisualizerComponent = ({
|
|
|
12576
12696
|
accentColor: "lime"
|
|
12577
12697
|
}
|
|
12578
12698
|
),
|
|
12579
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
12699
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$d })
|
|
12580
12700
|
] })
|
|
12581
12701
|
]
|
|
12582
12702
|
}
|
|
12583
12703
|
);
|
|
12584
12704
|
};
|
|
12585
12705
|
const EnumSetVisualizer = React.memo(EnumSetVisualizerComponent);
|
|
12586
|
-
const OPERATIONS$
|
|
12706
|
+
const OPERATIONS$7 = [
|
|
12587
12707
|
{ op: "offer", value: 50 },
|
|
12588
12708
|
{ op: "offer", value: 30 },
|
|
12589
12709
|
{ op: "offer", value: 70 },
|
|
@@ -12612,7 +12732,7 @@ const HEAP_CODE = [
|
|
|
12612
12732
|
" siftDown(0)",
|
|
12613
12733
|
" return result"
|
|
12614
12734
|
];
|
|
12615
|
-
const LEGEND_ITEMS$
|
|
12735
|
+
const LEGEND_ITEMS$c = [
|
|
12616
12736
|
{ color: "bg-purple-100", label: "Root (min)", border: "#c4b5fd" },
|
|
12617
12737
|
{ color: "bg-purple-500", label: "Active" },
|
|
12618
12738
|
{ color: "bg-purple-200", label: "Sift Path", border: "#a78bfa" },
|
|
@@ -12628,7 +12748,7 @@ function generateHeapSteps() {
|
|
|
12628
12748
|
description: "Initialize min-heap PriorityQueue. Parent is always smaller than children. O(log n) insert/remove.",
|
|
12629
12749
|
codeLine: -1
|
|
12630
12750
|
});
|
|
12631
|
-
for (const { op, value } of OPERATIONS$
|
|
12751
|
+
for (const { op, value } of OPERATIONS$7) {
|
|
12632
12752
|
if (op === "offer" && value !== void 0) {
|
|
12633
12753
|
heap.push(value);
|
|
12634
12754
|
let idx = heap.length - 1;
|
|
@@ -13134,7 +13254,7 @@ const PriorityQueueVisualizerComponent = ({ showControls = true, showCode = true
|
|
|
13134
13254
|
accentColor: "purple"
|
|
13135
13255
|
}
|
|
13136
13256
|
),
|
|
13137
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
13257
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$c })
|
|
13138
13258
|
] })
|
|
13139
13259
|
]
|
|
13140
13260
|
}
|
|
@@ -13144,7 +13264,7 @@ const PriorityQueueVisualizer = React.memo(
|
|
|
13144
13264
|
PriorityQueueVisualizerComponent
|
|
13145
13265
|
);
|
|
13146
13266
|
const SEGMENT_COUNT = 4;
|
|
13147
|
-
const OPERATIONS$
|
|
13267
|
+
const OPERATIONS$6 = [
|
|
13148
13268
|
{ thread: "T1", op: "put", key: "Alice", value: 100 },
|
|
13149
13269
|
{ thread: "T2", op: "put", key: "Bob", value: 200 },
|
|
13150
13270
|
{ thread: "T1", op: "put", key: "Charlie", value: 150 },
|
|
@@ -13167,7 +13287,7 @@ const CHM_CODE = [
|
|
|
13167
13287
|
" // Volatile reads ensure visibility",
|
|
13168
13288
|
" return bucket.get(key)"
|
|
13169
13289
|
];
|
|
13170
|
-
const LEGEND_ITEMS$
|
|
13290
|
+
const LEGEND_ITEMS$b = [
|
|
13171
13291
|
{ color: "bg-blue-50", label: "Active segment", border: "#60a5fa" },
|
|
13172
13292
|
{ color: "bg-red-50", label: "Locked", border: "#f87171" },
|
|
13173
13293
|
{ color: "bg-gray-50", label: "Unlocked", border: "#d1d5db" }
|
|
@@ -13194,7 +13314,7 @@ function generateConcurrentSteps() {
|
|
|
13194
13314
|
codeLine: -1,
|
|
13195
13315
|
activeThreads: []
|
|
13196
13316
|
});
|
|
13197
|
-
for (const { thread, op, key, value } of OPERATIONS$
|
|
13317
|
+
for (const { thread, op, key, value } of OPERATIONS$6) {
|
|
13198
13318
|
const hash = simpleHash$1(key);
|
|
13199
13319
|
const segmentId = hash % SEGMENT_COUNT;
|
|
13200
13320
|
if (op === "put") {
|
|
@@ -13523,7 +13643,7 @@ const ConcurrentHashMapVisualizerComponent = ({ showControls = true, showCode =
|
|
|
13523
13643
|
accentColor: "red"
|
|
13524
13644
|
}
|
|
13525
13645
|
),
|
|
13526
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
13646
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$b })
|
|
13527
13647
|
] })
|
|
13528
13648
|
]
|
|
13529
13649
|
}
|
|
@@ -13533,7 +13653,7 @@ const ConcurrentHashMapVisualizer = React.memo(
|
|
|
13533
13653
|
ConcurrentHashMapVisualizerComponent
|
|
13534
13654
|
);
|
|
13535
13655
|
const CAPACITY = 3;
|
|
13536
|
-
const OPERATIONS$
|
|
13656
|
+
const OPERATIONS$5 = [
|
|
13537
13657
|
{ thread: "P1", role: "producer", value: "Task-A" },
|
|
13538
13658
|
{ thread: "P2", role: "producer", value: "Task-B" },
|
|
13539
13659
|
{ thread: "C1", role: "consumer" },
|
|
@@ -13566,7 +13686,7 @@ const BQ_CODE = [
|
|
|
13566
13686
|
" notFull.signal()",
|
|
13567
13687
|
" lock.unlock()"
|
|
13568
13688
|
];
|
|
13569
|
-
const LEGEND_ITEMS$
|
|
13689
|
+
const LEGEND_ITEMS$a = [
|
|
13570
13690
|
{ color: "bg-green-100", label: "Producer" },
|
|
13571
13691
|
{ color: "bg-blue-100", label: "Consumer" },
|
|
13572
13692
|
{ color: "bg-red-100", label: "Blocked", border: "#fca5a5" }
|
|
@@ -13588,7 +13708,7 @@ function generateBlockingQueueSteps() {
|
|
|
13588
13708
|
blockedProducers: [],
|
|
13589
13709
|
blockedConsumers: []
|
|
13590
13710
|
});
|
|
13591
|
-
for (const { thread, role, value } of OPERATIONS$
|
|
13711
|
+
for (const { thread, role, value } of OPERATIONS$5) {
|
|
13592
13712
|
if (role === "producer") {
|
|
13593
13713
|
if (queue.length >= CAPACITY) {
|
|
13594
13714
|
blockedProducers.push(thread);
|
|
@@ -13921,7 +14041,7 @@ const BlockingQueueVisualizerComponent = ({ showControls = true, showCode = true
|
|
|
13921
14041
|
accentColor: "cyan"
|
|
13922
14042
|
}
|
|
13923
14043
|
),
|
|
13924
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
14044
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$a })
|
|
13925
14045
|
] })
|
|
13926
14046
|
]
|
|
13927
14047
|
}
|
|
@@ -13930,7 +14050,7 @@ const BlockingQueueVisualizerComponent = ({ showControls = true, showCode = true
|
|
|
13930
14050
|
const BlockingQueueVisualizer = React.memo(
|
|
13931
14051
|
BlockingQueueVisualizerComponent
|
|
13932
14052
|
);
|
|
13933
|
-
const OPERATIONS$
|
|
14053
|
+
const OPERATIONS$4 = [
|
|
13934
14054
|
{ thread: "R1", op: "read" },
|
|
13935
14055
|
{ thread: "R2", op: "read" },
|
|
13936
14056
|
{ thread: "W1", op: "write", value: "D" },
|
|
@@ -13952,7 +14072,7 @@ const COW_CODE = [
|
|
|
13952
14072
|
" array = newArray // Atomic swap",
|
|
13953
14073
|
" lock.unlock()"
|
|
13954
14074
|
];
|
|
13955
|
-
const LEGEND_ITEMS$
|
|
14075
|
+
const LEGEND_ITEMS$9 = [
|
|
13956
14076
|
{ color: "bg-blue-500", label: "Reader" },
|
|
13957
14077
|
{ color: "bg-orange-500", label: "Writer" },
|
|
13958
14078
|
{ color: "bg-green-500", label: "New element" }
|
|
@@ -13970,7 +14090,7 @@ function generateCOWSteps() {
|
|
|
13970
14090
|
codeLine: -1,
|
|
13971
14091
|
activeReaders: []
|
|
13972
14092
|
});
|
|
13973
|
-
for (const { thread, op, value } of OPERATIONS$
|
|
14093
|
+
for (const { thread, op, value } of OPERATIONS$4) {
|
|
13974
14094
|
if (op === "read") {
|
|
13975
14095
|
if (!activeReaders.includes(thread)) {
|
|
13976
14096
|
activeReaders.push(thread);
|
|
@@ -14264,7 +14384,7 @@ const CopyOnWriteVisualizerComponent = ({
|
|
|
14264
14384
|
accentColor: "lime"
|
|
14265
14385
|
}
|
|
14266
14386
|
),
|
|
14267
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
14387
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$9 })
|
|
14268
14388
|
] })
|
|
14269
14389
|
]
|
|
14270
14390
|
}
|
|
@@ -14287,7 +14407,7 @@ const IMMUTABLE_CODE = [
|
|
|
14287
14407
|
"var newList = new ArrayList<>(list)",
|
|
14288
14408
|
'newList.add("D") // Works!'
|
|
14289
14409
|
];
|
|
14290
|
-
const LEGEND_ITEMS$
|
|
14410
|
+
const LEGEND_ITEMS$8 = [
|
|
14291
14411
|
{ color: "bg-violet-100", label: "Immutable", border: "#c4b5fd" },
|
|
14292
14412
|
{ color: "bg-green-100", label: "Mutable copy", border: "#86efac" },
|
|
14293
14413
|
{ color: "bg-red-100", label: "Error", border: "#fca5a5" }
|
|
@@ -14565,7 +14685,7 @@ const ImmutableCollectionsVisualizerComponent = ({ showControls = true, showCode
|
|
|
14565
14685
|
accentColor: "violet"
|
|
14566
14686
|
}
|
|
14567
14687
|
),
|
|
14568
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
14688
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$8 })
|
|
14569
14689
|
] })
|
|
14570
14690
|
]
|
|
14571
14691
|
}
|
|
@@ -14596,7 +14716,7 @@ const BLOOM_FILTER_CODE = [
|
|
|
14596
14716
|
" return False # definitely not",
|
|
14597
14717
|
" return True # probably yes"
|
|
14598
14718
|
];
|
|
14599
|
-
const LEGEND_ITEMS$
|
|
14719
|
+
const LEGEND_ITEMS$7 = [
|
|
14600
14720
|
{ color: "bg-gray-200", label: "Bit = 0" },
|
|
14601
14721
|
{ color: "bg-indigo-500", label: "Bit = 1" },
|
|
14602
14722
|
{ color: "bg-yellow-400", label: "Currently checking", border: "#fbbf24" },
|
|
@@ -14975,7 +15095,7 @@ const BloomFilterVisualizerComponent = ({
|
|
|
14975
15095
|
accentColor: "purple"
|
|
14976
15096
|
}
|
|
14977
15097
|
),
|
|
14978
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
15098
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$7 })
|
|
14979
15099
|
] })
|
|
14980
15100
|
]
|
|
14981
15101
|
}
|
|
@@ -14983,7 +15103,7 @@ const BloomFilterVisualizerComponent = ({
|
|
|
14983
15103
|
};
|
|
14984
15104
|
const BloomFilterVisualizer = React.memo(BloomFilterVisualizerComponent);
|
|
14985
15105
|
const ORDER = 3;
|
|
14986
|
-
const OPERATIONS$
|
|
15106
|
+
const OPERATIONS$3 = [
|
|
14987
15107
|
{ op: "insert", key: 10 },
|
|
14988
15108
|
{ op: "insert", key: 20 },
|
|
14989
15109
|
{ op: "insert", key: 5 },
|
|
@@ -15014,7 +15134,7 @@ const BTREE_CODE = [
|
|
|
15014
15134
|
" split root, create new root",
|
|
15015
15135
|
" insertNonFull(root, key)"
|
|
15016
15136
|
];
|
|
15017
|
-
const LEGEND_ITEMS$
|
|
15137
|
+
const LEGEND_ITEMS$6 = [
|
|
15018
15138
|
{ color: "bg-gray-100", label: "Node", border: "#d1d5db" },
|
|
15019
15139
|
{ color: "bg-yellow-200", label: "Current node", border: "#fbbf24" },
|
|
15020
15140
|
{ color: "bg-blue-500", label: "Comparing key" },
|
|
@@ -15022,11 +15142,11 @@ const LEGEND_ITEMS$4 = [
|
|
|
15022
15142
|
{ color: "bg-red-400", label: "Not found" },
|
|
15023
15143
|
{ color: "bg-purple-400", label: "Splitting" }
|
|
15024
15144
|
];
|
|
15025
|
-
let nodeIdCounter = 0;
|
|
15026
|
-
function generateNodeId() {
|
|
15027
|
-
return `node_${nodeIdCounter++}`;
|
|
15145
|
+
let nodeIdCounter$1 = 0;
|
|
15146
|
+
function generateNodeId$1() {
|
|
15147
|
+
return `node_${nodeIdCounter$1++}`;
|
|
15028
15148
|
}
|
|
15029
|
-
function cloneNodes$
|
|
15149
|
+
function cloneNodes$2(nodes) {
|
|
15030
15150
|
const cloned = /* @__PURE__ */ new Map();
|
|
15031
15151
|
nodes.forEach((node, id) => {
|
|
15032
15152
|
cloned.set(id, {
|
|
@@ -15038,11 +15158,11 @@ function cloneNodes$1(nodes) {
|
|
|
15038
15158
|
return cloned;
|
|
15039
15159
|
}
|
|
15040
15160
|
function generateBTreeSteps() {
|
|
15041
|
-
nodeIdCounter = 0;
|
|
15161
|
+
nodeIdCounter$1 = 0;
|
|
15042
15162
|
const steps = [];
|
|
15043
15163
|
let nodes = /* @__PURE__ */ new Map();
|
|
15044
15164
|
let rootId = "";
|
|
15045
|
-
const initialRoot = generateNodeId();
|
|
15165
|
+
const initialRoot = generateNodeId$1();
|
|
15046
15166
|
rootId = initialRoot;
|
|
15047
15167
|
nodes.set(initialRoot, {
|
|
15048
15168
|
id: initialRoot,
|
|
@@ -15052,18 +15172,18 @@ function generateBTreeSteps() {
|
|
|
15052
15172
|
});
|
|
15053
15173
|
steps.push({
|
|
15054
15174
|
operation: "init",
|
|
15055
|
-
nodes: cloneNodes$
|
|
15175
|
+
nodes: cloneNodes$2(nodes),
|
|
15056
15176
|
rootId,
|
|
15057
15177
|
description: `Initialize empty B-Tree of order ${ORDER} (max ${ORDER - 1} keys per node)`,
|
|
15058
15178
|
codeLine: 0,
|
|
15059
15179
|
variables: { order: ORDER, maxKeys: ORDER - 1 }
|
|
15060
15180
|
});
|
|
15061
|
-
for (const { op, key } of OPERATIONS$
|
|
15181
|
+
for (const { op, key } of OPERATIONS$3) {
|
|
15062
15182
|
if (op === "insert") {
|
|
15063
15183
|
const root = nodes.get(rootId);
|
|
15064
15184
|
steps.push({
|
|
15065
15185
|
operation: "insert",
|
|
15066
|
-
nodes: cloneNodes$
|
|
15186
|
+
nodes: cloneNodes$2(nodes),
|
|
15067
15187
|
rootId,
|
|
15068
15188
|
targetKey: key,
|
|
15069
15189
|
currentNodeId: rootId,
|
|
@@ -15080,7 +15200,7 @@ function generateBTreeSteps() {
|
|
|
15080
15200
|
insertNode2.keys.sort((a, b) => a - b);
|
|
15081
15201
|
steps.push({
|
|
15082
15202
|
operation: "insert",
|
|
15083
|
-
nodes: cloneNodes$
|
|
15203
|
+
nodes: cloneNodes$2(nodes),
|
|
15084
15204
|
rootId,
|
|
15085
15205
|
targetKey: key,
|
|
15086
15206
|
currentNodeId: rootId,
|
|
@@ -15096,7 +15216,7 @@ function generateBTreeSteps() {
|
|
|
15096
15216
|
const rightKeys = insertNode2.keys.slice(medianIndex + 1);
|
|
15097
15217
|
steps.push({
|
|
15098
15218
|
operation: "split",
|
|
15099
|
-
nodes: cloneNodes$
|
|
15219
|
+
nodes: cloneNodes$2(nodes),
|
|
15100
15220
|
rootId,
|
|
15101
15221
|
currentNodeId: rootId,
|
|
15102
15222
|
description: `Node full (${insertNode2.keys.length} keys)! Split: median=${medianKey}, left=[${leftKeys.join(",")}], right=[${rightKeys.join(",")}]`,
|
|
@@ -15109,9 +15229,9 @@ function generateBTreeSteps() {
|
|
|
15109
15229
|
rightKeys
|
|
15110
15230
|
}
|
|
15111
15231
|
});
|
|
15112
|
-
const leftId = generateNodeId();
|
|
15113
|
-
const rightId = generateNodeId();
|
|
15114
|
-
const newRootId = generateNodeId();
|
|
15232
|
+
const leftId = generateNodeId$1();
|
|
15233
|
+
const rightId = generateNodeId$1();
|
|
15234
|
+
const newRootId = generateNodeId$1();
|
|
15115
15235
|
nodes.set(leftId, {
|
|
15116
15236
|
id: leftId,
|
|
15117
15237
|
keys: leftKeys,
|
|
@@ -15134,7 +15254,7 @@ function generateBTreeSteps() {
|
|
|
15134
15254
|
rootId = newRootId;
|
|
15135
15255
|
steps.push({
|
|
15136
15256
|
operation: "split",
|
|
15137
|
-
nodes: cloneNodes$
|
|
15257
|
+
nodes: cloneNodes$2(nodes),
|
|
15138
15258
|
rootId,
|
|
15139
15259
|
description: `Split complete! New root with median ${medianKey}`,
|
|
15140
15260
|
codeLine: 17,
|
|
@@ -15144,7 +15264,7 @@ function generateBTreeSteps() {
|
|
|
15144
15264
|
} else {
|
|
15145
15265
|
steps.push({
|
|
15146
15266
|
operation: "search",
|
|
15147
|
-
nodes: cloneNodes$
|
|
15267
|
+
nodes: cloneNodes$2(nodes),
|
|
15148
15268
|
rootId,
|
|
15149
15269
|
targetKey: key,
|
|
15150
15270
|
currentNodeId: rootId,
|
|
@@ -15157,7 +15277,7 @@ function generateBTreeSteps() {
|
|
|
15157
15277
|
for (let i = 0; i < node.keys.length; i++) {
|
|
15158
15278
|
steps.push({
|
|
15159
15279
|
operation: "compare",
|
|
15160
|
-
nodes: cloneNodes$
|
|
15280
|
+
nodes: cloneNodes$2(nodes),
|
|
15161
15281
|
rootId,
|
|
15162
15282
|
targetKey: key,
|
|
15163
15283
|
currentNodeId: nodeId,
|
|
@@ -15169,7 +15289,7 @@ function generateBTreeSteps() {
|
|
|
15169
15289
|
if (key === node.keys[i]) {
|
|
15170
15290
|
steps.push({
|
|
15171
15291
|
operation: "found",
|
|
15172
|
-
nodes: cloneNodes$
|
|
15292
|
+
nodes: cloneNodes$2(nodes),
|
|
15173
15293
|
rootId,
|
|
15174
15294
|
targetKey: key,
|
|
15175
15295
|
currentNodeId: nodeId,
|
|
@@ -15184,7 +15304,7 @@ function generateBTreeSteps() {
|
|
|
15184
15304
|
if (node.isLeaf) {
|
|
15185
15305
|
steps.push({
|
|
15186
15306
|
operation: "notFound",
|
|
15187
|
-
nodes: cloneNodes$
|
|
15307
|
+
nodes: cloneNodes$2(nodes),
|
|
15188
15308
|
rootId,
|
|
15189
15309
|
targetKey: key,
|
|
15190
15310
|
currentNodeId: nodeId,
|
|
@@ -15200,7 +15320,7 @@ function generateBTreeSteps() {
|
|
|
15200
15320
|
if (node.isLeaf) {
|
|
15201
15321
|
steps.push({
|
|
15202
15322
|
operation: "notFound",
|
|
15203
|
-
nodes: cloneNodes$
|
|
15323
|
+
nodes: cloneNodes$2(nodes),
|
|
15204
15324
|
rootId,
|
|
15205
15325
|
targetKey: key,
|
|
15206
15326
|
currentNodeId: nodeId,
|
|
@@ -15219,7 +15339,7 @@ function generateBTreeSteps() {
|
|
|
15219
15339
|
nodes.forEach((node) => allKeys.push(...node.keys));
|
|
15220
15340
|
steps.push({
|
|
15221
15341
|
operation: "done",
|
|
15222
|
-
nodes: cloneNodes$
|
|
15342
|
+
nodes: cloneNodes$2(nodes),
|
|
15223
15343
|
rootId,
|
|
15224
15344
|
description: `✓ Done! B-Tree contains ${allKeys.length} keys across ${nodes.size} nodes.`,
|
|
15225
15345
|
codeLine: -1,
|
|
@@ -15404,7 +15524,7 @@ const BTreeVisualizerComponent = ({
|
|
|
15404
15524
|
] }),
|
|
15405
15525
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4 p-2 bg-blue-50 rounded-lg border border-blue-200", children: [
|
|
15406
15526
|
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-blue-800 mb-1", children: "Operations" }),
|
|
15407
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-blue-700 font-mono", children: OPERATIONS$
|
|
15527
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-blue-700 font-mono", children: OPERATIONS$3.map((op, i) => /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "mr-2", children: [
|
|
15408
15528
|
op.op,
|
|
15409
15529
|
"(",
|
|
15410
15530
|
op.key,
|
|
@@ -15449,40 +15569,926 @@ const BTreeVisualizerComponent = ({
|
|
|
15449
15569
|
accentColor: "green"
|
|
15450
15570
|
}
|
|
15451
15571
|
),
|
|
15452
|
-
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$
|
|
15572
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$6 })
|
|
15453
15573
|
] })
|
|
15454
15574
|
]
|
|
15455
15575
|
}
|
|
15456
15576
|
);
|
|
15457
15577
|
};
|
|
15458
15578
|
const BTreeVisualizer = React.memo(BTreeVisualizerComponent);
|
|
15459
|
-
const
|
|
15460
|
-
|
|
15461
|
-
{ op: "
|
|
15462
|
-
{ op: "
|
|
15463
|
-
{ op: "
|
|
15464
|
-
{ op: "
|
|
15465
|
-
{ op: "
|
|
15466
|
-
{ op: "
|
|
15467
|
-
{ op: "
|
|
15468
|
-
{ op: "removeServer", server: "Server-B" }
|
|
15579
|
+
const OPERATIONS$2 = [
|
|
15580
|
+
{ op: "insert", word: "cat" },
|
|
15581
|
+
{ op: "insert", word: "car" },
|
|
15582
|
+
{ op: "insert", word: "card" },
|
|
15583
|
+
{ op: "insert", word: "care" },
|
|
15584
|
+
{ op: "insert", word: "dog" },
|
|
15585
|
+
{ op: "search", word: "car" },
|
|
15586
|
+
{ op: "search", word: "cab" },
|
|
15587
|
+
{ op: "prefix", word: "car" }
|
|
15469
15588
|
];
|
|
15470
|
-
const
|
|
15471
|
-
"class
|
|
15472
|
-
"
|
|
15473
|
-
"
|
|
15589
|
+
const TRIE_CODE = [
|
|
15590
|
+
"class TrieNode:",
|
|
15591
|
+
" children = {} # char -> node",
|
|
15592
|
+
" isEndOfWord = false",
|
|
15474
15593
|
"",
|
|
15475
|
-
"function
|
|
15476
|
-
"
|
|
15477
|
-
|
|
15478
|
-
"
|
|
15594
|
+
"function insert(word):",
|
|
15595
|
+
" node = root",
|
|
15596
|
+
" for char in word:",
|
|
15597
|
+
" if char not in node.children:",
|
|
15598
|
+
" node.children[char] = TrieNode()",
|
|
15599
|
+
" node = node.children[char]",
|
|
15600
|
+
" node.isEndOfWord = true",
|
|
15479
15601
|
"",
|
|
15480
|
-
"function
|
|
15481
|
-
"
|
|
15482
|
-
"
|
|
15483
|
-
"
|
|
15484
|
-
"
|
|
15485
|
-
"
|
|
15602
|
+
"function search(word):",
|
|
15603
|
+
" node = root",
|
|
15604
|
+
" for char in word:",
|
|
15605
|
+
" if char not in node.children:",
|
|
15606
|
+
" return false",
|
|
15607
|
+
" node = node.children[char]",
|
|
15608
|
+
" return node.isEndOfWord"
|
|
15609
|
+
];
|
|
15610
|
+
const LEGEND_ITEMS$5 = [
|
|
15611
|
+
{ color: "bg-gray-100", label: "Node", border: "#d1d5db" },
|
|
15612
|
+
{ color: "bg-blue-200", label: "Current", border: "#60a5fa" },
|
|
15613
|
+
{ color: "bg-green-400", label: "End of word" },
|
|
15614
|
+
{ color: "bg-yellow-200", label: "Path", border: "#fbbf24" },
|
|
15615
|
+
{ color: "bg-purple-400", label: "Match found" },
|
|
15616
|
+
{ color: "bg-red-400", label: "Not found" }
|
|
15617
|
+
];
|
|
15618
|
+
let nodeIdCounter = 0;
|
|
15619
|
+
function generateNodeId() {
|
|
15620
|
+
return `trie_${nodeIdCounter++}`;
|
|
15621
|
+
}
|
|
15622
|
+
function cloneNodes$1(nodes) {
|
|
15623
|
+
const cloned = /* @__PURE__ */ new Map();
|
|
15624
|
+
nodes.forEach((node, id) => {
|
|
15625
|
+
cloned.set(id, {
|
|
15626
|
+
...node,
|
|
15627
|
+
children: new Map(node.children)
|
|
15628
|
+
});
|
|
15629
|
+
});
|
|
15630
|
+
return cloned;
|
|
15631
|
+
}
|
|
15632
|
+
function generateTrieSteps() {
|
|
15633
|
+
nodeIdCounter = 0;
|
|
15634
|
+
const steps = [];
|
|
15635
|
+
const nodes = /* @__PURE__ */ new Map();
|
|
15636
|
+
const rootId = generateNodeId();
|
|
15637
|
+
nodes.set(rootId, {
|
|
15638
|
+
id: rootId,
|
|
15639
|
+
char: "",
|
|
15640
|
+
children: /* @__PURE__ */ new Map(),
|
|
15641
|
+
isEndOfWord: false
|
|
15642
|
+
});
|
|
15643
|
+
steps.push({
|
|
15644
|
+
operation: "init",
|
|
15645
|
+
nodes: cloneNodes$1(nodes),
|
|
15646
|
+
rootId,
|
|
15647
|
+
description: "Initialize empty Trie with root node. Each node stores a character and links to children.",
|
|
15648
|
+
codeLine: 0
|
|
15649
|
+
});
|
|
15650
|
+
for (const { op, word } of OPERATIONS$2) {
|
|
15651
|
+
if (op === "insert") {
|
|
15652
|
+
steps.push({
|
|
15653
|
+
operation: "insert",
|
|
15654
|
+
nodes: cloneNodes$1(nodes),
|
|
15655
|
+
rootId,
|
|
15656
|
+
word,
|
|
15657
|
+
currentNodeId: rootId,
|
|
15658
|
+
description: `insert("${word}"): Start at root, traverse/create nodes for each character`,
|
|
15659
|
+
codeLine: 4,
|
|
15660
|
+
variables: { word: `"${word}"` }
|
|
15661
|
+
});
|
|
15662
|
+
let currentId = rootId;
|
|
15663
|
+
const path = [rootId];
|
|
15664
|
+
for (let i = 0; i < word.length; i++) {
|
|
15665
|
+
const char = word[i];
|
|
15666
|
+
const currentNode = nodes.get(currentId);
|
|
15667
|
+
if (currentNode.children.has(char)) {
|
|
15668
|
+
const nextId = currentNode.children.get(char);
|
|
15669
|
+
path.push(nextId);
|
|
15670
|
+
steps.push({
|
|
15671
|
+
operation: "traverse",
|
|
15672
|
+
nodes: cloneNodes$1(nodes),
|
|
15673
|
+
rootId,
|
|
15674
|
+
word,
|
|
15675
|
+
currentNodeId: nextId,
|
|
15676
|
+
highlightChar: char,
|
|
15677
|
+
highlightPath: [...path],
|
|
15678
|
+
description: `'${char}' exists → traverse to child node`,
|
|
15679
|
+
codeLine: 9,
|
|
15680
|
+
variables: { char: `'${char}'`, i, word: `"${word}"` }
|
|
15681
|
+
});
|
|
15682
|
+
currentId = nextId;
|
|
15683
|
+
} else {
|
|
15684
|
+
const newId = generateNodeId();
|
|
15685
|
+
nodes.set(newId, {
|
|
15686
|
+
id: newId,
|
|
15687
|
+
char,
|
|
15688
|
+
children: /* @__PURE__ */ new Map(),
|
|
15689
|
+
isEndOfWord: false
|
|
15690
|
+
});
|
|
15691
|
+
currentNode.children.set(char, newId);
|
|
15692
|
+
path.push(newId);
|
|
15693
|
+
steps.push({
|
|
15694
|
+
operation: "create",
|
|
15695
|
+
nodes: cloneNodes$1(nodes),
|
|
15696
|
+
rootId,
|
|
15697
|
+
word,
|
|
15698
|
+
currentNodeId: newId,
|
|
15699
|
+
highlightChar: char,
|
|
15700
|
+
highlightPath: [...path],
|
|
15701
|
+
description: `'${char}' not found → create new node`,
|
|
15702
|
+
codeLine: 8,
|
|
15703
|
+
variables: { char: `'${char}'`, i, word: `"${word}"` }
|
|
15704
|
+
});
|
|
15705
|
+
currentId = newId;
|
|
15706
|
+
}
|
|
15707
|
+
}
|
|
15708
|
+
const endNode = nodes.get(currentId);
|
|
15709
|
+
endNode.isEndOfWord = true;
|
|
15710
|
+
endNode.word = word;
|
|
15711
|
+
steps.push({
|
|
15712
|
+
operation: "markEnd",
|
|
15713
|
+
nodes: cloneNodes$1(nodes),
|
|
15714
|
+
rootId,
|
|
15715
|
+
word,
|
|
15716
|
+
currentNodeId: currentId,
|
|
15717
|
+
highlightPath: path,
|
|
15718
|
+
description: `Mark node as end of word "${word}"`,
|
|
15719
|
+
codeLine: 10,
|
|
15720
|
+
variables: { word: `"${word}"`, isEndOfWord: "true" }
|
|
15721
|
+
});
|
|
15722
|
+
} else if (op === "search") {
|
|
15723
|
+
steps.push({
|
|
15724
|
+
operation: "search",
|
|
15725
|
+
nodes: cloneNodes$1(nodes),
|
|
15726
|
+
rootId,
|
|
15727
|
+
word,
|
|
15728
|
+
currentNodeId: rootId,
|
|
15729
|
+
description: `search("${word}"): Check if word exists in Trie`,
|
|
15730
|
+
codeLine: 12,
|
|
15731
|
+
variables: { word: `"${word}"` }
|
|
15732
|
+
});
|
|
15733
|
+
let currentId = rootId;
|
|
15734
|
+
const path = [rootId];
|
|
15735
|
+
let found = true;
|
|
15736
|
+
for (let i = 0; i < word.length; i++) {
|
|
15737
|
+
const char = word[i];
|
|
15738
|
+
const currentNode = nodes.get(currentId);
|
|
15739
|
+
if (currentNode.children.has(char)) {
|
|
15740
|
+
const nextId = currentNode.children.get(char);
|
|
15741
|
+
path.push(nextId);
|
|
15742
|
+
steps.push({
|
|
15743
|
+
operation: "traverse",
|
|
15744
|
+
nodes: cloneNodes$1(nodes),
|
|
15745
|
+
rootId,
|
|
15746
|
+
word,
|
|
15747
|
+
currentNodeId: nextId,
|
|
15748
|
+
highlightChar: char,
|
|
15749
|
+
highlightPath: [...path],
|
|
15750
|
+
description: `'${char}' found → continue searching`,
|
|
15751
|
+
codeLine: 17,
|
|
15752
|
+
variables: { char: `'${char}'`, i, word: `"${word}"` }
|
|
15753
|
+
});
|
|
15754
|
+
currentId = nextId;
|
|
15755
|
+
} else {
|
|
15756
|
+
found = false;
|
|
15757
|
+
steps.push({
|
|
15758
|
+
operation: "notFound",
|
|
15759
|
+
nodes: cloneNodes$1(nodes),
|
|
15760
|
+
rootId,
|
|
15761
|
+
word,
|
|
15762
|
+
currentNodeId: currentId,
|
|
15763
|
+
highlightChar: char,
|
|
15764
|
+
highlightPath: path,
|
|
15765
|
+
description: `'${char}' not found → word "${word}" does not exist`,
|
|
15766
|
+
codeLine: 16,
|
|
15767
|
+
variables: { char: `'${char}'`, result: "false" }
|
|
15768
|
+
});
|
|
15769
|
+
break;
|
|
15770
|
+
}
|
|
15771
|
+
}
|
|
15772
|
+
if (found) {
|
|
15773
|
+
const endNode = nodes.get(currentId);
|
|
15774
|
+
if (endNode.isEndOfWord) {
|
|
15775
|
+
steps.push({
|
|
15776
|
+
operation: "found",
|
|
15777
|
+
nodes: cloneNodes$1(nodes),
|
|
15778
|
+
rootId,
|
|
15779
|
+
word,
|
|
15780
|
+
currentNodeId: currentId,
|
|
15781
|
+
highlightPath: path,
|
|
15782
|
+
description: `✓ Found! "${word}" exists in Trie`,
|
|
15783
|
+
codeLine: 18,
|
|
15784
|
+
variables: { word: `"${word}"`, result: "true" }
|
|
15785
|
+
});
|
|
15786
|
+
} else {
|
|
15787
|
+
steps.push({
|
|
15788
|
+
operation: "notFound",
|
|
15789
|
+
nodes: cloneNodes$1(nodes),
|
|
15790
|
+
rootId,
|
|
15791
|
+
word,
|
|
15792
|
+
currentNodeId: currentId,
|
|
15793
|
+
highlightPath: path,
|
|
15794
|
+
description: `Path exists but not marked as word end → "${word}" is only a prefix`,
|
|
15795
|
+
codeLine: 18,
|
|
15796
|
+
variables: { word: `"${word}"`, result: "false" }
|
|
15797
|
+
});
|
|
15798
|
+
}
|
|
15799
|
+
}
|
|
15800
|
+
} else if (op === "prefix") {
|
|
15801
|
+
steps.push({
|
|
15802
|
+
operation: "prefix",
|
|
15803
|
+
nodes: cloneNodes$1(nodes),
|
|
15804
|
+
rootId,
|
|
15805
|
+
prefix: word,
|
|
15806
|
+
currentNodeId: rootId,
|
|
15807
|
+
description: `startsWith("${word}"): Find all words with this prefix`,
|
|
15808
|
+
codeLine: 12,
|
|
15809
|
+
variables: { prefix: `"${word}"` }
|
|
15810
|
+
});
|
|
15811
|
+
let currentId = rootId;
|
|
15812
|
+
const path = [rootId];
|
|
15813
|
+
let prefixExists = true;
|
|
15814
|
+
for (let i = 0; i < word.length; i++) {
|
|
15815
|
+
const char = word[i];
|
|
15816
|
+
const currentNode = nodes.get(currentId);
|
|
15817
|
+
if (currentNode.children.has(char)) {
|
|
15818
|
+
const nextId = currentNode.children.get(char);
|
|
15819
|
+
path.push(nextId);
|
|
15820
|
+
currentId = nextId;
|
|
15821
|
+
} else {
|
|
15822
|
+
prefixExists = false;
|
|
15823
|
+
break;
|
|
15824
|
+
}
|
|
15825
|
+
}
|
|
15826
|
+
if (prefixExists) {
|
|
15827
|
+
const matchedWords = [];
|
|
15828
|
+
const collectWords = (nodeId) => {
|
|
15829
|
+
const node = nodes.get(nodeId);
|
|
15830
|
+
if (node.isEndOfWord && node.word) {
|
|
15831
|
+
matchedWords.push(node.word);
|
|
15832
|
+
}
|
|
15833
|
+
node.children.forEach((childId) => collectWords(childId));
|
|
15834
|
+
};
|
|
15835
|
+
collectWords(currentId);
|
|
15836
|
+
steps.push({
|
|
15837
|
+
operation: "found",
|
|
15838
|
+
nodes: cloneNodes$1(nodes),
|
|
15839
|
+
rootId,
|
|
15840
|
+
prefix: word,
|
|
15841
|
+
currentNodeId: currentId,
|
|
15842
|
+
highlightPath: path,
|
|
15843
|
+
matchedWords,
|
|
15844
|
+
description: `Found ${matchedWords.length} word(s) with prefix "${word}": ${matchedWords.join(", ")}`,
|
|
15845
|
+
codeLine: 18,
|
|
15846
|
+
variables: { prefix: `"${word}"`, count: matchedWords.length }
|
|
15847
|
+
});
|
|
15848
|
+
} else {
|
|
15849
|
+
steps.push({
|
|
15850
|
+
operation: "notFound",
|
|
15851
|
+
nodes: cloneNodes$1(nodes),
|
|
15852
|
+
rootId,
|
|
15853
|
+
prefix: word,
|
|
15854
|
+
highlightPath: path,
|
|
15855
|
+
description: `No words found with prefix "${word}"`,
|
|
15856
|
+
codeLine: 16,
|
|
15857
|
+
variables: { prefix: `"${word}"`, count: 0 }
|
|
15858
|
+
});
|
|
15859
|
+
}
|
|
15860
|
+
}
|
|
15861
|
+
}
|
|
15862
|
+
const allWords = [];
|
|
15863
|
+
const collectAllWords = (nodeId) => {
|
|
15864
|
+
const node = nodes.get(nodeId);
|
|
15865
|
+
if (node.isEndOfWord && node.word) {
|
|
15866
|
+
allWords.push(node.word);
|
|
15867
|
+
}
|
|
15868
|
+
node.children.forEach((childId) => collectAllWords(childId));
|
|
15869
|
+
};
|
|
15870
|
+
collectAllWords(rootId);
|
|
15871
|
+
steps.push({
|
|
15872
|
+
operation: "done",
|
|
15873
|
+
nodes: cloneNodes$1(nodes),
|
|
15874
|
+
rootId,
|
|
15875
|
+
matchedWords: allWords,
|
|
15876
|
+
description: `✓ Done! Trie contains ${allWords.length} words: ${allWords.join(", ")}`,
|
|
15877
|
+
codeLine: -1
|
|
15878
|
+
});
|
|
15879
|
+
return steps;
|
|
15880
|
+
}
|
|
15881
|
+
const TrieNodeComponent = ({ nodeId, nodes, currentNodeId, highlightPath, depth }) => {
|
|
15882
|
+
const node = nodes.get(nodeId);
|
|
15883
|
+
if (!node) return null;
|
|
15884
|
+
const isCurrent = currentNodeId === nodeId;
|
|
15885
|
+
const isInPath = highlightPath == null ? void 0 : highlightPath.includes(nodeId);
|
|
15886
|
+
const children = Array.from(node.children.entries());
|
|
15887
|
+
let nodeStyle = "border-gray-300 bg-gray-50";
|
|
15888
|
+
if (isCurrent) {
|
|
15889
|
+
nodeStyle = "border-blue-400 bg-blue-100 ring-2 ring-blue-300";
|
|
15890
|
+
} else if (isInPath) {
|
|
15891
|
+
nodeStyle = "border-yellow-400 bg-yellow-100";
|
|
15892
|
+
}
|
|
15893
|
+
if (node.isEndOfWord) {
|
|
15894
|
+
nodeStyle += " ring-2 ring-green-400";
|
|
15895
|
+
}
|
|
15896
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
15897
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15898
|
+
"div",
|
|
15899
|
+
{
|
|
15900
|
+
className: `
|
|
15901
|
+
w-10 h-10 rounded-lg border-2 flex items-center justify-center
|
|
15902
|
+
text-sm font-bold transition-all duration-300
|
|
15903
|
+
${nodeStyle}
|
|
15904
|
+
`,
|
|
15905
|
+
children: node.char || "∅"
|
|
15906
|
+
}
|
|
15907
|
+
),
|
|
15908
|
+
node.isEndOfWord && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[9px] text-green-600 font-medium mt-0.5", children: node.word }),
|
|
15909
|
+
children.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
15910
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-0.5 h-4 bg-gray-300" }),
|
|
15911
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2", children: children.map(([char, childId]) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
15912
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-gray-500 mb-1", children: char }),
|
|
15913
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
15914
|
+
TrieNodeComponent,
|
|
15915
|
+
{
|
|
15916
|
+
nodeId: childId,
|
|
15917
|
+
nodes,
|
|
15918
|
+
currentNodeId,
|
|
15919
|
+
highlightPath,
|
|
15920
|
+
depth: depth + 1
|
|
15921
|
+
}
|
|
15922
|
+
)
|
|
15923
|
+
] }, childId)) })
|
|
15924
|
+
] })
|
|
15925
|
+
] });
|
|
15926
|
+
};
|
|
15927
|
+
const TrieVisualizerComponent = ({
|
|
15928
|
+
showControls = true,
|
|
15929
|
+
showCode = true,
|
|
15930
|
+
className = ""
|
|
15931
|
+
}) => {
|
|
15932
|
+
const VISUALIZER_ID = "trie-visualizer";
|
|
15933
|
+
const { copyUrlToClipboard } = useUrlState({ prefix: "trie", scrollToId: VISUALIZER_ID });
|
|
15934
|
+
const generateSteps = React.useMemo(() => generateTrieSteps, []);
|
|
15935
|
+
const {
|
|
15936
|
+
steps,
|
|
15937
|
+
currentStep,
|
|
15938
|
+
currentStepData,
|
|
15939
|
+
isPlaying,
|
|
15940
|
+
speed,
|
|
15941
|
+
setSpeed,
|
|
15942
|
+
handlePlayPause,
|
|
15943
|
+
handleStep,
|
|
15944
|
+
handleStepBack,
|
|
15945
|
+
handleReset
|
|
15946
|
+
} = useVisualizerPlayback({
|
|
15947
|
+
generateSteps
|
|
15948
|
+
});
|
|
15949
|
+
const stepData = currentStepData || {
|
|
15950
|
+
operation: "init",
|
|
15951
|
+
nodes: /* @__PURE__ */ new Map(),
|
|
15952
|
+
rootId: "",
|
|
15953
|
+
description: ""
|
|
15954
|
+
};
|
|
15955
|
+
const getStatusVariant = () => {
|
|
15956
|
+
if (stepData.operation === "notFound") return "error";
|
|
15957
|
+
if (stepData.operation === "found" || stepData.operation === "markEnd") return "success";
|
|
15958
|
+
if (stepData.operation === "done") return "warning";
|
|
15959
|
+
return "default";
|
|
15960
|
+
};
|
|
15961
|
+
const handleShare = React.useCallback(async () => {
|
|
15962
|
+
return copyUrlToClipboard({ step: currentStep });
|
|
15963
|
+
}, [copyUrlToClipboard, currentStep]);
|
|
15964
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
15965
|
+
"div",
|
|
15966
|
+
{
|
|
15967
|
+
id: VISUALIZER_ID,
|
|
15968
|
+
className: `bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden ${className}`,
|
|
15969
|
+
children: [
|
|
15970
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 bg-gradient-to-r from-teal-50 to-cyan-50 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
|
|
15971
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
15972
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-gray-900", children: "Trie (Prefix Tree)" }),
|
|
15973
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
15974
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-teal-100 text-teal-700 rounded", children: "Insert: O(m)" }),
|
|
15975
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-cyan-100 text-cyan-700 rounded", children: "Search: O(m)" })
|
|
15976
|
+
] })
|
|
15977
|
+
] }),
|
|
15978
|
+
/* @__PURE__ */ jsxRuntime.jsx(ShareButton, { onShare: handleShare })
|
|
15979
|
+
] }) }),
|
|
15980
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex gap-4 ${showCode ? "flex-col lg:flex-row" : ""}`, children: [
|
|
15981
|
+
/* @__PURE__ */ jsxRuntime.jsxs(VisualizationArea, { minHeight: 400, className: showCode ? "flex-1" : "w-full", children: [
|
|
15982
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4 p-3 bg-gradient-to-r from-teal-50 to-cyan-50 rounded-lg border border-teal-200", children: [
|
|
15983
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-teal-800 mb-2", children: "🌲 Trie - Prefix Tree" }),
|
|
15984
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-teal-700 space-y-1", children: [
|
|
15985
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "• Each node represents a character" }),
|
|
15986
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "• Words share common prefixes (space efficient)" }),
|
|
15987
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "• Perfect for autocomplete & spell checking" }),
|
|
15988
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "• m = word length, n = number of words" })
|
|
15989
|
+
] })
|
|
15990
|
+
] }),
|
|
15991
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-center overflow-x-auto py-4", children: stepData.nodes.size > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
15992
|
+
TrieNodeComponent,
|
|
15993
|
+
{
|
|
15994
|
+
nodeId: stepData.rootId,
|
|
15995
|
+
nodes: stepData.nodes,
|
|
15996
|
+
currentNodeId: stepData.currentNodeId,
|
|
15997
|
+
highlightPath: stepData.highlightPath,
|
|
15998
|
+
depth: 0
|
|
15999
|
+
}
|
|
16000
|
+
) }),
|
|
16001
|
+
stepData.matchedWords && stepData.matchedWords.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-4 p-3 bg-purple-50 rounded-lg border border-purple-200", children: [
|
|
16002
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-purple-800 mb-2", children: "Words found:" }),
|
|
16003
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: stepData.matchedWords.map((word) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
16004
|
+
"span",
|
|
16005
|
+
{
|
|
16006
|
+
className: "px-2 py-1 bg-purple-100 text-purple-700 text-sm rounded font-mono",
|
|
16007
|
+
children: word
|
|
16008
|
+
},
|
|
16009
|
+
word
|
|
16010
|
+
)) })
|
|
16011
|
+
] }),
|
|
16012
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16013
|
+
StatusPanel,
|
|
16014
|
+
{
|
|
16015
|
+
description: stepData.description,
|
|
16016
|
+
currentStep,
|
|
16017
|
+
totalSteps: steps.length,
|
|
16018
|
+
variant: getStatusVariant()
|
|
16019
|
+
}
|
|
16020
|
+
)
|
|
16021
|
+
] }),
|
|
16022
|
+
showCode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full lg:w-56 flex-shrink-0 space-y-2", children: [
|
|
16023
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16024
|
+
CodePanel,
|
|
16025
|
+
{
|
|
16026
|
+
code: TRIE_CODE,
|
|
16027
|
+
activeLine: (currentStepData == null ? void 0 : currentStepData.codeLine) ?? -1,
|
|
16028
|
+
variables: currentStepData == null ? void 0 : currentStepData.variables
|
|
16029
|
+
}
|
|
16030
|
+
),
|
|
16031
|
+
/* @__PURE__ */ jsxRuntime.jsx(HelpPanel, {})
|
|
16032
|
+
] })
|
|
16033
|
+
] }) }),
|
|
16034
|
+
showControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
|
|
16035
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16036
|
+
ControlPanel,
|
|
16037
|
+
{
|
|
16038
|
+
isPlaying,
|
|
16039
|
+
currentStep,
|
|
16040
|
+
totalSteps: steps.length,
|
|
16041
|
+
speed,
|
|
16042
|
+
onPlayPause: handlePlayPause,
|
|
16043
|
+
onStep: handleStep,
|
|
16044
|
+
onStepBack: handleStepBack,
|
|
16045
|
+
onReset: handleReset,
|
|
16046
|
+
onSpeedChange: setSpeed,
|
|
16047
|
+
accentColor: "teal"
|
|
16048
|
+
}
|
|
16049
|
+
),
|
|
16050
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$5 })
|
|
16051
|
+
] })
|
|
16052
|
+
]
|
|
16053
|
+
}
|
|
16054
|
+
);
|
|
16055
|
+
};
|
|
16056
|
+
const TrieVisualizer = React.memo(TrieVisualizerComponent);
|
|
16057
|
+
const INITIAL_SIZE = 8;
|
|
16058
|
+
const OPERATIONS$1 = [
|
|
16059
|
+
{ op: "union", a: 0, b: 1 },
|
|
16060
|
+
{ op: "union", a: 2, b: 3 },
|
|
16061
|
+
{ op: "union", a: 4, b: 5 },
|
|
16062
|
+
{ op: "union", a: 6, b: 7 },
|
|
16063
|
+
{ op: "union", a: 0, b: 2 },
|
|
16064
|
+
{ op: "union", a: 4, b: 6 },
|
|
16065
|
+
{ op: "find", a: 3 },
|
|
16066
|
+
{ op: "connected", a: 1, b: 3 },
|
|
16067
|
+
{ op: "connected", a: 0, b: 5 },
|
|
16068
|
+
{ op: "union", a: 0, b: 4 },
|
|
16069
|
+
{ op: "connected", a: 3, b: 7 }
|
|
16070
|
+
];
|
|
16071
|
+
const UF_CODE = [
|
|
16072
|
+
"class UnionFind:",
|
|
16073
|
+
" parent[] = [0, 1, ..., n-1]",
|
|
16074
|
+
" rank[] = [0, 0, ..., 0]",
|
|
16075
|
+
"",
|
|
16076
|
+
"function find(x):",
|
|
16077
|
+
" if parent[x] != x:",
|
|
16078
|
+
" parent[x] = find(parent[x])",
|
|
16079
|
+
" # path compression",
|
|
16080
|
+
" return parent[x]",
|
|
16081
|
+
"",
|
|
16082
|
+
"function union(x, y):",
|
|
16083
|
+
" rootX = find(x)",
|
|
16084
|
+
" rootY = find(y)",
|
|
16085
|
+
" if rootX == rootY: return",
|
|
16086
|
+
" # union by rank",
|
|
16087
|
+
" if rank[rootX] < rank[rootY]:",
|
|
16088
|
+
" parent[rootX] = rootY",
|
|
16089
|
+
" else if rank[rootX] > rank[rootY]:",
|
|
16090
|
+
" parent[rootY] = rootX",
|
|
16091
|
+
" else:",
|
|
16092
|
+
" parent[rootY] = rootX",
|
|
16093
|
+
" rank[rootX] += 1"
|
|
16094
|
+
];
|
|
16095
|
+
const LEGEND_ITEMS$4 = [
|
|
16096
|
+
{ color: "bg-gray-100", label: "Element", border: "#d1d5db" },
|
|
16097
|
+
{ color: "bg-blue-200", label: "Current", border: "#60a5fa" },
|
|
16098
|
+
{ color: "bg-yellow-200", label: "Path to root", border: "#fbbf24" },
|
|
16099
|
+
{ color: "bg-green-400", label: "Root / Same set" },
|
|
16100
|
+
{ color: "bg-red-400", label: "Different sets" },
|
|
16101
|
+
{ color: "bg-purple-400", label: "Union edge" }
|
|
16102
|
+
];
|
|
16103
|
+
function cloneElements(elements) {
|
|
16104
|
+
return elements.map((el) => ({ ...el }));
|
|
16105
|
+
}
|
|
16106
|
+
function generateUnionFindSteps() {
|
|
16107
|
+
const steps = [];
|
|
16108
|
+
const elements = Array.from({ length: INITIAL_SIZE }, (_, i) => ({
|
|
16109
|
+
id: i,
|
|
16110
|
+
parent: i,
|
|
16111
|
+
rank: 0
|
|
16112
|
+
}));
|
|
16113
|
+
steps.push({
|
|
16114
|
+
operation: "init",
|
|
16115
|
+
elements: cloneElements(elements),
|
|
16116
|
+
description: `Initialize ${INITIAL_SIZE} elements. Each element is its own parent (self-loop).`,
|
|
16117
|
+
codeLine: 1,
|
|
16118
|
+
variables: { n: INITIAL_SIZE }
|
|
16119
|
+
});
|
|
16120
|
+
const find = (x, trackPath = false) => {
|
|
16121
|
+
const path = [x];
|
|
16122
|
+
let current = x;
|
|
16123
|
+
while (elements[current].parent !== current) {
|
|
16124
|
+
current = elements[current].parent;
|
|
16125
|
+
path.push(current);
|
|
16126
|
+
}
|
|
16127
|
+
if (trackPath && path.length > 2) {
|
|
16128
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
16129
|
+
elements[path[i]].parent = current;
|
|
16130
|
+
}
|
|
16131
|
+
}
|
|
16132
|
+
return { root: current, path };
|
|
16133
|
+
};
|
|
16134
|
+
for (const operation of OPERATIONS$1) {
|
|
16135
|
+
if (operation.op === "find") {
|
|
16136
|
+
const x = operation.a;
|
|
16137
|
+
steps.push({
|
|
16138
|
+
operation: "find",
|
|
16139
|
+
elements: cloneElements(elements),
|
|
16140
|
+
highlightElements: [x],
|
|
16141
|
+
description: `find(${x}): Find the root of element ${x}`,
|
|
16142
|
+
codeLine: 4,
|
|
16143
|
+
variables: { x }
|
|
16144
|
+
});
|
|
16145
|
+
const { root, path } = find(x);
|
|
16146
|
+
steps.push({
|
|
16147
|
+
operation: "find",
|
|
16148
|
+
elements: cloneElements(elements),
|
|
16149
|
+
highlightElements: [root],
|
|
16150
|
+
pathToRoot: path,
|
|
16151
|
+
description: `Path: ${path.join(" → ")}. Root is ${root}`,
|
|
16152
|
+
codeLine: 8,
|
|
16153
|
+
variables: { x, root }
|
|
16154
|
+
});
|
|
16155
|
+
if (path.length > 2) {
|
|
16156
|
+
find(x, true);
|
|
16157
|
+
steps.push({
|
|
16158
|
+
operation: "pathCompress",
|
|
16159
|
+
elements: cloneElements(elements),
|
|
16160
|
+
highlightElements: path.slice(0, -1),
|
|
16161
|
+
pathToRoot: path,
|
|
16162
|
+
description: `Path compression: All nodes on path now point directly to root ${root}`,
|
|
16163
|
+
codeLine: 6,
|
|
16164
|
+
variables: { x, root }
|
|
16165
|
+
});
|
|
16166
|
+
}
|
|
16167
|
+
} else if (operation.op === "union") {
|
|
16168
|
+
const x = operation.a;
|
|
16169
|
+
const y = operation.b;
|
|
16170
|
+
steps.push({
|
|
16171
|
+
operation: "union",
|
|
16172
|
+
elements: cloneElements(elements),
|
|
16173
|
+
highlightElements: [x, y],
|
|
16174
|
+
description: `union(${x}, ${y}): Merge sets containing ${x} and ${y}`,
|
|
16175
|
+
codeLine: 10,
|
|
16176
|
+
variables: { x, y }
|
|
16177
|
+
});
|
|
16178
|
+
const { root: rootX, path: pathX } = find(x);
|
|
16179
|
+
const { root: rootY, path: pathY } = find(y);
|
|
16180
|
+
steps.push({
|
|
16181
|
+
operation: "find",
|
|
16182
|
+
elements: cloneElements(elements),
|
|
16183
|
+
highlightElements: [rootX, rootY],
|
|
16184
|
+
pathToRoot: [...pathX, ...pathY],
|
|
16185
|
+
description: `find(${x}) = ${rootX}, find(${y}) = ${rootY}`,
|
|
16186
|
+
codeLine: 11,
|
|
16187
|
+
variables: { x, y, rootX, rootY }
|
|
16188
|
+
});
|
|
16189
|
+
if (rootX === rootY) {
|
|
16190
|
+
steps.push({
|
|
16191
|
+
operation: "sameSet",
|
|
16192
|
+
elements: cloneElements(elements),
|
|
16193
|
+
highlightElements: [rootX],
|
|
16194
|
+
description: `Already in same set! No union needed.`,
|
|
16195
|
+
codeLine: 13,
|
|
16196
|
+
variables: { rootX, rootY, "same": "true" }
|
|
16197
|
+
});
|
|
16198
|
+
} else {
|
|
16199
|
+
const rankX = elements[rootX].rank;
|
|
16200
|
+
const rankY = elements[rootY].rank;
|
|
16201
|
+
let newRoot;
|
|
16202
|
+
if (rankX < rankY) {
|
|
16203
|
+
elements[rootX].parent = rootY;
|
|
16204
|
+
newRoot = rootY;
|
|
16205
|
+
} else if (rankX > rankY) {
|
|
16206
|
+
elements[rootY].parent = rootX;
|
|
16207
|
+
newRoot = rootX;
|
|
16208
|
+
} else {
|
|
16209
|
+
elements[rootY].parent = rootX;
|
|
16210
|
+
elements[rootX].rank++;
|
|
16211
|
+
newRoot = rootX;
|
|
16212
|
+
}
|
|
16213
|
+
steps.push({
|
|
16214
|
+
operation: "unionByRank",
|
|
16215
|
+
elements: cloneElements(elements),
|
|
16216
|
+
highlightElements: [newRoot],
|
|
16217
|
+
highlightEdge: [rootX, rootY],
|
|
16218
|
+
description: `Union by rank: rank[${rootX}]=${rankX}, rank[${rootY}]=${rankY}. ${newRoot} becomes root.`,
|
|
16219
|
+
codeLine: rankX === rankY ? 20 : rankX < rankY ? 16 : 18,
|
|
16220
|
+
variables: { rootX, rootY, rankX, rankY, newRoot }
|
|
16221
|
+
});
|
|
16222
|
+
}
|
|
16223
|
+
} else if (operation.op === "connected") {
|
|
16224
|
+
const x = operation.a;
|
|
16225
|
+
const y = operation.b;
|
|
16226
|
+
steps.push({
|
|
16227
|
+
operation: "find",
|
|
16228
|
+
elements: cloneElements(elements),
|
|
16229
|
+
highlightElements: [x, y],
|
|
16230
|
+
description: `connected(${x}, ${y}): Check if ${x} and ${y} are in the same set`,
|
|
16231
|
+
codeLine: 4,
|
|
16232
|
+
variables: { x, y }
|
|
16233
|
+
});
|
|
16234
|
+
const { root: rootX, path: pathX } = find(x);
|
|
16235
|
+
const { root: rootY, path: pathY } = find(y);
|
|
16236
|
+
const connected = rootX === rootY;
|
|
16237
|
+
steps.push({
|
|
16238
|
+
operation: "sameSet",
|
|
16239
|
+
elements: cloneElements(elements),
|
|
16240
|
+
highlightElements: connected ? [rootX] : [rootX, rootY],
|
|
16241
|
+
pathToRoot: [...pathX, ...pathY],
|
|
16242
|
+
description: connected ? `✓ Yes! Both ${x} and ${y} have root ${rootX}` : `✗ No! ${x} has root ${rootX}, ${y} has root ${rootY}`,
|
|
16243
|
+
codeLine: 13,
|
|
16244
|
+
variables: { x, y, rootX, rootY, connected: connected ? "true" : "false" },
|
|
16245
|
+
queryResult: connected
|
|
16246
|
+
});
|
|
16247
|
+
}
|
|
16248
|
+
}
|
|
16249
|
+
const roots = /* @__PURE__ */ new Set();
|
|
16250
|
+
for (let i = 0; i < INITIAL_SIZE; i++) {
|
|
16251
|
+
roots.add(find(i).root);
|
|
16252
|
+
}
|
|
16253
|
+
steps.push({
|
|
16254
|
+
operation: "done",
|
|
16255
|
+
elements: cloneElements(elements),
|
|
16256
|
+
description: `✓ Done! ${roots.size} distinct set(s) remain.`,
|
|
16257
|
+
codeLine: -1,
|
|
16258
|
+
variables: { sets: roots.size }
|
|
16259
|
+
});
|
|
16260
|
+
return steps;
|
|
16261
|
+
}
|
|
16262
|
+
const UnionFindVisualizerComponent = ({
|
|
16263
|
+
showControls = true,
|
|
16264
|
+
showCode = true,
|
|
16265
|
+
className = ""
|
|
16266
|
+
}) => {
|
|
16267
|
+
const VISUALIZER_ID = "unionfind-visualizer";
|
|
16268
|
+
const { copyUrlToClipboard } = useUrlState({ prefix: "uf", scrollToId: VISUALIZER_ID });
|
|
16269
|
+
const generateSteps = React.useMemo(() => generateUnionFindSteps, []);
|
|
16270
|
+
const {
|
|
16271
|
+
steps,
|
|
16272
|
+
currentStep,
|
|
16273
|
+
currentStepData,
|
|
16274
|
+
isPlaying,
|
|
16275
|
+
speed,
|
|
16276
|
+
setSpeed,
|
|
16277
|
+
handlePlayPause,
|
|
16278
|
+
handleStep,
|
|
16279
|
+
handleStepBack,
|
|
16280
|
+
handleReset
|
|
16281
|
+
} = useVisualizerPlayback({
|
|
16282
|
+
generateSteps
|
|
16283
|
+
});
|
|
16284
|
+
const stepData = currentStepData || {
|
|
16285
|
+
operation: "init",
|
|
16286
|
+
elements: [],
|
|
16287
|
+
description: ""
|
|
16288
|
+
};
|
|
16289
|
+
const groups = React.useMemo(() => {
|
|
16290
|
+
const groupMap = /* @__PURE__ */ new Map();
|
|
16291
|
+
for (const el of stepData.elements) {
|
|
16292
|
+
let current = el.id;
|
|
16293
|
+
while (stepData.elements[current].parent !== current) {
|
|
16294
|
+
current = stepData.elements[current].parent;
|
|
16295
|
+
}
|
|
16296
|
+
const root = current;
|
|
16297
|
+
if (!groupMap.has(root)) {
|
|
16298
|
+
groupMap.set(root, []);
|
|
16299
|
+
}
|
|
16300
|
+
groupMap.get(root).push(el.id);
|
|
16301
|
+
}
|
|
16302
|
+
return groupMap;
|
|
16303
|
+
}, [stepData.elements]);
|
|
16304
|
+
const getElementStyle = (id) => {
|
|
16305
|
+
var _a, _b, _c;
|
|
16306
|
+
const isHighlighted = (_a = stepData.highlightElements) == null ? void 0 : _a.includes(id);
|
|
16307
|
+
const isInPath = (_b = stepData.pathToRoot) == null ? void 0 : _b.includes(id);
|
|
16308
|
+
const isRoot = ((_c = stepData.elements[id]) == null ? void 0 : _c.parent) === id;
|
|
16309
|
+
if (isHighlighted && isRoot) return "border-green-500 bg-green-100 ring-2 ring-green-300";
|
|
16310
|
+
if (isHighlighted) return "border-blue-400 bg-blue-100 ring-2 ring-blue-300";
|
|
16311
|
+
if (isInPath) return "border-yellow-400 bg-yellow-100";
|
|
16312
|
+
if (isRoot) return "border-green-300 bg-green-50";
|
|
16313
|
+
return "border-gray-300 bg-gray-50";
|
|
16314
|
+
};
|
|
16315
|
+
const getStatusVariant = () => {
|
|
16316
|
+
if (stepData.queryResult === false) return "error";
|
|
16317
|
+
if (stepData.queryResult === true) return "success";
|
|
16318
|
+
if (stepData.operation === "done") return "warning";
|
|
16319
|
+
if (stepData.operation === "pathCompress" || stepData.operation === "unionByRank") return "success";
|
|
16320
|
+
return "default";
|
|
16321
|
+
};
|
|
16322
|
+
const handleShare = React.useCallback(async () => {
|
|
16323
|
+
return copyUrlToClipboard({ step: currentStep });
|
|
16324
|
+
}, [copyUrlToClipboard, currentStep]);
|
|
16325
|
+
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"];
|
|
16326
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16327
|
+
"div",
|
|
16328
|
+
{
|
|
16329
|
+
id: VISUALIZER_ID,
|
|
16330
|
+
className: `bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden ${className}`,
|
|
16331
|
+
children: [
|
|
16332
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-4 py-3 bg-gradient-to-r from-violet-50 to-purple-50 border-b border-gray-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between flex-wrap gap-2", children: [
|
|
16333
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
16334
|
+
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-gray-900", children: "Union-Find (Disjoint Set)" }),
|
|
16335
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
16336
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-violet-100 text-violet-700 rounded", children: "Find: O(α(n))" }),
|
|
16337
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-2 py-0.5 text-xs font-medium bg-purple-100 text-purple-700 rounded", children: "Union: O(α(n))" })
|
|
16338
|
+
] })
|
|
16339
|
+
] }),
|
|
16340
|
+
/* @__PURE__ */ jsxRuntime.jsx(ShareButton, { onShare: handleShare })
|
|
16341
|
+
] }) }),
|
|
16342
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "p-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `flex gap-4 ${showCode ? "flex-col lg:flex-row" : ""}`, children: [
|
|
16343
|
+
/* @__PURE__ */ jsxRuntime.jsxs(VisualizationArea, { minHeight: 400, className: showCode ? "flex-1" : "w-full", children: [
|
|
16344
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4 p-3 bg-gradient-to-r from-violet-50 to-purple-50 rounded-lg border border-violet-200", children: [
|
|
16345
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-semibold text-violet-800 mb-2", children: "🔗 Union-Find / Disjoint Set Union (DSU)" }),
|
|
16346
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-violet-700 space-y-1", children: [
|
|
16347
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
16348
|
+
"• ",
|
|
16349
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Path Compression" }),
|
|
16350
|
+
": Flattens tree on find()"
|
|
16351
|
+
] }),
|
|
16352
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
16353
|
+
"• ",
|
|
16354
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Union by Rank" }),
|
|
16355
|
+
": Attach smaller tree to larger"
|
|
16356
|
+
] }),
|
|
16357
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "• α(n) = inverse Ackermann, practically ≤ 4" }),
|
|
16358
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { children: "• Used in Kruskal's MST, cycle detection, networks" })
|
|
16359
|
+
] })
|
|
16360
|
+
] }),
|
|
16361
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
|
|
16362
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm font-medium text-gray-700 mb-2", children: "Elements (parent pointers shown as arrows)" }),
|
|
16363
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-3 justify-center", children: stepData.elements.map((el) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center", children: [
|
|
16364
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
16365
|
+
"div",
|
|
16366
|
+
{
|
|
16367
|
+
className: `
|
|
16368
|
+
w-12 h-12 rounded-lg border-2 flex flex-col items-center justify-center
|
|
16369
|
+
text-sm font-bold transition-all duration-300
|
|
16370
|
+
${getElementStyle(el.id)}
|
|
16371
|
+
`,
|
|
16372
|
+
children: [
|
|
16373
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: el.id }),
|
|
16374
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[9px] text-gray-500", children: [
|
|
16375
|
+
"r:",
|
|
16376
|
+
el.rank
|
|
16377
|
+
] })
|
|
16378
|
+
]
|
|
16379
|
+
}
|
|
16380
|
+
),
|
|
16381
|
+
el.parent !== el.id && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-gray-500 mt-1", children: [
|
|
16382
|
+
"↑ ",
|
|
16383
|
+
el.parent
|
|
16384
|
+
] }),
|
|
16385
|
+
el.parent === el.id && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs text-green-600 mt-1 font-medium", children: "root" })
|
|
16386
|
+
] }, el.id)) })
|
|
16387
|
+
] }),
|
|
16388
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-4", children: [
|
|
16389
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm font-medium text-gray-700 mb-2", children: [
|
|
16390
|
+
"Connected Components (",
|
|
16391
|
+
groups.size,
|
|
16392
|
+
" set",
|
|
16393
|
+
groups.size !== 1 ? "s" : "",
|
|
16394
|
+
")"
|
|
16395
|
+
] }),
|
|
16396
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2", children: Array.from(groups.entries()).map(([root, members], idx) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
16397
|
+
"div",
|
|
16398
|
+
{
|
|
16399
|
+
className: `px-3 py-2 rounded-lg border ${groupColors[idx % groupColors.length]} border-gray-300`,
|
|
16400
|
+
children: [
|
|
16401
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-gray-600 mb-1", children: [
|
|
16402
|
+
"Root: ",
|
|
16403
|
+
root
|
|
16404
|
+
] }),
|
|
16405
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1", children: members.sort((a, b) => a - b).map((m) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
16406
|
+
"span",
|
|
16407
|
+
{
|
|
16408
|
+
className: `w-6 h-6 rounded flex items-center justify-center text-xs font-medium
|
|
16409
|
+
${m === root ? "bg-green-200 text-green-800" : "bg-white text-gray-700 border border-gray-300"}
|
|
16410
|
+
`,
|
|
16411
|
+
children: m
|
|
16412
|
+
},
|
|
16413
|
+
m
|
|
16414
|
+
)) })
|
|
16415
|
+
]
|
|
16416
|
+
},
|
|
16417
|
+
root
|
|
16418
|
+
)) })
|
|
16419
|
+
] }),
|
|
16420
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16421
|
+
StatusPanel,
|
|
16422
|
+
{
|
|
16423
|
+
description: stepData.description,
|
|
16424
|
+
currentStep,
|
|
16425
|
+
totalSteps: steps.length,
|
|
16426
|
+
variant: getStatusVariant()
|
|
16427
|
+
}
|
|
16428
|
+
)
|
|
16429
|
+
] }),
|
|
16430
|
+
showCode && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "w-full lg:w-56 flex-shrink-0 space-y-2", children: [
|
|
16431
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16432
|
+
CodePanel,
|
|
16433
|
+
{
|
|
16434
|
+
code: UF_CODE,
|
|
16435
|
+
activeLine: (currentStepData == null ? void 0 : currentStepData.codeLine) ?? -1,
|
|
16436
|
+
variables: currentStepData == null ? void 0 : currentStepData.variables
|
|
16437
|
+
}
|
|
16438
|
+
),
|
|
16439
|
+
/* @__PURE__ */ jsxRuntime.jsx(HelpPanel, {})
|
|
16440
|
+
] })
|
|
16441
|
+
] }) }),
|
|
16442
|
+
showControls && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-4 py-3 bg-gray-50 border-t border-gray-200", children: [
|
|
16443
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
16444
|
+
ControlPanel,
|
|
16445
|
+
{
|
|
16446
|
+
isPlaying,
|
|
16447
|
+
currentStep,
|
|
16448
|
+
totalSteps: steps.length,
|
|
16449
|
+
speed,
|
|
16450
|
+
onPlayPause: handlePlayPause,
|
|
16451
|
+
onStep: handleStep,
|
|
16452
|
+
onStepBack: handleStepBack,
|
|
16453
|
+
onReset: handleReset,
|
|
16454
|
+
onSpeedChange: setSpeed,
|
|
16455
|
+
accentColor: "violet"
|
|
16456
|
+
}
|
|
16457
|
+
),
|
|
16458
|
+
/* @__PURE__ */ jsxRuntime.jsx(Legend, { items: LEGEND_ITEMS$4 })
|
|
16459
|
+
] })
|
|
16460
|
+
]
|
|
16461
|
+
}
|
|
16462
|
+
);
|
|
16463
|
+
};
|
|
16464
|
+
const UnionFindVisualizer = React.memo(UnionFindVisualizerComponent);
|
|
16465
|
+
const VIRTUAL_NODES_PER_SERVER = 3;
|
|
16466
|
+
const OPERATIONS = [
|
|
16467
|
+
{ op: "addServer", server: "Server-A" },
|
|
16468
|
+
{ op: "addServer", server: "Server-B" },
|
|
16469
|
+
{ op: "addServer", server: "Server-C" },
|
|
16470
|
+
{ op: "addKey", key: "user:1001" },
|
|
16471
|
+
{ op: "addKey", key: "user:1002" },
|
|
16472
|
+
{ op: "addKey", key: "session:abc" },
|
|
16473
|
+
{ op: "addKey", key: "cache:page1" },
|
|
16474
|
+
{ op: "removeServer", server: "Server-B" }
|
|
16475
|
+
];
|
|
16476
|
+
const CONSISTENT_HASHING_CODE = [
|
|
16477
|
+
"class ConsistentHash:",
|
|
16478
|
+
" ring = SortedMap() # position → server",
|
|
16479
|
+
" virtualNodes = 3 # replicas per server",
|
|
16480
|
+
"",
|
|
16481
|
+
"function addServer(server):",
|
|
16482
|
+
" for i in range(virtualNodes):",
|
|
16483
|
+
' pos = hash(server + "#" + i) % 360',
|
|
16484
|
+
" ring[pos] = server",
|
|
16485
|
+
"",
|
|
16486
|
+
"function getServer(key):",
|
|
16487
|
+
" pos = hash(key) % 360",
|
|
16488
|
+
" # Find first server with pos >= key pos",
|
|
16489
|
+
" server = ring.ceiling(pos)",
|
|
16490
|
+
" if server is null:",
|
|
16491
|
+
" server = ring.first() # wrap around",
|
|
15486
16492
|
" return server",
|
|
15487
16493
|
"",
|
|
15488
16494
|
"function removeServer(server):",
|
|
@@ -17460,15 +18466,75 @@ const SQLJoinVisualizerComponent = ({
|
|
|
17460
18466
|
}) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-[10px] text-gray-400 text-center py-2", children: "No results yet" })
|
|
17461
18467
|
] })
|
|
17462
18468
|
] }),
|
|
17463
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 p-3 bg-blue-50 rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "
|
|
17464
|
-
/* @__PURE__ */ jsxRuntime.jsxs("
|
|
17465
|
-
|
|
17466
|
-
|
|
18469
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mb-4 p-3 bg-gradient-to-r from-cyan-50 to-blue-50 rounded-lg border border-cyan-200", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-4", children: [
|
|
18470
|
+
/* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 100 60", className: "w-24 h-14 flex-shrink-0", children: [
|
|
18471
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18472
|
+
"circle",
|
|
18473
|
+
{
|
|
18474
|
+
cx: "35",
|
|
18475
|
+
cy: "30",
|
|
18476
|
+
r: "22",
|
|
18477
|
+
fill: joinType === "left" || joinType === "full" ? "#06b6d4" : joinType === "inner" ? "transparent" : "#e5e7eb",
|
|
18478
|
+
fillOpacity: joinType === "left" || joinType === "full" ? 0.6 : joinType === "inner" ? 0 : 0.5,
|
|
18479
|
+
stroke: "#0891b2",
|
|
18480
|
+
strokeWidth: "2"
|
|
18481
|
+
}
|
|
18482
|
+
),
|
|
18483
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18484
|
+
"circle",
|
|
18485
|
+
{
|
|
18486
|
+
cx: "65",
|
|
18487
|
+
cy: "30",
|
|
18488
|
+
r: "22",
|
|
18489
|
+
fill: joinType === "right" || joinType === "full" ? "#06b6d4" : joinType === "inner" ? "transparent" : "#e5e7eb",
|
|
18490
|
+
fillOpacity: joinType === "right" || joinType === "full" ? 0.6 : joinType === "inner" ? 0 : 0.5,
|
|
18491
|
+
stroke: "#0891b2",
|
|
18492
|
+
strokeWidth: "2"
|
|
18493
|
+
}
|
|
18494
|
+
),
|
|
18495
|
+
/* @__PURE__ */ jsxRuntime.jsx("clipPath", { id: "leftClip", children: /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "35", cy: "30", r: "22" }) }),
|
|
18496
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
18497
|
+
"circle",
|
|
18498
|
+
{
|
|
18499
|
+
cx: "65",
|
|
18500
|
+
cy: "30",
|
|
18501
|
+
r: "22",
|
|
18502
|
+
fill: "#22c55e",
|
|
18503
|
+
fillOpacity: "0.7",
|
|
18504
|
+
clipPath: "url(#leftClip)"
|
|
18505
|
+
}
|
|
18506
|
+
),
|
|
18507
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: "22", y: "33", fontSize: "8", fill: "#0e7490", fontWeight: "bold", children: "L" }),
|
|
18508
|
+
/* @__PURE__ */ jsxRuntime.jsx("text", { x: "74", y: "33", fontSize: "8", fill: "#0e7490", fontWeight: "bold", children: "R" })
|
|
17467
18509
|
] }),
|
|
17468
|
-
|
|
17469
|
-
|
|
17470
|
-
|
|
17471
|
-
|
|
18510
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1", children: [
|
|
18511
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-sm font-semibold text-cyan-800 mb-1", children: [
|
|
18512
|
+
joinType.toUpperCase(),
|
|
18513
|
+
" JOIN"
|
|
18514
|
+
] }),
|
|
18515
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-xs text-cyan-700", children: [
|
|
18516
|
+
joinType === "inner" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18517
|
+
"Returns only rows with matches in ",
|
|
18518
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "both" }),
|
|
18519
|
+
" tables (green intersection)."
|
|
18520
|
+
] }),
|
|
18521
|
+
joinType === "left" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18522
|
+
"Returns ",
|
|
18523
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "all" }),
|
|
18524
|
+
" rows from left table + matching rows from right. NULL if no match."
|
|
18525
|
+
] }),
|
|
18526
|
+
joinType === "right" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18527
|
+
"Returns ",
|
|
18528
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "all" }),
|
|
18529
|
+
" rows from right table + matching rows from left. NULL if no match."
|
|
18530
|
+
] }),
|
|
18531
|
+
joinType === "full" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
18532
|
+
"Returns ",
|
|
18533
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "all" }),
|
|
18534
|
+
" rows from both tables. NULL where no match exists."
|
|
18535
|
+
] })
|
|
18536
|
+
] })
|
|
18537
|
+
] })
|
|
17472
18538
|
] }) }),
|
|
17473
18539
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
17474
18540
|
StatusPanel,
|
|
@@ -17559,6 +18625,8 @@ exports.StatusPanel = StatusPanel;
|
|
|
17559
18625
|
exports.StepHistory = StepHistory;
|
|
17560
18626
|
exports.TreeSetInterviewVisualizer = TreeSetInterviewVisualizer;
|
|
17561
18627
|
exports.TreeSetVisualizer = TreeSetVisualizer;
|
|
18628
|
+
exports.TrieVisualizer = TrieVisualizer;
|
|
18629
|
+
exports.UnionFindVisualizer = UnionFindVisualizer;
|
|
17562
18630
|
exports.VisualizationArea = VisualizationArea;
|
|
17563
18631
|
exports.useInterviewMode = useInterviewMode;
|
|
17564
18632
|
exports.useUrlState = useUrlState;
|