Skip to content

Commit a8f7ac6

Browse files
committed
bottom up msort files, some heapsort fixes
1 parent 5c6dc16 commit a8f7ac6

File tree

4 files changed

+234
-1
lines changed

4 files changed

+234
-1
lines changed

src/algorithms/controllers/collapseChunkPlugin.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
// NOTE: any algorithm controller file that imports this must be listed
77
// below, otherwise hooks into what happens when blocks are expanded or
88
// contracted are not enabled.
9-
const importsThis = ['quickSort', 'quickSortM3', 'msort_arr_td', 'transitiveClosure'];
9+
const importsThis = ['quickSort', 'quickSortM3', 'msort_arr_td',
10+
'transitiveClosure', 'heapSort'];
1011

1112
// eslint-disable-next-line import/no-cycle
1213
// See also accompanying mods/hooks in src/context/GlobalState.js and

src/algorithms/controllers/heapSort.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
11
/* eslint-disable no-multi-spaces,indent,prefer-destructuring,brace-style */
22
import GraphTracer from '../../components/DataStructures/Graph/GraphTracer';
33
import ArrayTracer from '../../components/DataStructures/Array/Array1DTracer';
4+
import {areExpanded} from './collapseChunkPlugin';
5+
6+
// k displayed only if first BuildHeap is expanded
7+
function isBuildHeapExpanded() {
8+
return areExpanded(['BuildHeap']);
9+
}
10+
11+
// i, j (in build) displayed only if first DownHeap is expanded
12+
function isDownHeapkExpanded() {
13+
return areExpanded(['BuildHeap', 'DownHeapk']);
14+
}
15+
16+
// i, j (in sort) displayed only if second DownHeap is expanded
17+
function isDownHeap1Expanded() {
18+
return areExpanded(['SortHeap', 'DownHeap1']);
19+
}
420

