Solution code
const LEFT = 'left', LEFT_COLOR = 'orange';
const RIGHT = 'right', RIGHT_COLOR = 'blue';
const MIDDLE = 'middle', MID_COLOR = 'gray';
const MATCH_COLOR = 'green';
function visMainFunction(inputNums, target) {
initClassProperties();
startBatch();
const nums = VisArray.from(inputNums);
let left = 0;
let right = nums.length - 1;
makeVisVariable(target).options({label: 'target'}).createVis(true);
makeVisVariable(left).registerAsIndexPointer(nums, LEFT);
makeVisVariable(right).registerAsIndexPointer(nums, RIGHT);
endBatch();
explanationAndColorPaletteNotificaitons()
notification(`
π Weβre searching a <b>rotated sorted array</b> using binary search.<br/>
We keep three pointers:<br/>
β’ <b>${makeColoredSpan('left', LEFT_COLOR)}</b> β the start index of the current search range.<br/>
β’ <b>${makeColoredSpan('right', RIGHT_COLOR)}</b> β the end index of the current search range.<br/>
β’ <b>${makeColoredSpan('mid', MID_COLOR)}</b> β the middle index between <b>${makeColoredSpan('left', LEFT_COLOR)}</b> and <b>${makeColoredSpan('right', RIGHT_COLOR)}</b>.<br/><br/>
At each step, we first check:<br/>
β’ If <code>nums[${makeColoredSpan('mid', MID_COLOR)}] === target</code>, we have found the answer and return <b>${makeColoredSpan('mid', MID_COLOR)}</b> immediately.<br/><br/>
Otherwise, we compare <code>nums[${makeColoredSpan('mid', MID_COLOR)}]</code> with <code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code> to determine which segment <b>${makeColoredSpan('mid', MID_COLOR)}</b> lies in:<br/>
β’ If <code>nums[${makeColoredSpan('mid', MID_COLOR)}] β€ nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code>, then <b>${makeColoredSpan('mid', MID_COLOR)} is in the second sorted segment</b>, because every value in the first sorted segment is greater than <code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code>. Therefore, the <b>right half ([${makeColoredSpan('mid', MID_COLOR)}..${makeColoredSpan('right', RIGHT_COLOR)}])</b> is <b>assured to be sorted</b>.<br/>
β’ Otherwise (if <code>nums[${makeColoredSpan('mid', MID_COLOR)}] > nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code>), <b>${makeColoredSpan('mid', MID_COLOR)} is in the first sorted segment</b>, because no value in the second sorted segment can exceed <code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code>. Therefore, the <b>left half ([${makeColoredSpan('left', LEFT_COLOR)}..${makeColoredSpan('mid', MID_COLOR)}])</b> is <b>assured to be sorted</b>.<br/><br/>
Then we range-check the <b>half assured to be sorted</b>: if the target lies within its bounds, continue there; otherwise, search the other half.
`);
while (left <= right) {
startPartialBatch();
const mid = Math.floor((left + right) / 2);
const midVal = nums[mid];
const rightVal = nums[right];
const leftVal = nums[left];
const midVisManager = nums.makeVisManagerForIndex(mid);
midVisManager.addClass(MIDDLE);
notification(`
π Checking <b>${makeColoredSpan('mid', MID_COLOR)}</b> ${makeColoredSpan(mid, MID_COLOR)}:
<code>nums[${makeColoredSpan('mid', MID_COLOR)}]</code> = <b>${makeColoredSpan(midVal, MID_COLOR)}</b>.
`);
if (target === midVal) {
midVisManager.setProp('backgroundColor', MATCH_COLOR);
notification(`β
Found target <b>${makeColoredSpan(target, MATCH_COLOR)}</b> at index <b>${makeColoredSpan(mid, MID_COLOR)}</b>.`);
return mid;
}
if (midVal <= rightVal) {
notification(`
π§ Since <code>nums[${makeColoredSpan('mid', MID_COLOR)}]</code> (${makeColoredSpan(midVal, MID_COLOR)}) β€
<code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code> (${makeColoredSpan(rightVal, RIGHT_COLOR)}),
<b>${makeColoredSpan('mid', MID_COLOR)} is in the second sorted segment</b> (all first-segment values are > <code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code>).<br/>
Therefore, the <b>right half ([${makeColoredSpan('mid', MID_COLOR)}..${makeColoredSpan('right', RIGHT_COLOR)}])</b> is <b>assured to be sorted</b>.
`);
if (target > midVal && target <= rightVal) {
notification(`
π― The target <b>${makeColoredSpan(target, MATCH_COLOR)}</b> lies within the <b>assured-sorted</b>
<b>right half ([${makeColoredSpan('mid', MID_COLOR)}..${makeColoredSpan('right', RIGHT_COLOR)}])</b>, i.e.,
<b>(${makeColoredSpan(midVal, MID_COLOR)}, ${makeColoredSpan(rightVal, RIGHT_COLOR)}]</b>.<br/>
β‘οΈ Continue in this half: set <b>${makeColoredSpan('left', LEFT_COLOR)} = ${makeColoredSpan('mid', MID_COLOR)} + 1</b>.
`);
left = mid + 1;
} else {
notification(`
π« The target <b>${makeColoredSpan(target, MATCH_COLOR)}</b> is <b>outside</b> the <b>assured-sorted</b>
<b>right half ([${makeColoredSpan('mid', MID_COLOR)}..${makeColoredSpan('right', RIGHT_COLOR)}])</b>, i.e.,
<b>(${makeColoredSpan(midVal, MID_COLOR)}, ${makeColoredSpan(rightVal, RIGHT_COLOR)}]</b>.<br/>
β¬
οΈ Search the <b>other half</b>: set <b>${makeColoredSpan('right', RIGHT_COLOR)} = ${makeColoredSpan('mid', MID_COLOR)} - 1</b>.
`);
right = mid - 1;
}
} else {
notification(`
π§ Since <code>nums[${makeColoredSpan('mid', MID_COLOR)}]</code> (${makeColoredSpan(midVal, MID_COLOR)}) >
<code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code> (${makeColoredSpan(rightVal, RIGHT_COLOR)}),
<b>${makeColoredSpan('mid', MID_COLOR)} is in the first sorted segment</b> (no value in the second sorted segment can be greater than <code>nums[${makeColoredSpan('right', RIGHT_COLOR)}]</code>).<br/>
Therefore, the <b>left half ([${makeColoredSpan('left', LEFT_COLOR)}..${makeColoredSpan('mid', MID_COLOR)}])</b> is <b>assured to be sorted</b>.
`);
if (target >= leftVal && target < midVal) {
notification(`
π― The target <b>${makeColoredSpan(target, MATCH_COLOR)}</b> lies within the <b>assured-sorted</b>
<b>left half ([${makeColoredSpan('left', LEFT_COLOR)}..${makeColoredSpan('mid', MID_COLOR)}])</b>, i.e.,
<b>[${makeColoredSpan(leftVal, LEFT_COLOR)}, ${makeColoredSpan(midVal, MID_COLOR)})</b>.<br/>
β¬
οΈ Continue in this half: set <b>${makeColoredSpan('right', RIGHT_COLOR)} = ${makeColoredSpan('mid', MID_COLOR)} - 1</b>.
`);
right = mid - 1;
} else {
notification(`
π« The target <b>${makeColoredSpan(target, MATCH_COLOR)}</b> is <b>outside</b> the <b>assured-sorted</b>
<b>left half ([${makeColoredSpan('left', LEFT_COLOR)}..${makeColoredSpan('mid', MID_COLOR)}])</b>, i.e.,
<b>[${makeColoredSpan(leftVal, LEFT_COLOR)}, ${makeColoredSpan(midVal, MID_COLOR)})</b>.<br/>
β‘οΈ Search the <b>other half</b>: set <b>${makeColoredSpan('left', LEFT_COLOR)} = ${makeColoredSpan('mid', MID_COLOR)} + 1</b>.
`);
left = mid + 1;
}
}
midVisManager.removeClass(MIDDLE);
endBatch();
}
notification(`β Target <b>${makeColoredSpan(target, MATCH_COLOR)}</b> is <b>not found</b> in <code>nums</code>.`);
return -1;
}
function explanationAndColorPaletteNotificaitons() {
explanationNotification();
notification(`
π¨ <b>Color Palette & Pointer Legend</b><br/>
β’ ${makeColoredSpan('left', LEFT_COLOR)} β <i>left pointer</i> color for both <b>left index</b> and <b>left value</b>.<br/>
β’ ${makeColoredSpan('right', RIGHT_COLOR)} β <i>right pointer</i> color for both <b>right index</b> and <b>right value</b>.<br/>
β’ ${makeColoredSpan('mid', MID_COLOR)} β <i>mid pointer</i> color for both <b>mid index</b> and <b>mid value</b>.<br/>
β’ ${makeColoredSpan('target / match', MATCH_COLOR)} β highlight color when a value matches the target.<br/>
`);
}
function initClassProperties() {
setClassProperties(RIGHT, { backgroundColor: RIGHT_COLOR });
setClassProperties(LEFT, { backgroundColor: LEFT_COLOR });
setClassProperties(MIDDLE,{ backgroundColor: MID_COLOR });
}