<!DOCTYPE html> <html sort-visualize-app> <head> <meta charset="UTF-8" /> <title>javascript各种排序算法</title> <meta name="Description" content="jsdo.it - share JavaScript, HTML5 and CSS -" /> <meta name="Keywords" content="JavaScript,HTML5,CSS" /> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1" /> <style type="text/css"> body { background: #333; overflow: hidden; } .ui > div { display: inline-block; margin-bottom: 5px; } .ui input[type=radio] { display: none; } .ui .button-set { font-size: 0px; margin: 5px 5px; } .ui .button { display: inline-block; background: #ddd; padding: 6px 4px; border-right: 1px solid #bbb; font-size: 10px; font-family: monospace; } .ui .button:hover { background: #ccc; } .ui label:first-child .button { border-radius: 5px 0px 0px 5px; } .ui label:last-child .button { border-radius: 0px 5px 5px 0px; border-right-width: 0px; } .ui label:only-child .button { border-radius: 5px; } .ui input:checked + .button { background: #666; color: #fff; } .ui input[type=range] { position: relative; top: 7px; } </style> <meta charset="UTF-8"> <title>Sort Visualize</title> <link rel="stylesheet" href="/norahiko/oxly/css" type="text/css" charset="utf-8"> </head> <body> <div class="ui"> <div data-bind="foreach: algorithmList" class="button-set"> <label> <input type="radio" data-bind="checkedValue: $data, checked: $root.algorithm"> <span data-bind="text: $data" class="button"></span> </label> </div> <div> <input type="range" data-bind=" value: speed, attr: { min: speedMin, max: speedMax }"> </div> <div data-bind="foreach: sizeList" class="button-set"> <label> <input type="radio" data-bind="checkedValue: $data, checked: $root.size"> <span data-bind="text: $data" class="button"></span> </label> </div> <div class="button-set"> <label> <span data-bind="click: restart" class="button">Restart</span> </label> </div> </div> <div class="graph"> <canvas id="graph-canvas" width="450" height="380"></canvas> </div> <script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-3.0.0.js" type="text/javascript" charset="utf-8"></script> <script src="/norahiko/oxly/js" type="text/javascript" charset="utf-8"></script> <script type="text/javascript"> // https://github.com/norahiko/sort-visualize var helper = { range : function(min, max) { var res = []; for (var i = min; i < max; i++) { res.push(i); } return res; }, shuffle : function(ary) { for (var i = ary.length - 1; 0 <= i; i--) { var rnd = Math.random() * (i + 1) | 0; helper.swap(ary, i, rnd); } }, swap : function(ary, a, b) { if (a < 0 || b < 0 || ary.length <= a || ary.length <= b) { throw new Error('IndexError ' + a + " - " + b); } var temp = ary[a]; ary[a] = ary[b]; ary[b] = temp; }, insert : function(ary, from, to) { while (from != to) { if (from < to) { helper.swap(ary, from, from + 1); from += 1; } else { helper.swap(ary, from, from - 1); from -= 1; } } }, median3 : function(a, b, c) { if (b <= a) if (a <= c) return a; else if (c <= b) return b; else return c; else if (c <= a) return a; else if (c <= b) return c; else return b; }, getCanvas : function(id) { var canvas = document.getElementById(id); if (canvas === null || canvas.nodeName.toLowerCase() !== 'canvas') { return document.createElement('canvas'); } return canvas; } }; var graph = {}; (function() { var canvas; var ctx; var width; var height; var bgColor = '#333'; var barColor = '#6cf'; var highlightColor = '#cf6'; graph.init = function(c) { canvas = c; ctx = canvas.getContext('2d'); width = canvas.offsetWidth; height = canvas.offsetHeight; }; graph.draw = function(highlightIndexes, values) { ctx.fillStyle = bgColor; ctx.fillRect(0, 0, width, height); var idx1 = highlightIndexes[0]; var idx2 = highlightIndexes[1]; var size = values.length; var barWidth = (width - size + 1) / size; var barHeightUnit = height / size; var x = 0; var h = 0; ctx.fillStyle = barColor; for (var i = 0; i < values.length; i++) { h = values[i] * barHeightUnit; if (i === idx1 || i === idx2) { ctx.fillStyle = highlightColor; ctx.fillRect(x, height - h, barWidth, h); ctx.fillStyle = barColor; } else { ctx.fillRect(x, height - h, barWidth, h); } x = x + barWidth + 1; } }; })(); function SortStep(type, indexes) { // type = 'swap' | 'highlight' | 'insert' this.type = type; this.indexes = indexes; } SortStep.SWAP = 'swap'; SortStep.HIGHLIGHT = 'highlight'; SortStep.INSERT = 'insert'; SortStep.prototype.run = function(ary) { if (this.type === SortStep.SWAP) { helper.swap(ary, this.indexes[0], this.indexes[1]); } else if (this.type === SortStep.INSERT) { helper.insert(ary, this.indexes[0], this.indexes[1]); this.indexes[0] = -1; } }; function SortAlgorithm(values) { this.values = values; this.size = values.length; this.steps = []; this.finished = false; } SortAlgorithm.prototype.sort = function(algorithm) { this[algorithm](); this.steps.reverse(); if (algorithm !== 'bogo') { this.finished = true; } }; SortAlgorithm.prototype.addStep = function(type, indexes) { this.steps.push(new SortStep(type, indexes)); }; SortAlgorithm.prototype.swap = function(a, b) { helper.swap(this.values, a, b); this.addStep(SortStep.SWAP, [a, b]); }; SortAlgorithm.prototype.highlight = function(a, b) { this.addStep(SortStep.HIGHLIGHT, [a, b]); }; SortAlgorithm.prototype.insert = function(from, to) { helper.insert(this.values, from, to); this.addStep(SortStep.INSERT, [from, to]); }; SortAlgorithm.prototype.bubble = function bubbleSort() { for (var i = this.size - 1; 0 < i; i--) { for (var k = 0; k < i; k++) { if (this.values[k] > this.values[k + 1]) { this.swap(k, k + 1); } else { this.highlight(k, k + 1); } } } }; SortAlgorithm.prototype.selection = function selectionSort() { for (var i = 0; i < this.size - 1; i++) { var min = i; for (var k = i + 1; k < this.size; k++) { this.highlight(min, k); if (this.values[k] < this.values[min]) { min = k; } } this.swap(i, min); } }; SortAlgorithm.prototype.shaker = function shakerSort() { var left = 0; var right = this.size - 1; var incr = 1; var i = 0; var next; var lastSwapped = 0; var count = 0; while (left < right) { next = i + incr; if ((incr === 1 && this.values[i] > this.values[next]) || (incr === -1 && this.values[next] > this.values[i])) { this.swap(i, next); lastSwapped = i; } else { this.highlight(i, next); } if (next === right) { i = right = lastSwapped; incr = -incr; } else if (next === left) { i = left = lastSwapped; incr = -incr; } else { i = next; } } }; SortAlgorithm.prototype.insertion = function insertionSort() { for (var i = 1; i < this.size; i++) { for (var k = i; 0 < k; k--) { if (this.values[k - 1] > this.values[k]) { this.swap(k - 1, k); } else { this.highlight(k - 1, k); break; } } } }; SortAlgorithm.prototype.shell = function shellSort() { var gap = 1; while (gap < this.size) { gap = gap * 3 + 1; } while (1 < gap) { gap = gap / 3 | 0; for (var i = gap; i < this.size; i++) { for (var k = i; 0 < k; k -= gap) { if (this.values[k - gap] > this.values[k]) { this.swap(k - gap, k); } else { this.highlight(k - gap, k); break; } } } } }; SortAlgorithm.prototype.merge = function mergeSort() { this.mergeSortImpl(0, this.size - 1); }; SortAlgorithm.prototype.mergeSortImpl = function(left, right) { if (right <= left) { return; } var middle = (left + right) / 2 | 0; this.mergeSortImpl(left, middle); this.mergeSortImpl(middle + 1, right); var l = left; var m = middle + 1; while (l < m && m <= right) { this.highlight(l, m); if (this.values[l] >= this.values[m]) { this.insert(m, l); m++; } l++; } }; SortAlgorithm.prototype.quick = function quickSort() { this.quickSortImpl(0, this.size - 1); }; SortAlgorithm.prototype.quickSortImpl = function(left, right) { var values = this.values; var middle = (left + right) / 2 | 0; var pivot = helper.median3(values[left], values[middle], values[right]); var l = left; var r = right; while (true) { while (values[l] < pivot) { this.highlight(l, r); l++; } while (pivot < values[r]) { this.highlight(l, r); r--; } if (r <= l) { break; } this.swap(l, r); l++; r--; } if (left < l - 1) { this.quickSortImpl(left, l - 1); } if (r + 1 < right) { this.quickSortImpl(r + 1, right); } }; SortAlgorithm.prototype.heap = function heapSort() { for (var i = 0; i < this.size; i++) { this.swapUp(i); } for ( i = this.size - 1; 0 < i; i--) { if (this.values[0] > this.values[i]) { this.swap(0, i); } else { this.highlight(0, i); } this.swapDown(0, i); } }; SortAlgorithm.prototype.swapUp = function(cur) { var parent; while (cur !== 0) { parent = (cur - 1) / 2 | 0; if (this.values[parent] >= this.values[cur]) { this.highlight(parent, cur); break; } this.swap(parent, cur); cur = parent; } }; SortAlgorithm.prototype.swapDown = function(cur, length) { var values = this.values; var child; while (true) { child = cur * 2 + 1; if (values[child] < values[child + 1]) { child += 1; } if (values[cur] >= values[child]) { this.highlight(cur, child); break; } if (length <= child) { break; } this.swap(cur, child); cur = child; } }; SortAlgorithm.prototype.bogo = function bogoSort() { for (var i = 0; i < this.size; i++) { var rnd = (Math.random() * (this.size - i) | 0) + i; this.swap(i, rnd); } for ( i = 0; i < this.size - 1; i++) { this.highlight(i, i + 1); if (this.values[i] > this.values[i + 1]) { return; } } this.finished = true; }; function ViewModel() { this.algorithm = ko.observable('Bubble'); this.size = ko.observable(50); this.speed = ko.observable(9); this.sort = null; this.nums = []; this.algorithmList = ['Bubble', 'Selection', 'Shaker', 'Insertion', 'Shell', 'Merge', 'Heap', 'Quick', 'Bogo']; this.sizeList = [5, 10, 50, 100, 150]; this.speedMin = 1; // 2 seconds this.speedMax = 22; // 4 milliseconds this.intervalId = -1; graph.init(helper.getCanvas('graph-canvas')); this.changed = ko.computed({ read : function() { this.start(this.algorithm(), this.size()); }, owner : this, deferEvaluation : true, }); } ViewModel.prototype.start = function(algorithm, size) { var vm = this; this.nums = helper.range(1, size + 1); helper.shuffle(this.nums); this.sort = new SortAlgorithm(this.nums.slice()); clearInterval(this.intervalId); this.intervalId = setTimeout(animationFrame, 0); function animationFrame() { if (vm.sort.steps.length === 0) { if (vm.sort.finished) { graph.draw([-1, -1], vm.nums); return; } else { vm.sort.sort(algorithm.toLowerCase()); console.log(vm.sort.steps.length); } } var step = vm.sort.steps.pop(); step.run(vm.nums); graph.draw(step.indexes, vm.nums); vm.intervalId = setTimeout(animationFrame, vm.getIntervalTime()); } }; ViewModel.prototype.restart = function() { this.start(this.algorithm.peek(), this.size.peek()); }; ViewModel.prototype.getIntervalTime = function() { var speed = parseInt(this.speed.peek(), 10); return 2000 / speed / speed | 0; }; if (document.documentElement.hasAttribute('sort-visualize-app')) { document.addEventListener('DOMContentLoaded', main); } function main() { var vm = window.vm = new ViewModel(); ko.applyBindings(vm); vm.changed(); } </script> </body> </html>