diff --git a/public/coding_challenges/sorting-visualiser/algo.js b/public/coding_challenges/sorting-visualiser/algo.js index 16a8a5bb4f1bb807e80d1c8ef2df36a59dcdd59b..af7d87e411c2706663638972ddd4326aeba58921 100644 --- a/public/coding_challenges/sorting-visualiser/algo.js +++ b/public/coding_challenges/sorting-visualiser/algo.js @@ -3,6 +3,10 @@ class Algorithm { constructor(arr) { this.elems = arr; } + sort() { + while (!this.getComplete()) + this.sortIteration(); + } } let Algorithms = []; //# sourceMappingURL=algo.js.map \ No newline at end of file diff --git a/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js index 671bd0ad4c50ec773254392ba262f57f7b2aa4e6..6453806636e21c0fc03a7ec2b4e051d43eac585c 100644 --- a/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js +++ b/public/coding_challenges/sorting-visualiser/algorithms/bubble-sort.js @@ -4,8 +4,6 @@ class BubbleSort extends Algorithm { this.noIters = 0; this.position = 0; } - sort() { - } sortIteration() { let a = this.elems.get(this.position); let b = this.elems.get(this.position + 1); @@ -16,14 +14,15 @@ class BubbleSort extends Algorithm { } this.position++; // console.log(this.position, this.noIters) - if (this.position >= (this.elems.length - this.noIters)) { + if (this.position >= (this.elems.length - this.noIters - 1)) { this.noIters++; this.position = 0; } } getComplete() { - return this.noIters >= this.elems.length; + return this.noIters >= (this.elems.length - 1); } + setOpt(key, val) { } } Algorithms.push({ name: "bubble", diff --git a/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js index 4e55b94e568051fd0a38427b1aadbc3341e4d7df..d772045260e0075dc246f352cf598576ed690157 100644 --- a/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js +++ b/public/coding_challenges/sorting-visualiser/algorithms/cocktail-shaker-sort.js @@ -4,8 +4,6 @@ class CocktailShakerSort extends Algorithm { this.noIters = 0; this.position = 0; } - sort() { - } sortIteration() { let a = this.elems.get(this.position); let b = this.elems.get(this.position + 1); @@ -18,7 +16,7 @@ class CocktailShakerSort extends Algorithm { else this.position--; // console.log(this.position, this.noIters) - if ((this.position >= (this.elems.length - Math.ceil(this.noIters / 2))) || (this.position < (Math.floor(this.noIters / 2)))) { + if ((this.position >= (this.elems.length - Math.ceil(this.noIters / 2) - 1)) || (this.position < (Math.floor(this.noIters / 2)))) { this.noIters++; if (this.noIters % 2 == 0) this.position++; @@ -29,6 +27,7 @@ class CocktailShakerSort extends Algorithm { getComplete() { return this.noIters >= this.elems.length; } + setOpt(key, val) { } } Algorithms.push({ name: "cocktailshaker", diff --git a/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js b/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js index a6bb8066dc7a49e78df012a6e3e0a6a066f94483..fac783b82556bea5672436f7ea647ce027b8f618 100644 --- a/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js +++ b/public/coding_challenges/sorting-visualiser/algorithms/fisher-yates.js @@ -3,8 +3,6 @@ class FisherYates extends Algorithm { super(...arguments); this.shuffle_pos = 0; } - sort() { - } sortIteration() { if (this.getComplete()) return; @@ -17,5 +15,6 @@ class FisherYates extends Algorithm { return true; return false; } + setOpt(key, val) { } } //# sourceMappingURL=fisher-yates.js.map \ No newline at end of file diff --git a/public/coding_challenges/sorting-visualiser/algorithms/heap-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/heap-sort.js new file mode 100644 index 0000000000000000000000000000000000000000..778981c98425a6c1f583e6a657e5f8a47acc3c1d --- /dev/null +++ b/public/coding_challenges/sorting-visualiser/algorithms/heap-sort.js @@ -0,0 +1,119 @@ +var HeapSortState; +(function (HeapSortState) { + HeapSortState[HeapSortState["START"] = 0] = "START"; + HeapSortState[HeapSortState["HEAPIFY"] = 1] = "HEAPIFY"; + HeapSortState[HeapSortState["SORT"] = 2] = "SORT"; +})(HeapSortState || (HeapSortState = {})); +class HeapSort extends Algorithm { + constructor() { + super(...arguments); + this.state = HeapSortState.START; + } + sortIteration() { + if (this.state == HeapSortState.START) { + this.heapSize = this.elems.length; + //0 -> 1,2 + //1 -> 3,4 + //2 -> 5,6 + //n -> 2n+1,2n+2 + this.pos = Math.floor((this.heapSize - 1) / 2); + this.filterPos = this.pos; + this.state = HeapSortState.HEAPIFY; + } + else if (this.state == HeapSortState.HEAPIFY) { + if (!this.filterDownIteration()) { + this.pos--; + this.filterPos = this.pos; + if (this.pos < 0) { + this.state = HeapSortState.SORT; + console.log("Heap created"); + this.pos = 0; + this.filterPos = this.pos; + this.verifyMaxHeap(); + } + } + } + else if (this.state == HeapSortState.SORT) { + // this.heapSize = 0; + // return; + if (this.filterPos == -1) { + this.elems.swap(0, this.heapSize - 1); + this.heapSize--; + this.filterPos = 0; + } + else if (!this.filterDownIteration()) { + this.filterPos = -1; + } + // this.state = HeapSortState. + // this.heapSize = 0; + } + } + filterDownIteration() { + let filtered = false; + let fp = this.filterPos; + if (this.filterPos < 0 || this.filterPos >= this.heapSize) { + debugger; + return; + } + let parent = this.elems.get(this.filterPos); + let childIdx = this.filterPos * 2 + 1; + let c1, c2; + if (childIdx < this.heapSize) + c1 = this.elems.get(childIdx); + if ((childIdx + 1) < this.heapSize) + c2 = this.elems.get(childIdx + 1); + let greaterIdx, greater; + if (c1 != undefined) { + greaterIdx = childIdx; + greater = c1; + if (c2 != undefined && c2 > c1) { + greaterIdx++; + greater = c2; + } + } + if (greaterIdx && greater > parent) { + //swap + this.elems.swap(this.filterPos, greaterIdx); + //now filter child + this.filterPos = greaterIdx; + filtered = true; + } + //check + let a = this.elems.getArr(); + if (((fp * 2 + 1) < this.heapSize && a[fp] < a[fp * 2 + 1]) || ((fp * 2 + 2) < this.heapSize && a[fp] < a[fp * 2 + 2])) { + console.error(`INVALID HEAP index ${fp} : ${a[fp]} => ${a[fp * 2 + 1]}, ${a[fp * 2 + 2]}`); + console.log({ + parent, + c1, + c2, + greater, + greaterIdx + }); + debugger; + } + return filtered; + } + getComplete() { + return this.heapSize <= 0; + } + verifyMaxHeap() { + let a = this.elems.getArr(); + for (let i = 0; i < this.heapSize; i++) { + let p = a[i]; + let childIdx = i * 2 + 1; + let c1, c2; + c1 = a[childIdx]; + c2 = a[childIdx + 1]; + if (c1 > p && c2 > p) { + console.error(`INVALID HEAP index ${i} : ${p} => ${c1}, ${c2}`); + debugger; + } + } + } + setOpt(key, val) { } +} +Algorithms.push({ + name: "heapsort", + constructor: HeapSort, +}); +//# sourceMappingURL=heap-sort.js.map \ No newline at end of file diff --git a/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js b/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js index 99129ce86e1c4867632f83f794a31ed45a161b14..6bec6e71978fa3c705ae80e1513a462265ae23fc 100644 --- a/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js +++ b/public/coding_challenges/sorting-visualiser/algorithms/quick-sort.js @@ -5,14 +5,21 @@ var QuickSortState; QuickSortState[QuickSortState["FILTER"] = 2] = "FILTER"; QuickSortState[QuickSortState["SOLVED"] = 3] = "SOLVED"; })(QuickSortState || (QuickSortState = {})); +var QuickSortPivot; +(function (QuickSortPivot) { + QuickSortPivot[QuickSortPivot["LOW"] = 0] = "LOW"; + QuickSortPivot[QuickSortPivot["MIDDLE"] = 1] = "MIDDLE"; + QuickSortPivot[QuickSortPivot["HIGH"] = 2] = "HIGH"; + QuickSortPivot[QuickSortPivot["RANDOM"] = 3] = "RANDOM"; + QuickSortPivot[QuickSortPivot["LMH_MEAN"] = 4] = "LMH_MEAN"; +})(QuickSortPivot || (QuickSortPivot = {})); class QuickSort extends Algorithm { constructor() { super(...arguments); this.state = 0; + this.pivotType = 0; this.bt = []; } - sort() { - } sortIteration() { if (this.state == QuickSortState.START) { this.hi = this.elems.length - 1; @@ -20,7 +27,33 @@ class QuickSort extends Algorithm { this.state = QuickSortState.GETPIVOT; } if (this.state == QuickSortState.GETPIVOT) { - this.pivot = this.elems.get(this.lo); //could be any value really + switch (this.pivotType) { + case QuickSortPivot.LOW: + this.pivotIdx = this.lo + 1; + this.pivot = this.elems.get(this.pivotIdx); + break; + case QuickSortPivot.MIDDLE: + // this.pivot = this.elems.get(this.lo) + this.elems.get(this.hi); + // this.pivot /= 2; + this.pivotIdx = Math.floor((this.lo + this.hi) / 2); + this.pivot = this.elems.get(this.pivotIdx); + break; + case QuickSortPivot.HIGH: + this.pivotIdx = this.hi; + this.pivot = this.elems.get(this.pivotIdx); + break; + case QuickSortPivot.RANDOM: + // this.pivot = this.elems.get(Math.floor(Math.random() * (this.hi - this.lo))); + // this.pivot += this.elems.get(Math.floor(Math.random() * (this.hi - this.lo))); + // this.pivot /= 2; + this.pivotIdx = Math.floor(Math.random() * (this.hi - this.lo)) + this.lo; + this.pivot = this.elems.get(this.pivotIdx); + break; + case QuickSortPivot.LMH_MEAN: + this.pivot = this.elems.get(this.lo) + 1 + this.elems.get(this.hi) + this.elems.get(Math.round((this.lo + this.hi) / 2)); //could be any value really + this.pivot /= 3; + break; + } this.anchor = this.lo; this.insert = this.hi; this.state = QuickSortState.FILTER; @@ -55,9 +88,23 @@ class QuickSort extends Algorithm { getComplete() { return this.state == QuickSortState.SOLVED; } + setOpt(key, val) { + if (key == "pivotmode") { + this.pivotType = val; + } + } } Algorithms.push({ name: "quicksort", - constructor: QuickSort + constructor: QuickSort, + opts: { + "pivotmode": [ + ["first", QuickSortPivot.LOW], + ["middle", QuickSortPivot.MIDDLE], + ["last", QuickSortPivot.HIGH], + ["random", QuickSortPivot.RANDOM], + ["average", QuickSortPivot.LMH_MEAN] + ] + } }); //# sourceMappingURL=quick-sort.js.map \ No newline at end of file diff --git a/public/coding_challenges/sorting-visualiser/index.html b/public/coding_challenges/sorting-visualiser/index.html index 24c6d349bfa862b7a96095d91655e70ac0b095a9..e4a56e3605c2822100d599d2f2bad86f6acc5eb0 100644 --- a/public/coding_challenges/sorting-visualiser/index.html +++ b/public/coding_challenges/sorting-visualiser/index.html @@ -33,6 +33,9 @@ <li> <a href="sorter.html?algo=quicksort">Quick Sort</a> </li> + <li> + <a href="sorter.html?algo=heapsort">Heap Sort</a> + </li> </ul> </div> diff --git a/public/coding_challenges/sorting-visualiser/main.js b/public/coding_challenges/sorting-visualiser/main.js index a78121e2812b288e1bf74e4b7d321f19f320edf3..0108c0368cad06d32795c6b0ea4fc9b1794d7f37 100644 --- a/public/coding_challenges/sorting-visualiser/main.js +++ b/public/coding_challenges/sorting-visualiser/main.js @@ -1,12 +1,16 @@ /// <reference path="../../../p5.global-mode.d.ts" /> /// <reference path="./visualiser.ts" /> /// <reference path="./algo.ts" /> +let Bar; +let sBar; let Arr; let Algo; let AlgoName; let Vis; let visSelect; let algoSelect; +let sOpts; +let sReset; let sAccessors; let sWrites; let params; @@ -25,51 +29,95 @@ function setup() { speed = parseFloat(params["speed"]); else speed = 1; + //@ts-ignore + Bar = createDiv(); + Bar.id("bar"); + //@ts-ignore + sBar = createSpan(); + sBar.parent(Bar); visSelect = createSelect(false); + visSelect.parent(sBar); for (let v of Visualisers) { //@ts-ignore visSelect.option(v.name); } //@ts-ignore let ShuffleButton = createButton("Shuffle"); + ShuffleButton.parent(sBar); ShuffleButton.mouseClicked(() => { Algo = new FisherYates(Arr); AlgoName = "shuffle"; // shuffled = false; sorting = true; }); + ShuffleButton.doubleClicked(() => { + if (Algo) + Algo.sort(); + }); algoSelect = createSelect(false); + algoSelect.parent(sBar); for (let a of Algorithms) { //@ts-ignore algoSelect.option(a.name); } algoSelect.value(params["algo"]); + algoSelect.input(createOptSels); //@ts-ignore let SortButton = createButton("Sort"); + SortButton.parent(sBar); SortButton.mouseClicked(() => { if (shuffled && !sorting) { sorting = true; if (algoSelect.elt.value) { - let AlgoCon = Algorithms.reduce((a, c) => c.name == algoSelect.elt.value ? c : a, null).constructor; + let A = Algorithms.reduce((a, c) => c.name == algoSelect.elt.value ? c : a, null); + let AlgoCon = A.constructor; if (AlgoCon) { Algo = new AlgoCon(Arr); AlgoName = algoSelect.elt.value; + //set opts + let opts = document.getElementsByClassName("sort_opt"); + for (let elem of opts) { + let id = elem.id.split(':')[1]; + //@ts-ignore + Algo.setOpt(id, A.opts[id].reduce((a, c) => c[0] == elem.value ? c[1] : a, -1)); + } } } - // else - // if (params["algo"]) { - // let AlgoCon: AlgorithmConstructor = Algorithms.reduce((a, c) => c.name == params["algo"] ? c : a, null).constructor; - // if (AlgoCon) { - // Algo = new AlgoCon(Arr); - // AlgoName = params["algo"]; - // } - // } } }); + SortButton.doubleClicked(() => { + if (Algo) + Algo.sort(); + }); //@ts-ignore sAccessors = createSpan(); + sAccessors.parent(Bar); //@ts-ignore sWrites = createSpan(); + sWrites.parent(Bar); + //@ts-ignore + sOpts = createSpan(); + sOpts.parent(Bar); + sOpts.style("margin-left", "10px"); + //@ts-ignore + sReset = createSpan(); + sReset.parent(Bar); + sReset.style("position", "relative"); + sReset.style("float", "right"); + //@ts-ignore + let resetButton = createButton("Reset."); + resetButton.parent(sReset); + resetButton.mouseClicked(() => { + Algo = null; + if (params["count"]) + Arr = new WatchedArray(parseInt(params["count"])); + else + Arr = new WatchedArray(100); + }); + // resetButton.style("position", "absolute"); + // resetButton.style("right", "0"); + // resetButton.style("position", "absolute"); + createOptSels(); createCanvas(windowWidth - 40, 700); } function draw() { @@ -101,4 +149,31 @@ function draw() { function windowResized() { resizeCanvas(windowWidth - 30, height); } +function createOptSels() { + let old_opts = document.getElementsByClassName("sort_opt"); + while (old_opts.length) + old_opts[0].remove(); + //create new opts + let algoOpts = Algorithms.reduce((a, c) => c.name == algoSelect.elt.value ? c : a, null).opts; + if (algoOpts) { + for (let opt_key in algoOpts) { + let optSel = createSelect(false); + optSel.parent(sOpts); + optSel.addClass("sort_opt"); + optSel.id(`sort_opt:${opt_key}`); + for (let opt of algoOpts[opt_key]) { + //@ts-ignore + optSel.option(opt[0]); + } + optSel.input(() => { + if (Algo) { + Algo.setOpt(opt_key, algoOpts[opt_key].reduce((a, c) => c[0] == optSel.value() ? c[1] : a, -1)); + } + }); + } + } +} +// new p5(null, document.getElementById("sorter")); +//@ts-ignore +new p5(null, "sorter"); //# sourceMappingURL=main.js.map \ No newline at end of file diff --git a/public/coding_challenges/sorting-visualiser/sorter.html b/public/coding_challenges/sorting-visualiser/sorter.html index 6733c01c9eef105caed160173f7117bf39faf484..a05ae76c5d645a079748966cb7b62968370cb457 100644 --- a/public/coding_challenges/sorting-visualiser/sorter.html +++ b/public/coding_challenges/sorting-visualiser/sorter.html @@ -14,11 +14,13 @@ <script src="./visualisers/standard.js"></script> <script src="./visualisers/spiral.js"></script> <script src="./visualisers/pie.js"></script> + <script src="./visualisers/scatter.js"></script> <script src="./algorithms/fisher-yates.js"></script> <script src="./algorithms/bubble-sort.js"></script> <script src="./algorithms/cocktail-shaker-sort.js"></script> <script src="./algorithms/quick-sort.js"></script> + <script src="./algorithms/heap-sort.js"></script> </head> diff --git a/public/coding_challenges/sorting-visualiser/visualisers/scatter.js b/public/coding_challenges/sorting-visualiser/visualisers/scatter.js new file mode 100644 index 0000000000000000000000000000000000000000..6014fd0f3f738152f2d266592458a3ca4206b4d3 --- /dev/null +++ b/public/coding_challenges/sorting-visualiser/visualisers/scatter.js @@ -0,0 +1,21 @@ +Visualisers.push({ + name: "scatter", + fxn: (arr, c) => { + c.background(180, 180, 180); + c.push(); + c.noStroke(); + c.colorMode(HSB); + let a = arr.getArr(); + translate(0, height); + for (let i = 0; i < a.length; i++) { + let pScale = a[i] / a.length; + let col = c.color(pScale * 360, 100, 100); + c.stroke(col); + c.strokeWeight(10); + c.point(c.width / a.length, -c.height * pScale); + c.translate(c.width / a.length, 0); + } + c.pop(); + } +}); +//# sourceMappingURL=scatter.js.map \ No newline at end of file