521
export default {
622
initVisualisers() {
@@ -16,6 +32,7 @@ export default {
1632
};
1733
},
1834

35+
1936
/**
2037
*
2138
* @param {object} chunker
@@ -126,6 +143,16 @@ export default {
126143
heap = true;
127144
chunker.add(15, (vis, index) => {
128145
unhighlight(vis, index, false);
146+
// possible last chunk in BuildHeap/DownHeapk
147+
// remove i, j if !isDownHeapkExpanded
148+
if (!isDownHeapkExpanded()) {
149+
vis.array.assignVariable('i', undefined);
150+
vis.array.assignVariable('j', undefined);
151+
}
152+
// remove k if !isBuildHeapExpanded
153+
if (!isBuildHeapExpanded()) {
154+
vis.array.assignVariable('k', undefined);
155+
}
129156
}, [j]);
130157
} else {
131158
swap = A[i];
@@ -135,6 +162,15 @@ export default {
135162
chunker.add(18, (vis, p, c) => {
136163
unhighlight(vis, p, false);
137164
vis.array.assignVariable('i', c);
165+
// remove i, j if !isDownHeapkExpanded
166+
if (!isDownHeapkExpanded()) {
167+
vis.array.assignVariable('i', undefined);
168+
vis.array.assignVariable('j', undefined);
169+
}
170+
// remove k if !isDownHeapkExpanded
171+
if (!isBuildHeapExpanded()) {
172+
vis.array.assignVariable('k', undefined);
173+
}
138174
}, [i, j]);
139175
i = j;
140176
}
@@ -214,6 +250,12 @@ export default {
214250
heap = true;
215251
chunker.add(33, (vis, index) => {
216252
unhighlight(vis, index, false);
253+
// possible last chunk in SortHeap/DownHeap1
254+
// remove i, j if !isDownHeap1Expanded
255+
if (!isDownHeap1Expanded()) {
256+
vis.array.assignVariable('i', undefined);
257+
vis.array.assignVariable('j', undefined);
258+
}
217259
}, [j]);
218260
} else {
219261
swap = A[i];
@@ -223,6 +265,12 @@ export default {
223265
chunker.add(36, (vis, p, c) => {
224266
unhighlight(vis, p, false);
225267
vis.array.assignVariable('i', c);
268+
// possible last chunk in SortHeap/DownHeap1
269+
// remove i, j if !isDownHeap1Expanded
270+
if (!isDownHeap1Expanded()) {
271+
vis.array.assignVariable('i', undefined);
272+
vis.array.assignVariable('j', undefined);
273+
}
226274
}, [i, j]);
227275
i = j;
228276
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Merge Sort (bottom-up, for arrays)
2+
3+
---
4+
5+
Bottom up merge sort uses the idea of "runs" - sequences of elements
6+
that are sorted. Any array can be considered a sequence of runs, each
7+
of length one. Mergesort repeatedly merges consecutive pairs of runs to
8+
form runs of twice the length. Thus from an initial run length of 1,
9+
we get (half as many) runs of length 2, then runs of length 4, and so
10+
on until the array has a single run.
11+
12+
The merge operation is not in-place - it requires O(n) extra space. A
13+
simple solution (used here) is to merge the two sorted sub-arrays to a
14+
temporary array then copy them back to the original array. Merge uses
15+
three pointers/indices that scan from left to right; one for each of the
16+
input sub-arrays and one for the output array. At each stage the
17+
minimum input array element is copied to the output array and the
18+
indices for those two arrays are incremented. When one input array has
19+
been completely copied, any additional elements in the other input array
20+
are copied to the output array.
21+
22+
Copying can be reduced by alternating which array is used for input and
23+
which array is used for output for the different run lengths (not shown
24+
here). This algorithm can easily be adapted to take advantage of partial
25+
sortedness of the initial array. For example, instead of using fixed
26+
run lengths starting with 1, natural merge sort uses whatever runs exist
27+
in the data. Bottom up merge sort can also be adapted to linked lists
28+
(extra space and copying is not needed as we can just change the pointers
29+
in the list, though extra traversal of the list must be done) and versions
30+
of merge sort are often the preferred sorting algorithms in declarative
31+
languages that use such data structures extensively. Because merge
32+
sort only does sequential scans of the input and output at each stage,
33+
it can also be adapted to sorting large quantities of data that do not
34+
fit into main memories. Historically, when data was primarily stored on
35+
magnetic tape, versions of bottom up merge sort were absolutely essential.
36+
There are recursive "top down" versions of merge sort, shown elsewhere.
37+
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import parse from '../../pseudocode/parse';
2+
3+
export default parse(`
4+
\\Note{ This is a copied+modified version of the top down mergesort pseudocode.
5+
Merge is identical and bookmarks have been left in (but more will be needed
6+
for the code that is not the same). Ideally the animation sould look as
7+
similar to the top down version as possible. There is no call stack but
8+
runlength could be displayed where the top down stack is displayed.
9+
\\Note}
10+
11+
\\Code{
12+
Main
13+
// Sort array A[1]..A[size] in ascending order
14+
Mergesort(A, size)
15+
runlength <- 1 // each element is a (sorted) run of length 1
16+
while runlength < size
17+
\\Expl{ We stop when the whole array is a single run.
18+
\\Expl}
19+
\\In{
20+
merge all consecutive pairs of runs of length runlength \\Ref MergeAll
21+
runlength <- runlength * 2 // merging runs doubles the run length
22+
\\In}
23+
\\Code}
24+
25+
\\Code{
26+
MergeAll
27+
left <- 1
28+
while left + runlength < size
29+
\\Expl{ Unless size is a power of two there can be times when the
30+
number of runs is odd and we have a "leftover" run at the end
31+
(with length <= runlength), that will be merged in a later iteration.
32+
\\Expl}
33+
\\In{
34+
mid <- left + runlength - 1 // first run is A[left..mid]
35+
right <- minimum(mid+runlength, size) // next is A[mid+1..right]
36+
\\Expl{ The rightmost run in A may be shorter than runlength
37+
\\Expl}
38+
merge A[left..mid] and A[mid+1..right], with the result in A \\Ref MergeCopy
39+
left <- right + 1 // skip to the next pair of runs (if any)
40+
\\In}
41+
\\Code}
42+
43+
\\Note{
44+
XXXXXXXXXXXXXXXXXXXXXXXXXXXXX following verbatim from top-down mergesort
45+
\\Note}
46+
47+
\\Code{
48+
MergeCopy
49+
Merge(A, left, mid, right, B) \\Ref Merge
50+
\\Expl{ Takes two sorted array segments, A[left..mid] and A[mid+1..right],
51+
and merges them together to form a single sorted array segment
52+
in temporary array B[left..right].
53+
The animation shows values being deleted from A since they
54+
are no longer needed (they are actually still there).
55+
\\Expl}
56+
Copy merged elements back to A \\B copyBA
57+
\\Expl{ Copy elements from B[left..right] back to A[left..right].
58+
Copying can be reduced by merging
59+
from A to B and from B to A in alternate levels of recursion -
60+
a slightly more tricky coding.
61+
The animation shows values being deleted from B since they
62+
are no longer needed (they are actually still there).
63+
\\Expl}
64+
\\Note{ Might be better to move above to overview.
65+
\\Note}
66+
\\Code}
67+
68+
\\Code{
69+
Merge
70+
ap1 <- left \\B ap1
71+
max1 <- mid \\B max1
72+
\\Expl{ ap1 scans through the segment A[left..mid], "pointing at" or
73+
indexing elements of this array segment we copy from.
74+
\\Expl}
75+
ap2 <- max1+1 \\B ap2
76+
max2 <- right \\B max2
77+
\\Expl{ ap2 scans through the segment A[mid+1..right], "pointing at" or
78+
indexing elements of this array segment we copy from.
79+
\\Expl}
80+
bp <- ap1 \\B bp
81+
\\Expl{ bp scans through the segment B[left..right], "pointing at" or
82+
indexing elements of this array segment we copy to.
83+
\\Expl}
84+
while both A segments still have elements to copy \\Ref MergeWhile
85+
\\Expl{ we scan through both A segments from left to right by
86+
incrementing ap1 and ap2, copying to B as we go.
87+
The animation shows values being deleted from A since they
88+
are no longer needed (they are actually still there).
89+
\\Expl}
90+
\\In{
91+
copy the smaller A element, increment its pointer and bp \\Ref CopySmaller
92+
\\Expl{ The smaller of A[ap1] and A[ap2] is copied to B[bp].
93+
\\Expl}
94+
\\In}
95+
copy any remaining elements from A to B \\Ref CopyRest
96+
\\Expl{ One of the A segments will have been completely copied;
97+
the other has uncopied elements.
98+
\\Expl}
99+
\\Code}
100+
101+
\\Code{
102+
MergeWhile
103+
while ap1 <= max1 and ap2 <= max2 \\B MergeWhile
104+
\\Expl{ Elements up to max1/max2 must be copied; those before
105+
ap1/ap2 have been copied already.
106+
\\Expl}
107+
\\Code}
108+
109+
\\Code{
110+
CopySmaller
111+
if A[ap1] < A[ap2] \\B findSmaller
112+
\\In{
113+
B[bp] <- A[ap1] \\B copyap1
114+
\\Expl{ The animation shows the value being deleted from A[ap1] since it
115+
is no longer needed (it is actually still there).
116+
\\Expl}
117+
ap1 <- ap1+1 \\B ap1++
118+
bp <- bp+1 \\B bp++
119+
\\Note{ Clearer to duplicate this in then and else branches(?)
120+
\\Note}
121+
\\In}
122+
else
123+
\\In{
124+
B[bp] <- A[ap2] \\B copyap2
125+
\\Expl{ The animation shows the value being deleted from A[ap2] since it
126+
is no longer needed (it is actually still there).
127+
\\Expl}
128+
ap2 <- ap2+1 \\B ap2++
129+
bp <- bp+1 \\B bp++_2
130+
\\In}
131+
\\Code}
132+
133+
\\Code{
134+
CopyRest
135+
copy A[ap1..max1] to B[bp..] \\B CopyRest1
136+
\\Note{ Need to expand this? I dont think so.
137+
\\Note}
138+
copy A[ap2..max2] to B[bp..] \\B CopyRest2
139+
\\Expl{ One of these copy steps will do nothing because one of the
140+
A segments will be empty. If ap2 is not shown in the animation
141+
it is max2+1, off the end of the array.
142+
The animation shows values being deleted from A since they
143+
are no longer needed (they are actually still there).
144+
\\Expl}
145+
\\Code}
146+
147+
`);

0 commit comments

Comments
 (0)