diff --git a/algorithm/category.json b/algorithm/category.json index 2cd49020..81b8abb0 100644 --- a/algorithm/category.json +++ b/algorithm/category.json @@ -6,6 +6,12 @@ "bfs": "BFS" } }, + "search": { + "name": "Search", + "list": { + "binary_search": "Binary Search" + } + }, "sorting": { "name": "Sorting", "list": { diff --git a/algorithm/search/binary_search/desc.json b/algorithm/search/binary_search/desc.json new file mode 100644 index 00000000..7fe58532 --- /dev/null +++ b/algorithm/search/binary_search/desc.json @@ -0,0 +1,18 @@ +{ + "Binary Search": "Binary Search is a search algorithm that finds the position of a target value within a sorted array. It works by comparing the target value to the middle element of the array; if they are unequal, the lower or upper half of the array is eliminated depending on the result and the search is repeated in the remaining subarray until it is successful.", + "Applications": [ + "Finding values in a sorted collection", + "Traversing binary search trees" + ], + "Complexity": { + "time": "worst O(log(N)), best O(1), average O(log(N))", + "space": "worst O(log(N)) - recursive, O(1) - iterative" + }, + "References": [ + "Wikipedia" + ], + "files": { + "recursive": "Recursively searching a sorted array", + "iterative": "Iteratively searching a sorted array" + } +} \ No newline at end of file diff --git a/algorithm/search/binary_search/iterative/code.js b/algorithm/search/binary_search/iterative/code.js new file mode 100644 index 00000000..aa1a558a --- /dev/null +++ b/algorithm/search/binary_search/iterative/code.js @@ -0,0 +1,44 @@ +function BinarySearch(array, element) { // array = sorted array, element = element to be found, + + var minIndex = 0; + var maxIndex = array.length - 1; + var currentIndex; + var testElement; + + while (minIndex <= maxIndex) { + + middleIndex = Math.floor((minIndex + maxIndex) / 2); + testElement = array[middleIndex]; + + tracer._print('Searching at index: ' + middleIndex); + tracer._notify(middleIndex); + + if (testElement < element) { + + tracer._print('Going right.'); + minIndex = middleIndex + 1; + + } else if (testElement > element) { + + tracer._print('Going left.'); + maxIndex = middleIndex - 1; + + } else { + + tracer._print(element + ' is found at position ' + middleIndex + '!'); + tracer._select(middleIndex); + + return middleIndex; + } + } + + tracer._print(element + ' is not found!'); + return -1; +} + +var element = D[0]; + +tracer._sleep(1000); +tracer._pace(1000); +tracer._print('Using iterative binary search to find ' + element); +BinarySearch(D, element, 0, D.length - 1); \ No newline at end of file diff --git a/algorithm/search/binary_search/iterative/data.js b/algorithm/search/binary_search/iterative/data.js new file mode 100644 index 00000000..a26af590 --- /dev/null +++ b/algorithm/search/binary_search/iterative/data.js @@ -0,0 +1,3 @@ +var tracer = new Array1DTracer(); +var D = Array1D.randomSorted(15, 0, 50); +tracer._setData(D); \ No newline at end of file diff --git a/algorithm/search/binary_search/recursive/code.js b/algorithm/search/binary_search/recursive/code.js new file mode 100644 index 00000000..2a839ff3 --- /dev/null +++ b/algorithm/search/binary_search/recursive/code.js @@ -0,0 +1,38 @@ +function BinarySearch(array, element, minIndex, maxIndex) { // array = sorted array, element = element to be found, minIndex = minIndex index, maxIndex = maxIndex index + if (minIndex > maxIndex) { + tracer._print(element + ' is not found!'); + return -1; + } + + var middleIndex = Math.floor((minIndex + maxIndex) / 2); + var testElement = array[middleIndex]; + + tracer._print('Searching at index: ' + middleIndex); + tracer._notify(middleIndex); + + if (testElement < element) { + tracer._print('Going right.'); + return BinarySearch(array, element, middleIndex + 1, maxIndex); + } + + if (testElement > element) { + tracer._print('Going left.'); + return BinarySearch(array, element, minIndex, middleIndex - 1); + } + + if (testElement === element) { + tracer._print(element + ' is found at position ' + middleIndex + '!'); + tracer._select(middleIndex); + return middleIndex; + } + + tracer._print(element + ' is not found!'); + return -1; +} + +var element = D[0]; + +tracer._sleep(1000); +tracer._pace(1000); +tracer._print('Using binary search to find ' + element); +BinarySearch(D, element, 0, D.length - 1); \ No newline at end of file diff --git a/algorithm/search/binary_search/recursive/data.js b/algorithm/search/binary_search/recursive/data.js new file mode 100644 index 00000000..a26af590 --- /dev/null +++ b/algorithm/search/binary_search/recursive/data.js @@ -0,0 +1,3 @@ +var tracer = new Array1DTracer(); +var D = Array1D.randomSorted(15, 0, 50); +tracer._setData(D); \ No newline at end of file diff --git a/js/module/array1d.js b/js/module/array1d.js index 616f6311..390c5522 100644 --- a/js/module/array1d.js +++ b/js/module/array1d.js @@ -6,18 +6,21 @@ Array1DTracer.prototype = Object.create(Array2DTracer.prototype); Array1DTracer.prototype.constructor = Array1DTracer; var Array1D = { - random: function (N, min, max) { + random: function(N, min, max) { return Array2D.random(1, N, min, max)[0]; + }, + randomSorted: function(N, min, max) { + return Array2D.randomSorted(1, N, min, max)[0]; } }; // Override -Array1DTracer.prototype._setData = function (D) { +Array1DTracer.prototype._setData = function(D) { return Array2DTracer.prototype._setData.call(this, [D]); }; // Override -Array1DTracer.prototype._notify = function (idx1, idx2) { +Array1DTracer.prototype._notify = function(idx1, idx2) { if (idx2 === undefined) { Array2DTracer.prototype._notify.call(this, 0, idx1); } else { @@ -26,7 +29,7 @@ Array1DTracer.prototype._notify = function (idx1, idx2) { }; // Override -Array1DTracer.prototype._select = function (s, e) { +Array1DTracer.prototype._select = function(s, e) { if (e === undefined) { Array2DTracer.prototype._select.call(this, 0, s); } else { @@ -35,16 +38,19 @@ Array1DTracer.prototype._select = function (s, e) { }; // Override -Array1DTracer.prototype._selectSet = function (indexes) { +Array1DTracer.prototype._selectSet = function(indexes) { var coords = []; - indexes.forEach(function (index) { - coords.push({x: 0, y: index}); + indexes.forEach(function(index) { + coords.push({ + x: 0, + y: index + }); }); Array2DTracer.prototype._selectSet.call(this, coords); }; // Override -Array1DTracer.prototype._deselect = function (s, e) { +Array1DTracer.prototype._deselect = function(s, e) { if (e === undefined) { Array2DTracer.prototype._deselect.call(this, 0, s); } else { @@ -53,10 +59,13 @@ Array1DTracer.prototype._deselect = function (s, e) { }; // Override -Array1DTracer.prototype._deselectSet = function (indexes) { +Array1DTracer.prototype._deselectSet = function(indexes) { var coords = []; - indexes.forEach(function (index) { - coords.push({x: 0, y: index}); + indexes.forEach(function(index) { + coords.push({ + x: 0, + y: index + }); }); Array2DTracer.prototype._deselectSet.call(this, coords); }; \ No newline at end of file diff --git a/js/module/array2d.js b/js/module/array2d.js index a465683c..45cee352 100644 --- a/js/module/array2d.js +++ b/js/module/array2d.js @@ -12,21 +12,21 @@ Array2DTracer.prototype = Object.create(Tracer.prototype); Array2DTracer.prototype.constructor = Array2DTracer; // Override -Array2DTracer.prototype.resize = function () { +Array2DTracer.prototype.resize = function() { Tracer.prototype.resize.call(this); this.refresh(); }; // Override -Array2DTracer.prototype.clear = function () { +Array2DTracer.prototype.clear = function() { Tracer.prototype.clear.call(this); clearTableColor(); }; var Array2D = { - random: function (N, M, min, max) { + random: function(N, M, min, max) { if (!N) N = 10; if (!M) M = 10; if (min === undefined) min = 1; @@ -39,11 +39,19 @@ var Array2D = { } } return D; + }, + + randomSorted: function(N, M, min, max) { + return this.random(N, M, min, max).map(function(arr) { + return arr.sort(function(a, b) { + return a - b; + }); + }); } }; // Override -Array2DTracer.prototype._setData = function (D) { +Array2DTracer.prototype._setData = function(D) { this.D = D; this.viewX = this.viewY = 0; this.paddingH = 6; @@ -51,8 +59,8 @@ Array2DTracer.prototype._setData = function (D) { this.fontSize = 16; if (Tracer.prototype._setData.call(this, arguments)) { - $('.mtbl-row').each(function (i) { - $(this).children().each(function (j) { + $('.mtbl-row').each(function(i) { + $(this).children().each(function(j) { $(this).text(D[i][j]); }); }); @@ -75,47 +83,65 @@ Array2DTracer.prototype._setData = function (D) { return false; }; -Array2DTracer.prototype._notify = function (x1, y1, x2, y2) { +Array2DTracer.prototype._notify = function(x1, y1, x2, y2) { var second = x2 !== undefined && y2 !== undefined; - this.pushStep({type: 'notifying', x: x1, y: y1, value: this.D[x1][y1]}, !second); - if (second) this.pushStep({type: 'notifying', x: x2, y: y2, value: this.D[x2][y2]}, true); - this.pushStep({type: 'notified', x: x1, y: y1}, false); - if (second) this.pushStep({type: 'notified', x: x2, y: y2}, false); + this.pushStep({ + type: 'notifying', + x: x1, + y: y1, + value: this.D[x1][y1] + }, !second); + if (second) this.pushStep({ + type: 'notifying', + x: x2, + y: y2, + value: this.D[x2][y2] + }, true); + this.pushStep({ + type: 'notified', + x: x1, + y: y1 + }, false); + if (second) this.pushStep({ + type: 'notified', + x: x2, + y: y2 + }, false); }; -Array2DTracer.prototype._select = function (sx, sy, ex, ey) { +Array2DTracer.prototype._select = function(sx, sy, ex, ey) { this.pushSelectingStep('select', null, arguments); }; -Array2DTracer.prototype._selectRow = function (x, sy, ey) { +Array2DTracer.prototype._selectRow = function(x, sy, ey) { this.pushSelectingStep('select', 'row', arguments); }; -Array2DTracer.prototype._selectCol = function (y, sx, ex) { +Array2DTracer.prototype._selectCol = function(y, sx, ex) { this.pushSelectingStep('select', 'col', arguments); }; -Array2DTracer.prototype._selectSet = function (coords) { +Array2DTracer.prototype._selectSet = function(coords) { this.pushSelectingStep('select', 'set', arguments); }; -Array2DTracer.prototype._deselect = function (sx, sy, ex, ey) { +Array2DTracer.prototype._deselect = function(sx, sy, ex, ey) { this.pushSelectingStep('deselect', null, arguments); }; -Array2DTracer.prototype._deselectRow = function (x, sy, ey) { +Array2DTracer.prototype._deselectRow = function(x, sy, ey) { this.pushSelectingStep('deselect', 'row', arguments); }; -Array2DTracer.prototype._deselectCol = function (y, sx, ex) { +Array2DTracer.prototype._deselectCol = function(y, sx, ex) { this.pushSelectingStep('deselect', 'col', arguments); }; -Array2DTracer.prototype._deselectSet = function (coords) { +Array2DTracer.prototype._deselectSet = function(coords) { this.pushSelectingStep('deselect', 'set', arguments); }; -Array2DTracer.prototype.pushSelectingStep = function () { +Array2DTracer.prototype.pushSelectingStep = function() { var args = Array.prototype.slice.call(arguments); var type = args.shift(); var mode = args.shift(); @@ -123,27 +149,47 @@ Array2DTracer.prototype.pushSelectingStep = function () { var coord; switch (mode) { case 'row': - coord = {x: args[0], sy: args[1], ey: args[2]}; + coord = { + x: args[0], + sy: args[1], + ey: args[2] + }; break; case 'col': - coord = {y: args[0], sx: args[1], ex: args[2]}; + coord = { + y: args[0], + sx: args[1], + ex: args[2] + }; break; case 'set': - coord = {coords: args[0]}; + coord = { + coords: args[0] + }; break; default: if (args[2] === undefined && args[3] === undefined) { - coord = {x: args[0], y: args[1]}; + coord = { + x: args[0], + y: args[1] + }; } else { - coord = {sx: args[0], sy: args[1], ex: args[2], ey: args[3]}; + coord = { + sx: args[0], + sy: args[1], + ex: args[2], + ey: args[3] + }; } } - var step = {type: type}; + var step = { + type: type + }; $.extend(step, coord); this.pushStep(step, type == 'select'); }; -Array2DTracer.prototype.processStep = function (step, options) { +Array2DTracer.prototype.processStep = function(step, options) { switch (step.type) { case 'notifying': var $row = $table.find('.mtbl-row').eq(step.x); @@ -154,7 +200,7 @@ Array2DTracer.prototype.processStep = function (step, options) { var colorClass = step.type == 'select' || step.type == 'deselect' ? tableColorClass.selected : tableColorClass.notifying; var addClass = step.type == 'select' || step.type == 'notifying'; if (step.coords) { - step.coords.forEach(function (coord) { + step.coords.forEach(function(coord) { var x = coord.x; var y = coord.y; paintColor(x, y, x, y, colorClass, addClass); @@ -174,7 +220,7 @@ Array2DTracer.prototype.processStep = function (step, options) { } }; -Array2DTracer.prototype.getCellCss = function () { +Array2DTracer.prototype.getCellCss = function() { return { padding: this.paddingV.toFixed(1) + 'px ' + this.paddingH.toFixed(1) + 'px', 'font-size': this.fontSize.toFixed(1) + 'px' @@ -182,7 +228,7 @@ Array2DTracer.prototype.getCellCss = function () { }; // Override -Array2DTracer.prototype.refresh = function () { +Array2DTracer.prototype.refresh = function() { Tracer.prototype.refresh.call(this); var $parent = $table.parent(); @@ -193,7 +239,7 @@ Array2DTracer.prototype.refresh = function () { }; // Override -Array2DTracer.prototype.prevStep = function () { +Array2DTracer.prototype.prevStep = function() { this.clear(); $('#tab_trace .wrapper').empty(); var finalIndex = this.traceIndex - 1; @@ -202,13 +248,15 @@ Array2DTracer.prototype.prevStep = function () { return; } for (var i = 0; i < finalIndex; i++) { - this.step(i, {virtual: true}); + this.step(i, { + virtual: true + }); } this.step(finalIndex); }; // Override -Array2DTracer.prototype.mousedown = function (e) { +Array2DTracer.prototype.mousedown = function(e) { Tracer.prototype.mousedown.call(this, e); this.dragX = e.pageX; @@ -217,7 +265,7 @@ Array2DTracer.prototype.mousedown = function (e) { }; // Override -Array2DTracer.prototype.mousemove = function (e) { +Array2DTracer.prototype.mousemove = function(e) { Tracer.prototype.mousemove.call(this, e); if (this.dragging) { @@ -230,14 +278,14 @@ Array2DTracer.prototype.mousemove = function (e) { }; // Override -Array2DTracer.prototype.mouseup = function (e) { +Array2DTracer.prototype.mouseup = function(e) { Tracer.prototype.mouseup.call(this, e); this.dragging = false; }; // Override -Array2DTracer.prototype.mousewheel = function (e) { +Array2DTracer.prototype.mousewheel = function(e) { Tracer.prototype.mousewheel.call(this, e); e.preventDefault(); @@ -255,12 +303,12 @@ Array2DTracer.prototype.mousewheel = function (e) { this.refresh(); }; -var initTable = function () { +var initTable = function() { $table = $('
'); $module_container.append($table); }; -var paintColor = function (sx, sy, ex, ey, colorClass, addClass) { +var paintColor = function(sx, sy, ex, ey, colorClass, addClass) { for (var i = sx; i <= ex; i++) { var $row = $table.find('.mtbl-row').eq(i); for (var j = sy; j <= ey; j++) { @@ -271,7 +319,7 @@ var paintColor = function (sx, sy, ex, ey, colorClass, addClass) { } }; -var clearTableColor = function () { +var clearTableColor = function() { $table.find('.mtbl-cell').removeClass(Object.keys(tableColorClass).join(' ')); };