Hoi hoi!
Iβm @nyaomaru, a frontend engineer who dislikes crowded places, so I'm planning to take a quiet vacation in September. ποΈ
Have you used DSA View View already? ππ
DSA View View allows you to understand DSA by visualizing how your implementation actually runs.
But just introducing the tool is not enough.
Can it actually help us understand DSA?
In this article, we'll walk through three classic problems:
- Two Sum
- Binary Search
- Bubble Sort
We'll first understand the algorithm, and then see what is actually happening with DSA View View.
Let's learn together! πΈ
πΊοΈ Two Sum
Let's start with a very famous problem.
Given an array of numbers and a target value, find the indices of two numbers whose sum equals the target.
For example,
nums = [2, 7, 11, 15];
target = 9;
The answer is
[0, 1];
Because
2 + 7 = 9
Simple!
So, how should we find them? π€
Brute Force
The easiest approach is probably checking every possible pair.
function twoSum(nums: number[], target: number): number[] {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
return [i, j];
}
}
}
return [];
}
This works.
But if the array becomes large, we may need to compare a lot of pairs, right?
The time complexity is O(nΒ²)
Can we avoid checking the same values again and again?
Yes.
Let's use a Map.
function twoSum(nums: number[], target: number): number[] {
const seen = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
const current = nums[i];
const need = target - current;
if (seen.has(need)) {
return [seen.get(need)!, i];
}
seen.set(current, i);
}
return [];
}
The important part is this π
const need = target - current;
Instead of asking
Which two numbers should I combine?
we ask
What number do I need to complete the target?
Let's follow the example.
At first
current = 2
target = 9
need = 9 - 2
= 7
Have we already seen 7?
No.
So we remember 2.
seen = {
2 β 0
}
Next
current = 7
target = 9
need = 9 - 7
= 2
Have we already seen 2?
Yes! ππ
seen = {
2 β 0
}
So
return [0, 1];
Done!
Because we only need to walk through the array once, the time complexity becomes.
Time: O(n)
Space: O(n)
π Let's View View It
The implementation is quite small.
But when I was first learning this pattern, this part still felt a little magical.
if (seen.has(need))
- Where did
needcome from? - What is inside
seenat this moment? - Why does checking the previous values solve the problem?
This is exactly where visualization helps.
With DSA View View, we can move through the runtime one step at a time and inspect how the values change.
2
β
Need 7
β
Remember 2
β
7
β
Need 2
β
Found 2!
π
Now the Map is not just some mysterious trick.
We can actually follow the idea.
Remember what we have already seen, and check whether the value we need is there.
Nice! πΈ
π Binary Search
Next is Binary Search.
Suppose we have this sorted array
[1, 3, 5, 7, 9, 11, 13]
And we want to find
11
Of course, we could start from 1 and check every number.
1 β 3 β 5 β 7 β 9 β 11
That works.
But Binary Search does something smarter.
Instead of checking from the beginning, it checks the middle.
[1, 3, 5, 7, 9, 11, 13]
β
mid
Our middle value is 7.
We are looking for 11.
11 > 7
Because the array is sorted, we already know something very useful.
Everything on the left side of 7 is also smaller than 11.
So, we don't need that half anymore. π
[1, 3, 5, 7, 9, 11, 13]
βββββββββ
search
Now we check the middle of the remaining range.
[9, 11, 13]
β
mid
And
11 === 11
Found it! π
Here is the implementation.
function binarySearch(nums: number[], target: number): number {
let left = 0;
let right = nums.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (nums[mid] === target) {
return mid;
}
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
There are three important variables.
left
right
mid
They represent the current search range.
For our example, they start like this
left = 0
right = 6
mid = 3
[1, 3, 5, 7, 9, 11, 13]
β β β
left mid right
Because
nums[mid] < target;
we move left.
left = mid + 1;
Now
[1, 3, 5, 7, 9, 11, 13]
β β β
left mid right
And 11 is found.
Why Is Binary Search Fast?
This is the interesting part.
Each step removes about half of the remaining candidates.
If there are 1,000 values, we don't necessarily need 1,000 checks.
It becomes roughly
1000
β
500
β
250
β
125
β
...
That's why Binary Search has
Time: O(log n)
Space: O(1)
But there is one very important condition.
The data must be sorted.
Without sorted data, we cannot safely throw away half of the search range.
π Let's View View It
Binary Search is one of the algorithms that made me want a visualization tool in the first place.
The code itself is short
left = mid + 1;
or
right = mid - 1;
Easy.
But when learning it, I sometimes found myself thinking:
Wait... which part are we searching now? πΏ
When we visualize left, mid, and right, the idea becomes much easier to follow.
We are not randomly changing three numbers.
We are continuously shrinking the search area.
βββββββββββββββ
β
βββββββ
β
βββ
β
β
That's Binary Search!
Cut the unnecessary half.
Then cut it again. And again. And again.
Until we find the answer. βοΈπΈ
π«§ Bubble Sort
Finally, let's sort something!
Consider this array.
[5, 1, 4, 2, 8];
We want.
[1, 2, 4, 5, 8];
Bubble Sort repeatedly compares two neighboring values.
If they are in the wrong order, it swaps them.
Let's look at the beginning.
[5, 1, 4, 2, 8]
β β
Compare
5 > 1
So swap them.
[1, 5, 4, 2, 8]
Next
[1, 5, 4, 2, 8]
β β
Again
5 > 4
Swap!
[1, 4, 5, 2, 8]
And continue.
[1, 4, 5, 2, 8]
β β
5 > 2
Swap!
[1, 4, 2, 5, 8]
Eventually, larger values move toward the end of the array.
They kind of...
bubble up. π«§
That's why it is called Bubble Sort.
Here is a simple implementation:
function bubbleSort(nums: number[]): number[] {
for (let i = 0; i < nums.length - 1; i++) {
for (let j = 0; j < nums.length - i - 1; j++) {
if (nums[j] > nums[j + 1]) {
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
}
}
}
return nums;
}
We repeatedly compare
nums[j];
And
nums[j + 1];
and swap them when necessary.
After one full pass, the largest remaining value reaches its correct position near the end.
So on the next pass, we don't need to check that position again.
That's why the inner loop contains.
nums.length - i - 1;
Complexity
Bubble Sort isn't very fast for large arrays.
Its time complexity is
Time: O(nΒ²)
Space: O(1)
So I probably won't suddenly replace production sorting with Bubble Sort tomorrow. πΈ
But as a learning example, I really like it.
Why?
Because you can see the algorithm working.
π Let's View View It
This is probably the most visually satisfying one of the three.
Instead of only reading
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
we can follow the values moving through the array.
[5, 1, 4, 2, 8]
β swap
[1, 5, 4, 2, 8]
β swap
[1, 4, 5, 2, 8]
β swap
[1, 4, 2, 5, 8]
Then another pass begins.
The code contains nested loops, indexes, comparisons, and swaps.
But visually, the basic rule is extremely simple:
Compare neighbors. If the left one is bigger, swap them.
Repeat. Repeat. Repeat.
Sorted! π
π§ What Did We Actually Learn?
These three problems look quite different.
But each one introduces a useful way of thinking.
Two Sum
Remember information from previous steps.
Have I already seen what I need?
Binary Search
Use what we already know to remove impossible candidates.
Can I safely discard half of the search space?
Bubble Sort
Break a larger problem into many small comparisons.
Are these two values in the correct order?
This is one of the things I find interesting about learning DSA.
At first, the implementation can look like a collection of indexes, loops, conditions, and mysterious variables.
But behind the code, there is usually a much simpler idea.
And sometimes I don't fully understand that idea just by staring at the code.
I want to view it. ππ
π― Conclusion
In this article, we looked at three classic algorithms:
- Two Sum with a
Map - Binary Search
- Bubble Sort
And more importantly, we looked at how the data changes while they run.
I think this is where visualization can be especially useful.
- Reading the final implementation tells us what the code is.
- Stepping through it helps us understand why it works.
That's exactly why I built DSA View View.
You can write or load a TypeScript implementation, run it with your own inputs, and move backward and forward through the runtime.
If you are learning DSA too, try taking a problem you already solved and viewing it step by step.
You may notice something you didn't notice when only reading the code. π
And if there is a DSA problem you want me to cover next, please let me know in the comments!
I still have many algorithms to learn myself. πΈ
Let's train our DSA muscles together! πͺ
If you like DSA View View, please give it a star β
nyaomaru
/
dsa-view-view
DSA View View allows you to understand DSA to see the data flow. ππ Of course, it's free.
DSA View View
DSA View View turns TypeScript algorithm functions into step-by-step visual stories. ππ
Write code, run it with structured inputs, and see the arrays, matrices trees, lists, stacks, pointers, and return values move as the function executes.
It is built for those moments when reading the code is not enough and you want to view why the answer changes.
Why Try It?
-
π§ Step through real TypeScript
Paste or edit a function, validate it, then run the exact code in the browser. -
π§© Views that match the data
Arrays become bars, matrices become grids, trees become node graphs, linked lists become chains, and two-pointer area problems get their own visual view. -
π³ DSA-friendly inputs out of the box
TreeNode,ListNode,MinHeap,MaxHeap,PriorityQueue, nested arrays matrices, strings, numbers, and class-style inputs are supported without ceremony. -
π 39 built-in examples
Search by name, browseβ¦
And my DSA View View has launched at TinyLaunch! π Please take a loot π
See you in the next article!







Top comments (9)
The reframe from "which two numbers combine?" to "what do I need right now?" is the actual insight worth internalizing, and it transfers well beyond Two Sum β you see the same shift in a lot of graph traversal problems where caching visited nodes changes the question from "have I found it?" to "have I been here before?". What I've noticed on engineering teams is that developers who get the LLM to write the O(n) version often can't explain why it works, and when the interviewer or code reviewer asks them to trace it by hand they're stuck. A tool that forces you to step through
seenat each iteration closes that gap in a way that just reading the solution never does.Thatβs a great insight. πΊ
Reframing the question is a transferable skill that goes far beyond DSA and applies to everyday engineering problems as well.
AI can generate working code, but without understanding why it works, debugging and maintaining it becomes much harder. In the AI era, being able to trace and explain our reasoning may become more important than ever.
The debugging point holds, and there's a concrete mechanism behind it. Someone who cannot reconstruct why a two-pointer approach works for Two Sum on a sorted array will reach for the same pattern when the array is unsorted, or when duplicates are allowed β and get silent wrong answers instead of an error. The model optimized for "passes the given examples," not "handles the invariant." That gap is exactly what reasoning closes: not as an abstract exercise, but as the thing that lets you change one constraint without breaking everything else.
That βhandles the invariantβ point is exactly what scares me. πΉ
I feel like training our DSA muscles gives us a better chance of dealing with uncertainty when constraints change, instead of only knowing how to make one example pass.
Of course, DSA alone is not enough. But I do think strong fundamentals remain important no matter how much the tools around us change.
Thatβs one of the reasons I want to keep learning it too. πͺπΈ
You're right that the transfer value is real. The failure mode I've hit most often is not that someone can't recognize a problem as "binary search shaped" β it's that they can't articulate the pre-condition that makes binary search valid. When requirements shift mid-project (sorted β partially sorted, sequential β concurrent), that articulation is exactly what tells you whether your solution still holds or needs to be rebuilt from scratch. The people who can name the invariant are the ones who catch the breakage in review instead of production.
I think thatβs exactly where a lot of the difficulty of software engineering lives.
We donβt just build something once and finish. The system keeps changing, the requirements keep changing, and assumptions that were correct at one point can become wrong later.
That constant need to adapt is what makes software engineering both interesting and difficult.
In production, I think what weβre really looking for is not just βa correct answer,β but the right answer inside all of that changing complexity.
And honestly, I find that difficult all the time too. πΉ
This is a strong reminder that dsa, typescript, opensource, learning need an observable contract. The happy path is rarely the expensive part; it is the boundary behavior, stale state, and partial failure path that decide whether the design holds up in production.
Thanks for the thoughtful comment! πΈ
Learning DSA doesnβt translate directly into production quality, but I believe the fundamentals help develop the reasoning skills needed to understand edge cases, stale state, and partial failures.
Exactly. The lasting value is not memorizing a particular solution but learning to state the constraints, test the edge cases, and notice when an apparently simple transformation changes the problem.