Skip to content

Commit a5e3f68

Browse files
committed
Adding new algorithm: msort_list_td
1 parent 9f131e9 commit a5e3f68

File tree

12 files changed

+813
-173
lines changed

12 files changed

+813
-173
lines changed

src/algorithms/controllers/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,4 @@ export { default as HashingDelete } from './HashingDelete';
3232
export { default as HashingChainingInsertion} from './HashingChainingInsertion'
3333
export { default as isort} from './isort'
3434
export { default as BSTrec} from './BSTrec'
35+
export { default as msort_list_td} from './msort_list_td'
Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
// Heapsort animation
2+
//
3+
// It's worth looking at this code if you are planning to write any new
4+
// modules.
5+
//
6+
// This was the first animation done and the code is reasonably simple -
7+
// the abstractions supported match what we need for this algorithm.
8+
// For various other algorithms, the code seems much more messy - maybe
9+
// the abstractions for the data structures/rendering are not quite what
10+
// is needed or the coding is done with a sledgehammer, so to speak.
11+
//
12+
// The original version of this code was not quite right in the way it
13+
// adapted (or didn't adapt) to expansion/collapse of code blocks. This
14+
// was added later in a reasonably simple way (again, other algorithms
15+
// may use the sledgehammer style).
16+
//
17+
// One thing that could make the code here more readable is to use
18+
// meaningful strings for bookmarks rather than numbers.
19+
// The way colors are done could also be improved - currently moving to
20+
// a less insane scheme so there is some consistency between colors for
21+
// array elements and tree/graph nodes.
22+
23+
/* eslint-disable no-multi-spaces,indent,prefer-destructuring,brace-style */
24+
import GraphTracer from '../../components/DataStructures/Graph/GraphTracer';
25+
import ArrayTracer from '../../components/DataStructures/Array/Array1DTracer';
26+
import {areExpanded} from './collapseChunkPlugin';
27+
import {colors} from '../../components/DataStructures/colors';
28+
29+
// currently colors for graphs (including trees) are still a mess; this
30+
// is kind of a stub for when they are fixed up. The code involving
31+
// (un)highlight() and .sorted() should be fixed at this point also.
32+
// We define colors for the array (_A) and tree (_T) views; note current and
33+
// child nodes are swapped at times in downheap. At the start, nothing
34+
// is selected but it turns out that whenever anything is de-selected it
35+
// is part of a heap.
36+
const HSColors = {
37+
CURRENT_A: colors.apple,
38+
CHILD_A: colors.sky,
39+
HEAP_A: colors.leaf,
40+
CURRENT_T: 3, // Red (globalColors.apple)
41+
CHILD_T: 4, // Blue (globalColors.sky)
42+
HEAP_T: 1, // Green (globalColors.leaf)
43+
}
44+
45+
46+
// k displayed only if first BuildHeap is expanded
47+
// Note: This is only needed in the last chunk of BuildHeap. The code
48+
// looks like it displays k throughout BuildHeap but when BuildHeap is
49+
// collapsed, only the last chunk is rendered so the other chunks don't
50+
// matter and we can avoid testing what is expanded there. Another
51+
// approach would be to use a wrapper function for assigning to k, which
52+
// checks isBuildHeapExpanded() (it doesn't generalise well for i and j
53+
// though).
54+
function isBuildHeapExpanded() {
55+
return areExpanded(['BuildHeap']);
56+
}
57+
58+
// i, j (in build) displayed only if first DownHeap is expanded
59+
// See Note in isBuildHeapExpanded()
60+
function isDownHeapkExpanded() {
61+
return areExpanded(['BuildHeap', 'DownHeapk']);
62+
}
63+
64+
// i, j (in sort) displayed only if second DownHeap is expanded
65+
function isDownHeap1Expanded() {
66+
return areExpanded(['SortHeap', 'DownHeap1']);
67+
}
68+
69+
export default {
70+
initVisualisers() {
71+
return {
72+
array: {
73+
instance: new ArrayTracer('array', null, 'Array view', { arrayItemMagnitudes: true }), // Label the input array as array view
74+
order: 0,
75+
},
76+
heap: {
77+
instance: new GraphTracer('heap', null, 'Tree view'), // Label the animation of the heap as tree view
78+
order: 1,
79+
},
80+
};
81+
},
82+
83+
84+
/**
85+
*
86+
* @param {object} chunker
87+
* @param {array} nodes array of numbers needs to be sorted
88+
*/
89+
run(chunker, { nodes }) {
90+
// create a copy, can't simply let A = nodes because it creates a reference
91+
// sort A in-place will cause nodes sorted as well
92+
const A = [...nodes];
93+
let n = nodes.length;
94+
let i;
95+
let heap;
96+
let swap;
97+
98+
chunker.add(
99+
1,
100+
(vis, array) => {
101+
vis.heap.setHeap(array);
102+
// tell the graph renderer that it is heapsort
103+
// so that the array index should start from 1
104+
vis.array.set(array, 'heapsort');
105+
},
106+
[nodes],
107+
);
108+
109+
const highlight = (vis, index, primaryColor = true) => {
110+
if (primaryColor) {
111+
vis.heap.colorNode(index + 1, HSColors.CURRENT_T);
112+
vis.array.selectColor(index, colors.apple);
113+
} else {
114+
vis.heap.colorNode(index + 1, HSColors.CHILD_T);
115+
vis.array.selectColor(index, colors.sky);
116+
}
117+
};
118+
119+
const unhighlight = (vis, index, primaryColor = true) => {
120+
if (primaryColor) {
121+
vis.heap.colorNode(index + 1, HSColors.HEAP_T);
122+
} else {
123+
vis.heap.colorNode(index + 1, HSColors.HEAP_T);
124+
}
125+
vis.array.selectColor(index, HSColors.HEAP_T);
126+
};
127+
128+
const swapAction = (b, n1, n2) => {
129+
chunker.add(b, (vis, _n1, _n2) => {
130+
vis.heap.swapNodes(_n1 + 1, _n2 + 1);
131+
vis.array.swapElements(_n1, _n2);
132+
}, [n1, n2]);
133+
};
134+
135+
/** NOTE: In Lee's code, array index starts from 1
136+
* however, in JS, array index naturally starts from 0
137+
* index start from 0:
138+
* parent = k , left child = 2*k + 1, right child = 2*k + 2
139+
* index start from 1:
140+
* parent = k , left child = 2*k, right child = 2*k + 1
141+
*/
142+
143+
// keep track of last node highlighted due to i (or k) so we can
144+
// unhighlight it of buildHeap is collapsed
145+
let lastiHighlight;
146+
147+
// build heap
148+
// start from the last non-leaf node, work backwards to maintain the heap
149+
let lastNonLeaf = Math.floor(n / 2) - 1;
150+
for (let k = lastNonLeaf; k >= 0; k -= 1) {
151+
152+
let j;
153+
const tmp = i;
154+
i = k;
155+
156+
chunker.add(4, (vis, index1, index2, first, max) => {
157+
vis.array.assignVariable('k', index1);
158+
if (index1 === first) { // done the first time we reach here
159+
for (let l = index1 + 1; l <= max; l++) { // color leaves
160+
vis.heap.colorNode(l + 1, HSColors.HEAP_T);
161+
vis.array.selectColor(l, HSColors.HEAP_T);
162+
}
163+
}
164+
if (index2 != null) {
165+
unhighlight(vis, index2);
166+
vis.array.removeVariable('j');
167+
}
168+
highlight(vis, index1);
169+
}, [i, tmp, lastNonLeaf, n - 1]);
170+
171+
chunker.add(6, (vis, index1, index2) => {
172+
vis.array.assignVariable('i', index1);
173+
}, [i, tmp]);
174+
175+
lastiHighlight = k;
176+
heap = false;
177+
chunker.add(7);
178+
179+
chunker.add(8);
180+
// if current node's left child's index is greater than array length,
181+
// then current node is a leaf
182+
while (!(2 * i + 1 >= n || heap)) {
183+
chunker.add(10);
184+
185+
// left child is smaller than right child
186+
if (2 * i + 2 < n && A[2 * i + 1] < A[2 * i + 2]) {
187+
j = 2 * i + 2;
188+
chunker.add(11, (vis, index) => {
189+
highlight(vis, index, false);
190+
vis.array.assignVariable('j', index);
191+
}, [j]);
192+
} else {
193+
j = 2 * i + 1;
194+
chunker.add(13, (vis, index) => {
195+
highlight(vis, index, false);
196+
vis.array.assignVariable('j', index);
197+
}, [j]);
198+
}
199+
200+
chunker.add(14);
201+
// parent is greater than largest child, so it is already a valid heap
202+
if (A[i] >= A[j]) {
203+
heap = true;
204+
chunker.add(15, (vis, index, lastH, cur_k) => {
205+
unhighlight(vis, index, false);
206+
// possible last chunk in BuildHeap/DownHeapk
207+
// remove i, j if !isDownHeapkExpanded
208+
if (!isDownHeapkExpanded()) {
209+
vis.array.removeVariable('i');
210+
vis.array.removeVariable('j');
211+
}
212+
// remove k+highlighting if !isBuildHeapExpanded & last
213+
// chunk of BuildHeap
214+
if (!isBuildHeapExpanded() && cur_k === 0) {
215+
vis.array.removeVariable('k');
216+
if (lastH !== index)
217+
unhighlight(vis, lastH);
218+
}
219+
}, [j, lastiHighlight, k]);
220+
} else {
221+
swap = A[i];
222+
A[i] = A[j];
223+
A[j] = swap;
224+
swapAction(17, i, j);
225+
lastiHighlight = j;
226+
chunker.add(18, (vis, p, c, lastH, cur_k) => {
227+
unhighlight(vis, p, false);
228+
vis.array.assignVariable('i', c);
229+
// remove i, j if !isDownHeapkExpanded
230+
if (!isDownHeapkExpanded()) {
231+
vis.array.removeVariable('i');
232+
vis.array.removeVariable('j');
233+
}
234+
// remove k+highlighting if !isDownHeapkExpanded & last
235+
// chunk of BuildHeap
236+
if (!isBuildHeapExpanded() && cur_k === 0) {
237+
vis.array.removeVariable('k');
238+
if (lastH !== p)
239+
unhighlight(vis, lastH);
240+
}
241+
}, [i, j, lastiHighlight, k]);
242+
i = j;
243+
}
244+
}
245+
}
246+
247+
// sort heap
248+
249+
while (n > 1) {
250+
chunker.add(20, (vis, nVal, index) => {
251+
// clear variables & show 'n'
252+
vis.array.clearVariables();
253+
vis.array.assignVariable('n', nVal - 1);
254+
unhighlight(vis, index);
255+
}, [n, i]);
256+
257+
let j;
258+
swap = A[n - 1];
259+
A[n - 1] = A[0];
260+
A[0] = swap;
261+
262+
chunker.add(21, (vis, index) => {
263+
highlight(vis, index);
264+
highlight(vis, 0, false);
265+
}, [n - 1]);
266+
swapAction(21, 0, n - 1);
267+
268+
chunker.add(22, (vis, index) => {
269+
unhighlight(vis, index, false);
270+
vis.array.sorted(index);
271+
vis.heap.removeNodeColor(index + 1);
272+
vis.heap.sorted(index + 1);
273+
274+
vis.array.assignVariable('n', index - 1);
275+
}, [n - 1]);
276+
n -= 1;
277+
278+
i = 0;
279+
chunker.add(24, (vis, index1, nVal) => {
280+
vis.array.assignVariable('i', index1);
281+
}, [i, n]);
282+
283+
chunker.add(25);
284+
heap = false;
285+
286+
chunker.add(26, (vis, nVal) => {
287+
// if (nVal === 0) vis.array.clearVariables();
288+
}, [n]);
289+
// need to maintain the heap after swap
290+
while (!(2 * i + 1 >= n || heap)) {
291+
chunker.add(28);
292+
293+
if (2 * i + 2 < n && A[2 * i + 1] < A[2 * i + 2]) {
294+
j = 2 * i + 2;
295+
chunker.add(29, (vis, index) => {
296+
highlight(vis, index, false);
297+
vis.array.assignVariable('j', index);
298+
}, [j]);
299+
} else {
300+
j = 2 * i + 1;
301+
chunker.add(31, (vis, index) => {
302+
highlight(vis, index, false);
303+
vis.array.assignVariable('j', index);
304+
}, [j]);
305+
}
306+
307+
chunker.add(32);
308+
if (A[i] >= A[j]) {
309+
heap = true;
310+
chunker.add(33, (vis, index) => {
311+
unhighlight(vis, index, false);
312+
}, [j]);
313+
} else {
314+
swap = A[i];
315+
A[i] = A[j];
316+
A[j] = swap;
317+
swapAction(35, i, j);
318+
chunker.add(36, (vis, p, c) => {
319+
unhighlight(vis, p, false);
320+
vis.array.assignVariable('i', c);
321+
// remove i, j if !isDownHeap1Expanded
322+
if (!isDownHeap1Expanded()) {
323+
vis.array.removeVariable('i');
324+
vis.array.removeVariable('j');
325+
}
326+
}, [i, j]);
327+
i = j;
328+
}
329+
}
330+
}
331+
chunker.add(37, (vis) => {
332+
// Put in done state
333+
vis.array.clearVariables();
334+
// vis.array.deselect(0);
335+
unhighlight(vis, 0, true);
336+
vis.array.sorted(0);
337+
vis.heap.removeNodeColor(1);
338+
vis.heap.sorted(1);
339+
});
340+
// for test
341+
return A;
342+
},
343+
};

src/algorithms/explanations/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,4 @@ export { default as DFSrecExp } from './DFSrecExp.md';
2525
export { default as HashingExp } from './HashingExp.md';
2626
export { default as isort} from './isort.md'
2727
export { default as BSTrec} from './BSTrec.md'
28+
export { default as msort_list_td} from './msort_list_td.md'

0 commit comments

Comments
 (0)