Solution code
const REVERSED_HEAD_CLASS = 'reversed_head';
const CURR_CLASS = 'curr';
const NEXT_CLASS = 'next';
const REVERSE_CLASS = 'reverse';
const HIDDEN_LINE_CLASS = 'hidden_line';
const REVERSED_HEAD_COLOR = '#d4b106';
const CURR_COLOR = 'blue';
const NEXT_COLOR = 'orange';
function visMainFunction(inputHead) {
initClassProperties();
explanationAndColorSchemaNotifications();
const head = convertInputToLinkedList(inputHead);
let reversedListHead = null;
let current = head;
const animation = new Animation(head);
while (current !== null) {
animation.next();
const nextNode = current.next;
current.next = reversedListHead;
reversedListHead = current;
current = nextNode;
}
notification(`
π <b>Done</b><br>
${makeColoredSpan('current', CURR_COLOR)} is <b>null</b>; ${makeColoredSpan('reversedListHead', REVERSED_HEAD_COLOR)} now points to the new head of the reversed list.<br>
Return <code>reversedListHead</code>.
`);
return reversedListHead;
}
class Animation {
constructor(originalHead) {
startPartialBatch();
const head = copyLinkedList(originalHead);
this.list = new VisLinkedList(head, { dataPropName: 'val' }).createVis(true);
this.reversedListHead = null;
this.current = head;
this.highlightCurrent();
notification(`
π <b>Setup</b><br>
${makeColoredSpan('current', CURR_COLOR)} starts at the head; ${makeColoredSpan('reversedListHead', REVERSED_HEAD_COLOR)} is <b>null</b> (no reversed nodes yet).<br>
We will reverse links <i>in-place</i>, one edge at a time.
`);
endBatch();
}
next() {
startPartialBatch();
this.highlightNext();
notification(`
ποΈ Save ${makeColoredSpan('next', NEXT_COLOR)} = <code>current.next</code> first,<br>
so we donβt lose access to the rest of the list when we flip the arrow from ${makeColoredSpan('current', CURR_COLOR)}.
`);
notification(`
β©οΈ Reverse the arrow:<br>
Set <code>current.next = reversedListHead</code> so ${makeColoredSpan('current', CURR_COLOR)} now points back to ${makeColoredSpan('reversedListHead', REVERSED_HEAD_COLOR)}.
`);
this.hideCurrentToNext();
this.reverseReversedHeadToCurrent();
notification(`
βΆοΈ Advance pointers in order:<br>
1) ${makeColoredSpan('reversedListHead', REVERSED_HEAD_COLOR)} = <i>old</i> ${makeColoredSpan('current', CURR_COLOR)}<br>
2) ${makeColoredSpan('current', CURR_COLOR)} = ${makeColoredSpan('next', NEXT_COLOR)} (the saved node)
`);
this.unhighlightReversedListHead();
this.reversedListHead = this.current;
this.highlightReversedListHead();
this.current = this.current.next;
this.highlightCurrent();
notification(`
β»οΈ Loop invariant:<br>
β’ The prefix ending at ${makeColoredSpan('reversedListHead', REVERSED_HEAD_COLOR)} is fully <b>reversed</b>.<br>
β’ ${makeColoredSpan('current', CURR_COLOR)} is the first node of the <b>unprocessed</b> suffix.<br>
Continue until ${makeColoredSpan('current', CURR_COLOR)} becomes <b>null</b>.
`);
endBatch();
}
reverseReversedHeadToCurrent() {
this.reversedListHead && resetEdgeClass(this.list, this.reversedListHead, this.current, REVERSE_CLASS);
}
hideCurrentToNext() {
this.current && this.current.next && resetEdgeClass(this.list, this.current, this.current.next, HIDDEN_LINE_CLASS);
}
highlightNext() {
resetNodeClass(this.list, this.current.next, NEXT_CLASS, false);
}
unhighlightReversedListHead() {
resetNodeClass(this.list, this.reversedListHead, null, true);
}
highlightReversedListHead() {
resetNodeClass(this.list, this.reversedListHead, REVERSED_HEAD_CLASS, true);
}
highlightCurrent() {
resetNodeClass(this.list, this.current, CURR_CLASS, true);
}
}
function resetNodeClass(list, node, newClass, clearOld=true) {
if (!node) return;
const visManager = list.makeVisManagerForNode(node);
clearOld && visManager.removeAllClasses();
newClass && visManager.addClass(newClass);
}
function resetEdgeClass(list, src, tgt, newClass) {
const visManager = list.makeVisManagerForEdge(src, tgt);
visManager.removeAllClasses();
visManager.addClass(newClass);
}
function initClassProperties() {
setClassProperties(REVERSED_HEAD_CLASS, { backgroundColor: REVERSED_HEAD_COLOR });
setClassProperties(CURR_CLASS, { backgroundColor: CURR_COLOR });
setClassProperties(NEXT_CLASS, { backgroundColor: NEXT_COLOR });
setClassProperties(REVERSE_CLASS, { lineDirection: 'reverse' });
setClassProperties(HIDDEN_LINE_CLASS, { lineWidth: 0 });
}
function convertInputToLinkedList(arr) {
const dummy = new ListNode(0);
let current = dummy;
for (const val of arr) {
current.next = new ListNode(val);
current = current.next;
}
return dummy.next;
}
function copyLinkedList(head) {
if (!head) return null;
const dummy = {};
let current = head;
let copyCurrent = dummy;
while (current) {
const newNode = { val: current.val, next: null };
copyCurrent.next = newNode;
copyCurrent = newNode;
current = current.next;
}
return dummy.next;
}
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val);
this.next = (next===undefined ? null : next);
}
function explanationAndColorSchemaNotifications() {
explanationNotification();
notification(`
π¨ <b>Color Schema</b><br><br>
<b>${makeColoredSpan('reversedListHead', REVERSED_HEAD_COLOR)}</b> β head of the already <b>reversed prefix</b> (grows each step).<br>
<b>${makeColoredSpan('current', CURR_COLOR)}</b> β the node currently being processed (its <code>.next</code> will be flipped).<br>
<b>${makeColoredSpan('next', NEXT_COLOR)}</b> β temporary save of <code>current.next</code> to preserve access to the remaining list.<br><br>
β©οΈ <b>Reversed edge</b> β shows the arrow flipped as part of the reversing process.
`);
}