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
-
20
- /* eslint-disable no-multi-spaces,indent,prefer-destructuring,brace-style */
21
1
import GraphTracer from '../../components/DataStructures/Graph/GraphTracer' ;
22
2
import ArrayTracer from '../../components/DataStructures/Array/Array1DTracer' ;
23
- import { areExpanded } from './collapseChunkPlugin' ;
24
-
25
- // k displayed only if first BuildHeap is expanded
26
- // Note: This is only needed in the last chunk of BuildHeap. The code
27
- // looks like it displays k throughout BuildHeap but when BuildHeap is
28
- // collapsed, only the last chunk is rendered so the other chunks don't
29
- // matter and we can avoid testing what is expanded there. Another
30
- // approach would be to use a wrapper function for assigning to k, which
31
- // checks isBuildHeapExpanded() (it doesn't generalise well for i and j
32
- // though).
33
- function isBuildHeapExpanded ( ) {
34
- return areExpanded ( [ 'BuildHeap' ] ) ;
35
- }
36
-
37
- // i, j (in build) displayed only if first DownHeap is expanded
38
- // See Note in isBuildHeapExpanded()
39
- function isDownHeapkExpanded ( ) {
40
- return areExpanded ( [ 'BuildHeap' , 'DownHeapk' ] ) ;
41
- }
42
3
43
4
export default {
44
5
initVisualisers ( ) {
@@ -53,249 +14,4 @@ export default {
53
14
} ,
54
15
} ;
55
16
} ,
56
-
57
-
58
- /**
59
- *
60
- * @param {object } chunker
61
- * @param {array } nodes array of numbers needs to be sorted
62
- */
63
- run ( chunker , { nodes } ) {
64
- // create a copy, can't simply let A = nodes because it creates a reference
65
- // sort A in-place will cause nodes sorted as well
66
- const A = [ ...nodes ] ;
67
- let n = nodes . length ;
68
- let i ;
69
- let heap ;
70
- let swap ;
71
-
72
- chunker . add (
73
- 1 ,
74
- ( vis , array ) => {
75
- vis . heap . setHeap ( array ) ;
76
- // tell the graph renderer that it is heapsort
77
- // so that the array index should start from 1
78
- vis . array . set ( array , 'heapsort' ) ;
79
- } ,
80
- [ nodes ] ,
81
- ) ;
82
-
83
- const highlight = ( vis , index , primaryColor = true ) => {
84
- if ( primaryColor ) {
85
- vis . heap . visit ( index + 1 ) ;
86
- vis . array . select ( index ) ;
87
- } else {
88
- vis . heap . select ( index + 1 ) ;
89
- vis . array . patch ( index ) ;
90
- }
91
- } ;
92
-
93
- const unhighlight = ( vis , index , primaryColor = true ) => {
94
- if ( primaryColor ) {
95
- vis . heap . leave ( index + 1 ) ;
96
- vis . array . deselect ( index ) ;
97
- } else {
98
- vis . heap . deselect ( index + 1 ) ;
99
- vis . array . depatch ( index ) ;
100
- }
101
- } ;
102
-
103
- const swapAction = ( b , n1 , n2 ) => {
104
- chunker . add ( b , ( vis , _n1 , _n2 ) => {
105
- vis . heap . swapNodes ( _n1 + 1 , _n2 + 1 ) ;
106
- vis . array . swapElements ( _n1 , _n2 ) ;
107
- } , [ n1 , n2 ] ) ;
108
- } ;
109
-
110
- /** NOTE: In Lee's code, array index starts from 1
111
- * however, in JS, array index naturally starts from 0
112
- * index start from 0:
113
- * parent = k , left child = 2*k + 1, right child = 2*k + 2
114
- * index start from 1:
115
- * parent = k , left child = 2*k, right child = 2*k + 1
116
- */
117
-
118
- // keep track of last node highlighted due to i (or k) so we can
119
- // unhighlight it of buildHeap is collapsed
120
- let lastiHighlight ;
121
-
122
- // build heap
123
- // start from the last non-leaf node, work backwards to maintain the heap
124
- for ( let k = Math . floor ( n / 2 ) - 1 ; k >= 0 ; k -= 1 ) {
125
-
126
- let j ;
127
- const tmp = i ;
128
- i = k ;
129
-
130
- chunker . add ( 4 , ( vis , index1 , index2 ) => {
131
- vis . array . assignVariable ( 'k' , index1 ) ;
132
- // if (tmp != null) { // XXX looks dodgy using tmp here?
133
- if ( index2 != null ) {
134
- unhighlight ( vis , index2 ) ;
135
- vis . array . removeVariable ( 'j' ) ;
136
- }
137
- highlight ( vis , index1 ) ;
138
- } , [ i , tmp ] ) ;
139
-
140
- chunker . add ( 6 , ( vis , index1 , index2 ) => {
141
- vis . array . assignVariable ( 'i' , index1 ) ;
142
- } , [ i , tmp ] ) ;
143
-
144
- lastiHighlight = k ;
145
- heap = false ;
146
- chunker . add ( 7 ) ;
147
-
148
- chunker . add ( 8 ) ;
149
- // if current node's left child's index is greater than array length,
150
- // then current node is a leaf
151
- while ( ! ( 2 * i + 1 >= n || heap ) ) {
152
- chunker . add ( 10 ) ;
153
-
154
- // left child is smaller than right child
155
- if ( 2 * i + 2 < n && A [ 2 * i + 1 ] < A [ 2 * i + 2 ] ) {
156
- j = 2 * i + 2 ;
157
- chunker . add ( 11 , ( vis , index ) => {
158
- highlight ( vis , index , false ) ;
159
- vis . array . assignVariable ( 'j' , index ) ;
160
- } , [ j ] ) ;
161
- } else {
162
- j = 2 * i + 1 ;
163
- chunker . add ( 13 , ( vis , index ) => {
164
- highlight ( vis , index , false ) ;
165
- vis . array . assignVariable ( 'j' , index ) ;
166
- } , [ j ] ) ;
167
- }
168
-
169
- chunker . add ( 14 ) ;
170
- // parent is greater than largest child, so it is already a valid heap
171
- if ( A [ i ] >= A [ j ] ) {
172
- heap = true ;
173
- chunker . add ( 15 , ( vis , index , lastH ) => {
174
- unhighlight ( vis , index , false ) ;
175
- // possible last chunk in BuildHeap/DownHeapk
176
- // remove i, j if !isDownHeapkExpanded
177
- if ( ! isDownHeapkExpanded ( ) ) {
178
- vis . array . removeVariable ( 'i' ) ;
179
- vis . array . removeVariable ( 'j' ) ;
180
- }
181
- // remove k+highlighting if !isBuildHeapExpanded
182
- if ( ! isBuildHeapExpanded ( ) ) {
183
- vis . array . removeVariable ( 'k' ) ;
184
- unhighlight ( vis , lastH ) ;
185
- }
186
- } , [ j , lastiHighlight ] ) ;
187
- } else {
188
- swap = A [ i ] ;
189
- A [ i ] = A [ j ] ;
190
- A [ j ] = swap ;
191
- swapAction ( 17 , i , j ) ;
192
- lastiHighlight = j ;
193
- chunker . add ( 18 , ( vis , p , c , lastH ) => {
194
- unhighlight ( vis , p , false ) ;
195
- vis . array . assignVariable ( 'i' , c ) ;
196
- // remove i, j if !isDownHeapkExpanded
197
- if ( ! isDownHeapkExpanded ( ) ) {
198
- vis . array . removeVariable ( 'i' ) ;
199
- vis . array . removeVariable ( 'j' ) ;
200
- }
201
- // remove k+highlighting if !isDownHeapkExpanded
202
- if ( ! isBuildHeapExpanded ( ) ) {
203
- vis . array . removeVariable ( 'k' ) ;
204
- unhighlight ( vis , lastH ) ;
205
- }
206
- } , [ i , j , lastiHighlight ] ) ;
207
- i = j ;
208
- }
209
- }
210
- }
211
-
212
- // sort heap
213
-
214
- while ( n > 1 ) {
215
- chunker . add ( 20 , ( vis , nVal , index ) => {
216
- // clear variables & show 'n'
217
- vis . array . clearVariables ( ) ;
218
- vis . array . assignVariable ( 'n' , nVal - 1 ) ;
219
- unhighlight ( vis , index ) ; // XXX skip for first loop iteration?
220
- } , [ n , i ] ) ;
221
-
222
- let j ;
223
- swap = A [ n - 1 ] ;
224
- A [ n - 1 ] = A [ 0 ] ;
225
- A [ 0 ] = swap ;
226
-
227
- chunker . add ( 21 , ( vis , index ) => {
228
- highlight ( vis , index ) ;
229
- highlight ( vis , 0 , false ) ;
230
- } , [ n - 1 ] ) ;
231
- swapAction ( 21 , 0 , n - 1 ) ;
232
-
233
- chunker . add ( 22 , ( vis , index ) => {
234
- unhighlight ( vis , index , false ) ;
235
- vis . array . sorted ( index ) ;
236
- vis . heap . sorted ( index + 1 ) ;
237
-
238
- vis . array . assignVariable ( 'n' , index - 1 ) ;
239
- } , [ n - 1 ] ) ;
240
- n -= 1 ;
241
-
242
- i = 0 ;
243
- chunker . add ( 24 , ( vis , index1 , nVal ) => {
244
- vis . array . assignVariable ( 'i' , index1 ) ;
245
- } , [ i , n ] ) ;
246
-
247
- chunker . add ( 25 ) ;
248
- heap = false ;
249
-
250
- chunker . add ( 26 , ( vis , nVal ) => {
251
- // if (nVal === 0) vis.array.clearVariables();
252
- } , [ n ] ) ;
253
- // need to maintain the heap after swap
254
- while ( ! ( 2 * i + 1 >= n || heap ) ) {
255
- chunker . add ( 28 ) ;
256
-
257
- if ( 2 * i + 2 < n && A [ 2 * i + 1 ] < A [ 2 * i + 2 ] ) {
258
- j = 2 * i + 2 ;
259
- chunker . add ( 29 , ( vis , index ) => {
260
- highlight ( vis , index , false ) ;
261
- vis . array . assignVariable ( 'j' , index ) ;
262
- } , [ j ] ) ;
263
- } else {
264
- j = 2 * i + 1 ;
265
- chunker . add ( 31 , ( vis , index ) => {
266
- highlight ( vis , index , false ) ;
267
- vis . array . assignVariable ( 'j' , index ) ;
268
- } , [ j ] ) ;
269
- }
270
-
271
- chunker . add ( 32 ) ;
272
- if ( A [ i ] >= A [ j ] ) {
273
- heap = true ;
274
- chunker . add ( 33 , ( vis , index ) => {
275
- unhighlight ( vis , index , false ) ;
276
- } , [ j ] ) ;
277
- } else {
278
- swap = A [ i ] ;
279
- A [ i ] = A [ j ] ;
280
- A [ j ] = swap ;
281
- swapAction ( 35 , i , j ) ;
282
- chunker . add ( 36 , ( vis , p , c ) => {
283
- unhighlight ( vis , p , false ) ;
284
- vis . array . assignVariable ( 'i' , c ) ;
285
- } , [ i , j ] ) ;
286
- i = j ;
287
- }
288
- }
289
- }
290
- chunker . add ( 37 , ( vis ) => {
291
- // Put in done state
292
- vis . array . clearVariables ( ) ;
293
- vis . array . deselect ( 0 ) ;
294
- vis . array . sorted ( 0 ) ;
295
- vis . heap . sorted ( 1 ) ;
296
- unhighlight ( vis , 0 , true ) ;
297
- } ) ;
298
- // for test
299
- return A ;
300
- } ,
301
17
} ;
0 commit comments