Rough visualization of mergesort complete.

master
ben-burlingham 10 years ago
parent 4e0b92a1d1
commit d008d5cc70
  1. 151
      css/style.css
  2. 176
      index.html
  3. 92
      js/mergesort.js
  4. 12
      js/quicksort.js
  5. 75
      js/visualizer-actions.js
  6. 37
      js/visualizer-inits.js
  7. 5
      js/visualizer.js

@ -0,0 +1,151 @@
/* https://color.adobe.com/create/color-wheel/?base=2&rule=Monochromatic&selected=4&name=My%20Color%20Theme&mode=rgb&rgbvalues=0.17972473935894973,0.30406031865629857,0.5,0.6594494787178995,0.7916562131964631,1,0.35944947871789945,0.6081206373125971,1,0.0014128559043085631,0.1949717379336841,0.5,0.28755958297431955,0.48649650985007775,0.8&swatchOrder=0,1,2,3,4 */
@font-face {
font-family: 'thin';
src: url('res/distproth-webfont.eot');
src: url('res/distproth-webfont.eot?#iefix') format('embedded-opentype'),
url('res/distproth-webfont.woff2') format('woff2'),
url('res/distproth-webfont.woff') format('woff'),
url('res/distproth-webfont.ttf') format('truetype'),
url('res/distproth-webfont.svg#district_prothin') format('svg');
font-weight: normal;
font-style: normal;
}
* {
box-sizing:border-box;
}
html, body {
font-family:sans-serif;
margin:0;
}
.sorter {
background:#f4f4f4;
border:1px solid #bbb;
height:270px;
margin:20px auto;
padding:10px;
position:relative;
width:960px;
}
.sorter-sidebar {
height:230px;
position:absolute;
right:10px;
top:10px;
width:160px;
}
.sorter-svg {
background:#fff;
border:1px solid #ccc;
height:200px;
left:10px;
position:absolute;
top:10px;
width:770px;
}
.sorter-svg .marker {
fill:#bbb;
}
.sorter-svg .marker text {
fill:#2E4E7F;
font-size:10px;
}
.sorter-properties {
height:40px;
left:10px;
position:absolute;
top:220px;
width:770px;
}
.sorter-properties .property {
border-right:1px solid #ddd;
float:left;
text-align:center;
width:128px;
}
.sorter-properties .p6 {
border:0;
}
.sorter-properties .property .title {
color:#777;
font-size:10px;
}
.sorter-properties .property .value {
font-size:13px;
margin-top:10px;
}
.controls-container {
height:45px;
padding-bottom:10px;
text-align:center;
}
.controls-container button {
border:1px solid #fff;
background:#d4d4d4;
color:#2E4E7F;
cursor:pointer;
font-size:12px;
height:30px;
line-height:30px;
margin:0 2px;
text-align:center;
transition:background 0.2s ease, border 0.2s ease;
vertical-align: top;
width:30px;
}
.controls-container button:hover {
background:#bbb;
border:1px solid #2E4E7F;
}
.controls-container button:focus {
outline:0;
}
.controls-container .stat {
font-size:13px;
font-weight:bold;
line-height:30px;
}
.message-container {
border-top:1px solid #ddd;
height:100px;
line-height:20px;
text-align:right;
}
.message-container .message {
border-bottom:1px solid #ddd;
font-size:12px;
}
.range-container {
height:50px;
margin-top:40px;
text-align:center;
}
.range-container input {
margin-bottom:5px;
}
.range-container .msg {
font-size:11px;
}

