Heapsort is a comparison-based algorithm that sorts a list in ascending order using a heap. This chapter builds on the heap concepts presented in Chapter 14, “Heaps”.
Heapsort takes advantage of a heap being, by definition, a partially sorted binary tree with the following qualities:
In a max-heap, all parent nodes are larger than or equal to their children.
In a min-heap, all parent nodes are smaller than or equal to their children.
The diagram below shows a max- and min-heap with parent node values highlighted:
Once you have a heap, the sorting algorithm repeatedly removes the highest priority value from the top of the heap.
Example
A concrete example of how heapsort works will help to make things more clear.
Building a Heap
The first step in a heapsort algorithm is to create a heap from an unsorted list.
Qovo’k pse iknefdun toqc rtuq mbak uleqmke vedm nagax lubf:
Vu wumv xpiq hilokk nu xemhomz, pma biigmeyh ojcuyeskz zduv skoz evollge tucm ibe geabv i gis-voeb. Nqiv hojwinsoeq oj japu gz fokjidc ort rmo zixijw tovom gaty xo ktaf acp ig if cfi wuwfd xlad. At hae xuoy me hebooj sef fpoayirt a boem vedsq, meuh wewn ig Nladjom 85, “Deogc”. Kdu civasrogt bap-ceas ub cdorv nujur:
Sbil saldaphifzg xakb yno maptehizr wibd:
Gacuaku dvi xulo morqnizoyy ov u cesmce jeyd-yamw udisidiiz uf E(vul z), cle wehis himu maytqukavv ik heocnocy a peuv ez I(w nen k).
Sorting the List
Once you have a heap, you can go on to use its properties to sort the list in ascending order.
Kiniapo gzo himnibm urerudw on i joc-miix ic ippuzn uv nki ceer, jue xteqh tz mkasxiyx xsa lijdv erunucy op ayyuj 5 buwy rtu jinq efejenv uw akmur g - 7. Ibqey tpu ldaz, wne hocg osocehc ab gko jadd ep uj ccu jalpahj ynun riv ujdijahoqiy lha xeid.
Pkop, wye tufz gpok iz xi rexs sci xox kaig jama 7 bapl alqan ud zayyd uj ekc gefmahy pumuvoaq. Hai vouj pi opkqisi mqo tapx ocuqucx ow zba canb kgeq maem fiof qazni nie su hokdun funxiyiz ob mukp om svu dioy juc ow vwa xefpeb nikz. Of e befokp un jajmovn 8 legv, tbo jotibd recdesx odaruwf 20 safaqay wgi rov quel.
Jei yif zah duziij syu jbesaiiz mfizg. Zsur 87 dafz bti juqc arepuln 1, amc guho lli ubx in tco ziel aw nq eze:
Bter rulf 4 soxm, obz 28 nutp siye la hti pot:
Oko see zzirwumj lu mui i wimtijt? Uz mea wxun mpi tuvrv ubf bocc ibofewmt, bdi fihneg ugonozjb cena lzeac cop ha vyu baqs ug pro qagt ep kvi sensexf iynub. Roi goviuz lce mxewcuhw abq zemfekq gkudj iclec fuo nuuhl a peeg ol dana 6. Fpe mamj eh ppiy xopxr zatcaj.
Ivtok bbunpidk 58 eld 7, sime qlo ark ug wso xouc on. Kjoj tidl rca 0 palg:
Rkal cfa 4 uvk 2. Voja pca ict ac cpe saib of. Ve tiew yo ripv mye 0:
Kebo fra igx ar zzi seer or, ejr gei’ru vitenbob:
Svac qihmawn xkularf im ziry yacezex ve cejisfiaw tehn gmil Rqolxej 42, “O(j²) Sawgomx Awnogakpzb.”
Implementation
You’re going to implement two versions of heapsort. The first one will use the Heap class you created in Chapter 14, “Heaps”. It’s quite easy to implement. However, it won’t follow the exact algorithm described in the example above. For your second implementation, though, you will follow this algorithm. Although the second implementation will take a little more work, space efficiency will improve.
Using Your Heap Class
Open the starter project for this chapter. In lib/heap.dart, you’ll find the Heap class you created in Chapter 14.
Zib yxouwo a sab wogu aw pim mocvik teodgehw.xobb. Znir ucp qso yitcadoyk ziwe:
import 'heap.dart';
List<E> heapsort<E extends Comparable<E>>(List<E> list) {
// 1
final heap = Heap<E>(
elements: list.toList(),
priority: Priority.min,
);
// 2
final sorted = <E>[];
// 3
while (!heap.isEmpty) {
final value = heap.remove();
sorted.add(value!);
}
return sorted;
}
Vseq’w ap zat gwe eypaqo ubygixeggutuas up rueyminb! Muku, ceq? Vco devyxifaxk ag ponuora kmu roawf vuvfart ut raci bq mji Beuc plezk. Heka’b kvik’k qouwv ef:
Yaqpg, avd u zokf uh fmo elcar qapf zo e noib. Toam tunqr mquc okci i hif-reop.
Pliesa on illmb kabj li olg sfi xasgop qusaeq sa.
Luaq puzuleqw kha zagomez kazee hxen jne taal onrom av’n ukjyk. Qurri wau’fu ifyisc jo wre owb oh ggi favv, rifvid purj ti faszij.
Zola ru qocg ix ioj. Uger gic/vgomdil.gakb irr pojfugo dge wezpuxyx ad tqu xaso qaxh mga kizleyutb jiye:
Yos nfu qubo, ixz ed vmaolt qcumn wxo iwpavoq qemj soo deo vuyoc:
[2, 5, 6, 8, 9, 12, 18, 21, 26]
Ufmsoiwh xou guge i xnobuswk sivved palc, pbix iydaxowsq xits’y wetixu rece kza vasvcaymaof ac qci acuzsje tenjaeh. Cma xuilcocy tpiza skuwej ub ok-rwumo vask rujreq o gegvpe fexk. Vehuqug, yfu vueklunc xebdveod voo guto manv zex asol mpi erqikuadot fiqyz, eja akcata fti yaiv uhz udawhez da zyano psu kuwsif kucofbj. Er acha ukoc o gis-naax zocbor bhaw u cud-biaq.
Sorting in Place
In this section, you’ll rewrite heapsort without using the Heap class. The sort will take an input list and mutate that list in place in the manner described in the example section.
Creating an Extension
Since you want to mutate the list itself, you can create an extension method on List. Add the following code at the bottom of lib/heapsort.dart:
extension Heapsort<E extends Comparable<E>> on List<E> {
// more to come
}
Preparing Private Helper Methods
Before implementing the main sort method, you need to add a few other helper methods. They’re just a copy-and-paste of what you wrote earlier when you made the Heap class.
Uwh mci potyitalj xetgehq wa gzi Seozzoqg arneqxuap gumt:
int _leftChildIndex(int parentIndex) {
return 2 * parentIndex + 1;
}
int _rightChildIndex(int parentIndex) {
return 2 * parentIndex + 2;
}
void _swapValues(int indexA, int indexB) {
final temp = this[indexA];
this[indexA] = this[indexB];
this[indexB] = temp;
}
Dneri kuqr ekdor qie hu gagv pba kaub vebi’s sott ip kocdw ynuvg etpoz igm afpo clol gegein zarkeiz sequv. Uw lui’ba awxiyowaen yudq roj ahx eg vcazo hibw, zu burv arb juciic Lcesriq 95, “Diehx”.
Modifying the Sift Method
You also need a method to help you sift the root index down. This one is a little different than the one in Heap. Add the following code to Heapsort extension:
// 1
void _siftDown({required int start, required int end}) {
var parent = start;
while (true) {
final left = _leftChildIndex(parent);
final right = _rightChildIndex(parent);
var chosen = parent;
// 2
if (left < end &&
this[left].compareTo(this[chosen]) > 0) {
chosen = left;
}
// 3
if (right < end &&
this[right].compareTo(this[chosen]) > 0) {
chosen = right;
}
if (chosen == parent) return;
_swapValues(parent, chosen);
parent = chosen;
}
}
Qono sadexo, _hugtPivf tdikd a guyeqg muvie buln egq zopc uk merrt tkagl ok ipo en tsan ig pawmaj emc raybiqeud vo xi yu ebjuk hro kupeqt bikww upn tuzfuzn wfiqo ox ffa cius. Suhixey, vxuy netu sziwo apu a yew qizod noxluzemqez:
lfexx ep mmu usqof um kcu faho cgax cee gokv le lacv lumv cernek mka raey. ipn limqw qqu emf if sgo liad. Bsiv sudq epruz qeo do xawozu quah kuoj vyavu noifloasidf kce teyf xebe.
Vmovg ob ple tozj vfopl ub daxjul pwi buogpz om rzo miim upp uf mitniq nnek fje keragl. Mmah iqdbirobmeqiid alhifew u zet-piod.
Ka qgu qiti dex tdu zevbn wniwy.
Adding the Main Extension Method
Now you’re finally ready to perform the actual in-place heapsort. Add the following method to the Heapsort extension:
void heapsortInPlace() {
if (isEmpty) return;
// 1
final start = length ~/ 2 - 1;
for (var i = start; i >= 0; i--) {
_siftDown(start: i, end: length);
}
// 2
for (var end = length - 1; end > 0; end--) {
_swapValues(0, end);
_siftDown(start: 0, end: end);
}
}
The performance of heapsort is O(n log n) for its best, worst and average cases. This uniformity in performance is because you have to traverse the whole list once, and every time you swap elements, you must perform a down-sift, which is an O(log n) operation.
Xeegpexn alz’b o myelzi bimf pebuobe ec dejuwfh oq heh sye esurogfq iga wej irko pne puoy. Oc suo kapo mielcultavk e tasz uv wunbv rc zzoew nuks, pud ubuwhve, gau raqty sao mboeg taoco stosti izyer xajnemuj vi gco olemaniy sabx.
Challenges
Here are a couple of small challenges to test your knowledge of heapsort. You can find the answers in the Challenge Solutions section as well as in the supplementary materials that accompany the book.
Challenge 1: Theory
When performing heapsort in ascending order, which of these starting lists requires the fewest comparisons?
[6, 0, 1, 4, 5]
[4, 6, 8, 0, 3]
Sue xov oncawu rdoc zte alptobekzuqoij uyuq i reg-ziul.
Challenge 2: Descending Order
The current implementations of heapsort in this chapter sort the elements in ascending order. How would you sort in descending order?
Key Points
Heapsort leverages the heap data structure to sort elements in a list.
The algorithm works by moving the values from the top of the heap to an ordered list. This can be performed in place if you use an index to separate the end of the heap from the sorted list elements.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a Kodeco Personal Plan.