@ -5,165 +5,12 @@
<title>D3</title>
<link rel="stylesheet" href="vendor/fontawesome/css/font-awesome.css">
<style type='text/css'>
/* https://color.adobe.com/create/color-wheel/?base=2&rule=Monochromatic&selected=4&name=My%20Color%20Theme&mode=rgb&rgbvalues=0.17972473935894973,0.30406031865629857,0.5,0.6594494787178995,0.7916562131964631,1,0.35944947871789945,0.6081206373125971,1,0.0014128559043085631,0.1949717379336841,0.5,0.28755958297431955,0.48649650985007775,0.8&swatchOrder=0,1,2,3,4 */
@font-face {
font-family: 'thin';
src: url('res/distproth-webfont.eot');
src: url('res/distproth-webfont.eot?#iefix') format('embedded-opentype'),
url('res/distproth-webfont.woff2') format('woff2'),
url('res/distproth-webfont.woff') format('woff'),
url('res/distproth-webfont.ttf') format('truetype'),
url('res/distproth-webfont.svg#district_prothin') format('svg');
font-weight: normal;
font-style: normal;
}
* {
box-sizing:border-box;
}
html, body {
font-family:sans-serif;
margin:0;
}
.sorter {
background:#f4f4f4;
border:1px solid #bbb;
height:250px;
margin:20px auto;
padding:10px;
position:relative;
width:960px;
}
.sorter-sidebar {
height:230px;
position:absolute;
right:10px;
top:10px;
width:160px;
}
.sorter-svg {
background:#fff;
border:1px solid #ccc;
height:180px;
left:10px;
position:absolute;
top:10px;
width:770px;
}
.sorter-svg .marker {
fill:#bbb;
}
.sorter-svg .marker text {
fill:#2E4E7F;
font-size:10px;
}
.sorter-properties {
height:40px;
left:10px;
position:absolute;
top:200px;
width:770px;
}
.sorter-properties .property {
border-right:1px solid #ddd;
float:left;
text-align:center;
width:128px;
}
.sorter-properties .p6 {
border:0;
}
.sorter-properties .property .title {
color:#777;
font-size:10px;
}
.sorter-properties .property .value {
font-size:13px;
margin-top:10px;
}
.controls-container {
height:45px;
padding-bottom:10px;
text-align:center;
}
.controls-container button {
border:1px solid #fff;
background:#d4d4d4;
color:#2E4E7F;
cursor:pointer;
font-size:12px;
height:30px;
line-height:30px;
margin:0 2px;
text-align:center;
transition:background 0.2s ease, border 0.2s ease;
vertical-align: top;
width:30px;
}
.controls-container button:hover {
background:#bbb;
border:1px solid #2E4E7F;
}
.controls-container button:focus {
outline:0;
}
.controls-container .stat {
font-size:13px;
font-weight:bold;
line-height:30px;
}
.message-container {
border-top:1px solid #ddd;
height:100px;
line-height:20px;
text-align:right;
}
.message-container .message {
border-bottom:1px solid #ddd;
font-size:12px;
}
.range-container {
height:50px;
margin-top:40px;
text-align:center;
}
.range-container input {
margin-bottom:5px;
}
.range-container .msg {
font-size:11px;
}
</style>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
for each one: in place? adaptive? stable? swaps. comparisons. random example. best case example. worst case example.
<h1>Quicksort discussion</h1>
<!-- <h1>Quicksort discussion</h1>
used by chrome. <br>
swap. highlight. (un)fade.
<div class="sorter"
@ -175,14 +22,23 @@
data-best-perf='O(n + m + 3k)'
data-worst-memory='0 (in place)'
></div>
-->
<!-- <h1>Mergesort discussion</h1>
<h1>Mergesort discussion</h1>
used by firefox and safari. <br>
helpful: http://stackoverflow.com/questions/2967153/space-requirements-of-a-merge-sort<br>
<div class="sorter" data-algorithm='merge'></div>
<div class="sorter"
data-algorithm='merge'
data-stable='Maybe'
data-adaptive='Maybe'
data-worst-perf='O(n + m + 3k)'
data-avg-perf='O(n + m + 3k)'
data-best-perf='O(n + m + 3k)'
data-worst-memory='0 (in place)'
></div>
<!--
<h1>Shellsort discussion</h1>
several ways to pick gap width, but dependence on input data makes gap selection trivial. <br>
swap. highlight. fade.
@ -207,7 +63,7 @@
<h1>radix sort discussion</h1>
<div class="sorter" data-algorithm='radix'></div>
-->
<!--
<h1>Heapsort discussion</h1>

@ -1,7 +1,10 @@
/**
*
*/
var MergeSort = function() {};
var MergeSort = function(VisualizerInstance) {
this.V = VisualizerInstance;
this.comparisons = 0;
};
MergeSort.prototype = Object.create(Sorter.prototype);
@ -9,22 +12,64 @@ MergeSort.prototype = Object.create(Sorter.prototype);
*
*/
MergeSort.prototype.sort = function(arr, start, end) {
this.V
.instruct(this.V.unhighlight, 0)
.instruct(this.V.unfade, 0)
.instruct(this.V.fade, 0, -1, start - 1)
.instruct(this.V.fade, 0, end + 1, arr.length)
.instruct(this.V.message, 0, 1, `Comparisons: ${this.comparisons}`)
.instruct(this.V.message, 0, 2, '')
.instruct(this.V.message, 0, 3, '')
.instruct(this.V.message, 0, 4, '')
.instruct(this.V.message, 0, 5, '');
if (arr.length === 0) {
return arr;
}
if (start === end) {
this.addInstruction('single', start);
this.V
.instruct(this.V.message, 100, 2, `Single element [${start}]`)
return new Array(arr[start]);
}
var mid = start + Math.floor((end - start) / 2);
this.V
.instruct(this.V.message, 0, 2, `Sorting [${start}] - [${end}]`)
.instruct(this.V.message, 0, 3, 'Slicing and recursing:')
.instruct(this.V.message, 100, 4, `[${start}]-[${mid}] and [${mid + 1}]-[${end}]`);
var arr1 = this.sort(arr, start, mid);
var arr2 = this.sort(arr, mid + 1, end);
var result = this.merge(arr1, arr2);
var i, j, x, y, v;
var len1 = arr1.length;
var len2 = arr2.length;
for (var i = 0; i < len1; i++) {
x = Visualizer.padding + (i + start) * (Visualizer.itemW + Visualizer.spacerW);
y = Visualizer.padding * 2 + Visualizer.itemH;
v = arr1[i].value;
this.V.instruct(this.V.item, 0, 'secondary', x, y, v, '#05350D')
}
for (var j = 0; j < len2; j++) {
x = Visualizer.padding + (j + len1 + start) * (Visualizer.itemW + Visualizer.spacerW);
y = Visualizer.padding * 2 + Visualizer.itemH;
v = arr2[j].value;
this.V.instruct(this.V.item, 0, 'secondary', x, y, v, '#028E2D')
}
this.V
.instruct(this.V.fade, 0, 0, arr.length)
.instruct(this.V.message, 0, 2, ``)
.instruct(this.V.message, 0, 3, 'Merging slices:')
.instruct(this.V.message, 0, 4, `[${start}]-[${mid}] and [${mid + 1}]-[${end}]`)
var result = this.merge(arr1, arr2);
this.V.instruct(this.V.removeSecondary, 0);
return result;
};
@ -33,20 +78,53 @@ MergeSort.prototype.sort = function(arr, start, end) {
*/
MergeSort.prototype.merge = function(arr1, arr2) {
var result = [];
var e, x, y, v;
this.V.instruct(this.V.removeTertiary, 100);
while (arr1.length > 0 || arr2.length > 0) {
x = Visualizer.padding + result.length * (Visualizer.itemW + Visualizer.spacerW);
y = Visualizer.padding * 3 + Visualizer.itemH * 2
if (arr1.length === 0) {
result.push(arr2.shift());
e = arr2.shift();
result.push(e);
this.V
.instruct(this.V.item, 0, 'tertiary', x, y, e.value, '#8E5500')
.instruct(this.V.message, 0, 4, 'One element left to merge.')
.instruct(this.V.message, 100, 5, `Pushing ${e.value} to sub-result.`);
}
else if (arr2.length === 0) {
result.push(arr1.shift());
e = arr1.shift();
result.push(e);
this.V
.instruct(this.V.item, 0, 'tertiary', x, y, e.value, '#8E5500')
.instruct(this.V.message, 0, 4, 'One element left to merge.')
.instruct(this.V.message, 100, 5, `Pushing ${e.value} to sub-result.`);
}
else if (arr1[0].value <= arr2[0].value) {
result.push(arr1.shift());
e = arr1.shift()
result.push(e);
this.V
.instruct(this.V.item, 0, 'tertiary', x, y, e.value, '#8E5500')
.instruct(this.V.message, 0, 4, `${e.value} <= ${arr2[0].value}`)
.instruct(this.V.message, 100, 5, `Pushing ${e.value} to sub-result.`);
}
else {
result.push(arr2.shift());
e = arr2.shift();
result.push(e);
this.V
.instruct(this.V.item, 0, 'tertiary', x, y, e.value, '#8E5500')
.instruct(this.V.message, 0, 4, `${arr1[0].value} > ${e.value}`)
.instruct(this.V.message, 100, 5, `Pushing ${e.value} to sub-result.`);
}
this.comparisons++;
this.V.instruct(this.V.message, 0, 1, `Comparisons: ${this.comparisons}`);
}
return result;

@ -1,12 +1,14 @@
/**
*
*/
var QuickSort = function(Visualizer) {
var QuickSort = function(VisualizerInstance) {
this.swaps = 0;
this.comparisons = 0;
this.V = Visualizer;
this.V = VisualizerInstance;
this.V
.instruct(this.V.hideMarker, 0, 1)
.instruct(this.V.hideMarker, 0, 2)
.instruct(this.V.message, 0, 2, 'Swaps: ' + this.swaps)
.instruct(this.V.message, 0, 1, 'Comparisons: ' + this.comparisons);
};
@ -73,21 +75,21 @@ QuickSort.prototype.sort = function(arr, start, end) {
this.V
.instruct(this.V.message, 0, 5, `${arr[right].value} > ${pivotval}, decrement right`)
.instruct(this.V.message, 0, 1, 'Comparisons: ' + this.comparisons)
.instruct(this.V.marker, 500, 2, right);
.instruct(this.V.marker, 100, 2, right);
right--;
}
this.V
.instruct(this.V.message, 0, 5, `Right stop: ${arr[right].value} <= ${pivotval} `)
.instruct(this.V.marker, 500, 2, right);
.instruct(this.V.marker, 100, 2, right);
if (left <= right) {
tmp = arr[left];
arr[left] = arr[right];
arr[right] = tmp;
this.V.instruct(this.V.swap, 500, left, right);
this.V.instruct(this.V.swap, 100, left, right);
left++;
right--;

@ -20,17 +20,20 @@ Visualizer.prototype.swap = function(delay, indexA, indexB) {
};
/**
* Highlights an index.
* Highlights a range of indices with a color. End index and color optional.
*/
Visualizer.prototype.highlight = function(delay, index) {
if (index < 0) {
console.error('Trying to highlight index of ' + index);
return;
Visualizer.prototype.highlight = function(delay, startIndex, endIndex, color) {
if (endIndex === undefined) {
endIndex = startIndex;
}
if (color === undefined) {
color = 'orangered';
}
this.groups.each(function(d, i) {
if (d.index === index) {
d3.select(this).select('rect').attr('fill', 'orangered')
if (d.index >= startIndex && d.index <= endIndex) {
d3.select(this).select('rect').attr('fill', color);
}
});
};
@ -39,7 +42,8 @@ Visualizer.prototype.highlight = function(delay, index) {
* Un-highlights an index.
*/
Visualizer.prototype.unhighlight = function() {
this.svg.selectAll('.item').attr('fill', function(d) { return d.fill; });
// this.svg.selectAll('.item').attr('fill', function(d) { return d.fill; });
this.svg.selectAll('.item').attr('fill', '#1A45AC');
};
/**
@ -63,37 +67,56 @@ Visualizer.prototype.unfade = function() {
};
/**
*
* Message updates.
*/
Visualizer.prototype.hideMarker = function(delay, which) {
this.svg.select(`#marker${which}`).attr('style', 'display:none');
Visualizer.prototype.message = function(delay, which, msg) {
var msg = msg || '&nbsp;';
this.parent.querySelector(`.message:nth-child(${which})`).innerHTML = msg;
};
/**
*
*/
Visualizer.prototype.showMarker = function(delay, which) {
this.svg.select(`#marker${which}`).attr('style', 'display:block');
Visualizer.prototype.item = function(delay, classname, x, y, text, color) {
var g = this.svg.append('g')
.attr('class', classname)
.attr('transform', `translate(${x}, ${y})`);
g.append('rect').attr('width', Visualizer.itemW)
.attr('height', Visualizer.itemH)
.attr('fill', color);
g.append('text').text(text)
.attr('fill', '#aaa')
.attr('font-size', 10)
.attr('font-family', 'sans-serif')
.attr('transform', function doTransform(d) {
return `rotate(90 0,0), translate(5, -3)`;
});
};
/**
* Marker movement.
*
*/
Visualizer.prototype.marker = function(delay, which, index, label) {
if (label !== undefined) {
label = label[0].toString();
this.svg.select(`#marker${which} text`).text(label)
}
Visualizer.prototype.removeSecondary = function() {
this.svg.selectAll('.secondary').remove();
};
this.svg.select(`#marker${which}`)
.transition().duration(delay)
.attr('transform', `translate(${Visualizer.calculateX(index)},${Visualizer.spacerW})`)
/**
*
*/
Visualizer.prototype.removeTertiary = function() {
this.svg.selectAll('.tertiary').remove();
};
/**
* Message updates.
*
*/
Visualizer.prototype.message = function(delay, which, msg) {
var msg = msg || '&nbsp;';
this.parent.querySelector(`.message:nth-child(${which})`).innerHTML = msg;
Visualizer.prototype.remove = function(delay, id) {
this.svg.select(`#${id}`).remove();
};
/**
*
*/
// Visualizer.prototype.move = function()

@ -25,17 +25,18 @@ Visualizer.prototype.initItems = function(n) {
// Items
this.groups = this.svg.selectAll('g').data(shuffled).enter().insert('g')
.attr('transform', `translate(0, ${Visualizer.itemY})`);
.attr('transform', `translate(0, ${Visualizer.padding})`);
this.groups.append('rect')
.attr('class', 'item')
.attr('height', Visualizer.itemH)
.attr('width', Visualizer.itemW)
.attr('fill', function doFill(d) { return d.fill; });
// .attr('fill', function doFill(d) { return d.fill; });
.attr('fill', '#1A45AC');
this.groups.transition(500)
.attr('transform', function doTransform(d, i) {
return `translate(${i * (Visualizer.itemW + Visualizer.spacerW) + Visualizer.spacerW}, ${Visualizer.itemY})`;
return `translate(${i * (Visualizer.itemW + Visualizer.spacerW) + Visualizer.padding}, ${Visualizer.padding})`;
});
// Item labels
@ -47,36 +48,6 @@ Visualizer.prototype.initItems = function(n) {
.attr('transform', function doTransform(d) {
return `rotate(90 0,0), translate(5, -3)`;
});
// Markers
var m1 = this.svg.append('g')
.attr('class', 'marker')
.attr('id', 'marker1')
.attr('transform', `translate(${Visualizer.spacerW}, ${Visualizer.spacerW})`);
m1.append('rect')
.attr('height', Visualizer.itemW)
.attr('width', Visualizer.itemW);
m1.append('text')
.attr('text-anchor', 'middle')
.attr('x', (Visualizer.itemW / 2))
.attr('y', 10);
var m2 = this.svg.append('g')
.attr('transform', `translate(${Visualizer.spacerW}, ${Visualizer.spacerW})`)
.attr('class', 'marker')
.attr('style', 'display:none')
.attr('id', 'marker2');
m2.append('rect')
.attr('height', Visualizer.itemW)
.attr('width', Visualizer.itemW);
m2.append('text')
.attr('text-anchor', 'middle')
.attr('x', (Visualizer.itemW / 2))
.attr('y', 10);
};
/**

@ -63,7 +63,7 @@ function Visualizer(parent) {
Visualizer.spacerW = 5;
Visualizer.itemW = 14;
Visualizer.itemH = 50;
Visualizer.itemY = 20;
Visualizer.padding = 10;
/**
* Static.
@ -105,6 +105,7 @@ Visualizer.prototype.followInstruction = function() {
// TODO fix init slider
// TODO heap sort
// TODO extra memory
// TODO width and height updates
// NOTE interesting (anti?)pattern here.
// NOTE use of call() vs apply() (apply only delivered first array item as string)
@ -135,8 +136,6 @@ Visualizer.prototype.reset = function() {
this.message(0, 4, '');
this.message(0, 5, '');
this.marker(0, 1, 0);
this.marker(0, 2, 0);
this.unhighlight();
this.unfade();

Loading…
Cancel
Save