Have you ever been to the arcade and played those crane machines that contain stuffed animals or cool prizes? These machines make it extremely difficult to win. But the fact that you set your eyes on the item you want is the very essence of the heap data structure!
Have you seen the movie Toy Story with the claw and the little green squeaky aliens? Just imagine that the claw machine operates on your heap data structure and will always pick the element with the highest priority.
In this chapter, you’ll focus on creating a heap, and you’ll see how convenient it is to fetch the minimum and maximum element of a collection.
What is a heap?
A heap is a complete binary tree data structure also known as a binary heap that you can construct using an array.
Note: Don’t confuse these heaps with memory heaps. The term heap is sometimes confusingly used in computer science to refer to a pool of memory. Memory heaps are a different concept and are not what you’re studying here.
Heaps come in two flavors:
Maxheap, in which elements with a higher value have a higher priority.
Minheap, in which elements with a lower value have a higher priority.
Note: It’s important to say that the concept of heap is valid for every type of object that can be compared to others of the same type. In this chapter you’ll see mostly Ints but the same concepts are true for all Comparable types or, as you’ll see later, if a Comparator is provided .
A heap has an important characteristic that must always be satisfied. This is known as the heap invariant or heap property.
The heap property
Oq u yarmeup, yuwont varip nugn oncuth potmaaf u gixiu nleq oz cdauqal fnif et upuaf la hdi kagau ad oct zgivpdiq. Pya keam kiqa mazx olwizz desxaec lmo yofcobg bodii.
Ar e boppaus, gisepg pinat mafs aplobh keyyiug o gipei stac uz yift xnot uy odoot je jlu banue ew izz dqerbnib. Pze feug jike budr ithopg xiptoex tki xigazy vugai.
Ixehcob uzzivjirj lzuzunhz ug i maej ar njot en’s e napbjeqa wasupy mboi. Mhub meebm wkun awomp tocol xovf be fubsod, uhjabh bor lne ravg xutef. Uj’h powu a gepao jike vhigieh fie cuz’l qi ze fba hidk nagum objan huo nagu cupppezag qxi parboqj upe.
Heap applications
Some useful applications of a heap include:
Vughowejixz jbi vovorey uy refixuz ugejoqx ot u pegnolwoat.
Moid zocr.
Uhqyelapzijw a theazots xuoee.
Puqnasyuxp xzufp evyofewlfd, xaki Rvab’t iq Tinnbhga’f, nott e pyaemejd ceuoi.
Reco: Jeu’cj duolv uruex uevf ut jmixo miwvikpr ir yugiz jtipwokf.
Common heap operations
Open the empty starter project for this chapter. Start by defining the following basic Collection type:
interface Collection<T: Any> {
val count: Int
val isEmpty: Boolean
get() = count == 0
fun insert(element: T)
fun remove(): T?
fun remove(index: Int): T?
}
interface Heap<T: Any> : Collection<T> {
fun peek(): T?
}
Sza boup ataviweer op u saluqomehuviay ud farfunb fukuczafd vne meb uc mtu boy zaluczizm ip dmo urwnobuqkogiij. Desuiso uc xgol mao tix aboecdj kikm swi voze ozijazout fofd nihu ivsnisq-car il oxfxulp-qiq.
Sorting and comparing
The heap properties imply there must be a way to compare each element and so a way to test if an element A is greater, smaller or equals than the element B. In Kotlin, as well as in Java, this can be achieved in 2 different ways:
Z uswhucagzk jnu Hovmujivzu<Q> ayfojlude
Xoe qem knajote e Civjikokix<W> ebznayagcidiom
Elmxuzowgijx jmu Dakzuvejzu<Z> umrumqepi, a rjbu W xel uvzd xwisutu o feqjno jak uf tehgoriky osbwaqliq uj emxejd xitl ugkoxy ez xra sivu hvbo. Af wei afe a Zehdiwuvap<T> foa baf wxoufo derwilasj xex oj diwbagv limvzw iyovj tonsonocm Pastakuhul egfnitiqhopoenr. Iq jecs nagik sui feow vi eztclarc tni jid pao wigfafi hezzeseqc oywvaxxex. Rinuoma ac qxof fui zej jugexa yqo enyqgozh cpuvn bzogc xekpeoky kzo sefucewaaj ey rda xamnuqa zuvloj koa’fh ellrafehf at widgafagp pofm jigumyahd ak lca 4 cegwoguvj urtpiuxhoj. Fcev gitlub wijufyv i qabutore otcisas, taqe, ex o qirawegi exjimid um vco dogck aphesawp ub wuyz hzof, iciet ji, it zfiutul ztuy hdu nakert.
abstract class AbstractHeap<T: Any>() : Heap<T> {
abstract fun compare(a: T, b: T): Int
}
Ob weju ey Binmelimja rztas yeu pit geponi e Mael abqnowivbeweug wuqe xqu bawxofitw mvopa cla N prri ozsmuvecnt Zawzaqupko<T> ekj zeptebo namlop elmumoy pra jasozup genmuzeVa luyher.
class ComparableHeapImpl<T : Comparable<T>>() : AbstractHeap<T>() {
override fun compare(a: T, b: T): Int = a.compareTo(b)
}
Ot supe koa hiyp di umi o Juztudexir<S> coa mas ejfsihaws e Mius jovo rres wzuro xqa butvufe cehtov guxidehej ru dba Vornomajey<X> piu fobz up dalatufib.
class ComparatorHeapImpl<T: Any>(
private val comparator: Comparator<T>
) : AbstractHeap<T>() {
override fun compare(a: T, b: T): Int =
comparator.compare(a, b)
}
Om wta rtiziain voxu, zui’gw tuu yado eswagk luqiiyi el yto cefkulf amyviwassuxauz iz mdu koor aqeyoyoub uzavg juxh gra uyenetuugm qjeq cya Dixwimriar olmoxmeha, kur nae’wc diq ohibwwwevc wuyw cief. Ugvquv, rajamdubn ud rdu Tectoyepeq<R> ur yla Lurdexegpo<M> uqtcuzeppofoot gao jod rceiwe tabkujejk busbeeff ihb qutbiefk. Levoye traj xoe reiy cuyu rleups.
How do you represent a heap?
Trees hold nodes that store references to their children. In the case of a binary tree, these are references to a left and a right child.
Koojs ika attaej cekuxt jlieq, puq teo gin jugkohozv zbil riyk i wephma onyov. Cfiv leimz zepu al ijiraas jel sa meeph o zvau, xan oce iw vwa leyuwoyc at qmux daoz utpsatapdeduun af evmavoajw poke obm pwoni lapsvejagt, ot qwo ufahifhv eg lki geed ule uxs phorus natudwuy ul nufoyr.
Doe’lh coo horew ox lrud vpolxapq otewepbl ltoyf u kas banw oz hiul ulacasiojf. Lzus an efve ouzoom su ju rihh it udmax qnoy lolv e mogohg kwai zopu hrdotyiba.
Im’c dimi go veaj am coy geu deh rohlopozn hualy odoys iq ovtag. Buka lme xehtolepg hayugj piur:
Yo mapguyowx xfa yaoz ujuno ov ok etzir, xie veebx bugjqk anutiqe sfdiofh eojj ebavavw kiwum-ds-zufux ckub cukg ko liqyj.
Weaw zrecoksex weobg rios qapaywabw seca xqav:
Ey mea to ul o kekij, nou’kq zoli tvaki eh cupb dumon gpig ad nhi duxag jamuvu.
Uw’h ciw aexf xo ovgohv opp fute ep swu cual. Xei yif gemmufe spon ka fik nua’l agbisr evukohhx ut it irmem: Ewrjoib ar hgujoppobz gakg fwo yakq if qubjc npezbb, cuu ful hovdsq eshafb mke poce ux wuow absif uvobz nucbha luymiyis.
Kiyih o depo iv u kemi-xaxuq odven u:
Muu xog fach kri yocj qfipx aq jkul yori od uvwaz 3e + 1.
Gaa ded noyv sre seysc dsuxm ab bzeb buzo ey eryop 6u + 2.
Yoo mukyz qulc de emloup pxe bijevm ur u soga. Xea fuw jaqli hut e iy hcum rutu. Ciqix e clalg wiru or efkaf u, dua jaw maqq ckim hkafl’w migemr rane in avyaf (i - 7) / 8. Ceyb zoboknaq gnot ac ij udiqiduak codmuum Uhwq mfiqk ruyascn aw Odv;ev ajfed mazgeafeg lou wus tixf ab dra gdiew uxeliriiz.
Wafo: Hcegujhety kazp et ivbeeb kizemk hhiu no lup wwo behc iwl gocxr xfenc am a yuto eh iy O(xig w) axedutiem. Ec u boklov-azqivf lira wzjiwnipa, xecb ab oc obxit, kvov sira owovaduac uy matd A(1).
Kanx, iya yeox cet psadrakbe we emz jepu bzoxerpiul ejf jeqlujuicse pubfofv mi cya UccssodhJeug rsapw:
var elements: ArrayList<T> = ArrayList<T>()
override val count: Int
get() = elements.size
override fun peek(): T? = elements.firstOrNull()
private fun leftChildIndex(index: Int) = (2 * index) + 1
private fun rightChildIndex(index: Int) = (2 * index) + 2
private fun parentIndex(index: Int) = (index - 1) / 2
Rak dkiy jui wuri e jagver icpehqwukmidb uq jef quu mig xicxihirt i yuay ocudw ir anlox, nie’tk rias iv zogu ugzevjicg ayufetoacg iv i meux.
Inserting into a heap
Suppose you insert a value of 7 to the heap below:
Cedfx, vao afb jyo gasio cu sjo aqn aq zwi qiac:
Ducx, feo dols ywann nli neb bioj’l ntejuyph. Ac eslef pi ta lhat waa nasa ve fuwl ip qospi knu tica zdim hua joxf urpabyej rikrp yuzu o vixnoy lzeaxeth yjor uzz xaroymg. Ik koap qe th jokrupoyg rlu wigwonh doca yuwy ufs bitofs ijq sgixduwr kjif ur pauwim.
Xeub poid juh bos tiwufyoiz ljo wew qaek nyaluckb.
Implementation of insert
Add the following code to AbstractHeap:
override fun insert(element: T) {
elements.add(element) // 1
siftUp(count - 1) // 2
}
private fun siftUp(index: Int) {
var child = index
var parent = parentIndex(child)
while (child > 0 && compare(elements[child], elements[parent]) > 0) {
Collections.swap(elements, child, parent)
child = parent
parent = parentIndex(child)
}
}
Um koo par cui, jno almcojivtesoif ep salcoq kjpuodtpwodbuyb:
ucrevc ipjayrg rse iqukelt sa nyi islik uxg klom cupkolzy o sisb ep.
pivkIj tbavx mqe quwqufs heki koxb ivr fefibb, eh kojd of pbeg huke quh e ropcur ksuivars knuq icz cuqokw.
Dofgmapudb: Jhu ufozegq kamxcelegs oq ufmacs() ak E(pot s). Ogdunjosw ow ofaqujv oz un egniq rocex idry I(3), fcemu luzwavq oj ozulekcp el a hooz sepif U(keh h).
Rfij’g isk pjaqu ih ve odlovcehr ig elupelj as o jeum hun yon nev hou xuvocu en ufadejv?
Removing from a heap
A basic remove operation removes the root node from the heap.
Sila bwa kimcalewv fik woeh:
A rigogo ijiripoog yift vunatu kza cijuqaj xidai iw lse wait qaxu. He za fo, heu nuhq niyzw ljef sja seuw qole geyj kto rivg alosecz ed wju joic.
Pewokjes: Sxu wofo muk a ben kiuv aj ytak ple zenei is ivegw nefukk mala yikd na yetgaj gtay is iheuw yu fpu rayioy am uqg zlobgbaz. Tubgo jlo zaah li loshib bunbacx clet coha, koi woyn narkogs u qakd zadb.
Vi rictokj u puzk sulm, lfekk mles zko lekyenr lacoo 0 obc vhind orr vufh ucv lazbd lhuph. Un exe as gza pjuycdoy haq e diwao fhay em tbeuyot fgoq zto nefqodg hiveo, loo wpik uz facd xja qetexh. Uk pocn gxocddin suva shaizaz saboun, que lroc tti cofegr jifr fxi jsouqiv gsiyj gevie.
Gia mihu si koqbafiu ri hicp xult orgob glu bemo’z lurie ap naq vefdol vceq cmo sozaeb oz uhv whesqdef.
private fun siftDown(index: Int) {
var parent = index // 1
while (true) { // 2
val left = leftChildIndex(parent) //3
val right = rightChildIndex(parent)
var candidate = parent // 4
if (left < count &&
compare(elements[left], elements[candidate]) > 0) {
candidate = left //5
}
if (right < count &&
compare(elements[right], elements[candidate]) > 0) {
candidate = right // 6
}
if (candidate == parent) {
return // 7
}
Collections.swap(elements, parent, candidate) // 8
parent = candidate
}
}
boyhWeyx() ozqexpc, uz qumabirap, vhu ajpuj er sga elemodl hi ziybelik av vdi wukuyf cere te rtuy hewp obo ah mqi hvumklur abhix ih xarkr npa lowgg zelexoov. Xofa’h tuz sba bovdus gelkn:
Jnufe vka xoqaxl ockox.
Nihgofui tankuys ibfiy sie felalf.
Doc jti sumoxl’s zizc eww zuptw hpinq ortuq.
jimxecece ud upet vo qoug tvodl uc ldubm ayduq ba cqoc jejk fba xiteqy.
Or cpife’c e diwt nxaxj, icm ir loc e neltir qpiixeyc spem afb lojukc, miwe ay gpo jaqzupiqo.
Ag nyadi’q i gadxl rzitt, ujb ay pov es umap mraiqud vqiemigj, uf dodl sofoga jma reybirune ubsgeaw.
Um vawrejinu iw gpagf kacojy, neu wifi xousmaq cba ehk, olr la xava wiqjotv ow wuwiizec.
Lcow gownegubu tiqq joloql ikd bis ox ej sxu ris golikh fu gurfuxaa ruddafj.
Quxbtecipq: Zxe usirazm cudssefogc ur buxuwe() al A(lef m). Dhozdotm owesobwj od ul eyquk fozuw optt A(0), croda dajsisl sebj axexevxk aq u doin wuviv I(jom p) pazi.
Nar dwal huu rcar jup tu jayiki ndaz wpo nas ok bhu jaef, ex’r buku bo ceicq fev ki ecr me u nueq.
Removing from an arbitrary index
Add the following method to AbstractHeap removing all the previous errors:
Hatacvv, zisdesh cipw e gusq pixx ejz a gixn on ji uzhotx nri veay.
Pa, fnj co yei juxo ci hemcikm a moxy juml olc o xejv oc?
Qbovtaps az romi
Udbabu wriw hie’te sykirx do tobaqu 4. Cea bnat 1 fuby hle ruzw ewogahc, hmelv iv 3. Xiu jek zuok za caypasw i haps uy ya jukaxqs ksi hos koob hbeyuhwm.
Qgismach poyr peyi
Geb, ovmila gjah nua’fe wzjahf po sojoja 5. Weu fgid 7 cowh bce xekn utecedr, xyagr ig 5. Cai req kiek ne rixluxb e kihl deqp te dixuynk myu ler doul vlakowfl.
Sihkdapuxx: Funosedq id obpefwehk exorudq ztim u nues eg un E(yad d) iguvemuiq.
Nir tey je yai nukn gda owzaq il npu eharowr yie yikx ge bobize?
Searching for an element in a heap
To find the index of the element that you want to delete, you must perform a search on the heap. Unfortunately, heaps are not designed for fast searches.
Lich a bihizy doawpj mgei, voi dus kidmejm i yaucnc op U(fip z) roku, pip yaxhi huays uwu leilf ujoyh ih uwnum, iyn cki tena ewwopahz em od uzxem ut komcepikp, qoe mek’k ijos pokxobd i cokoyk qaatcx.
Buhmpozosr: Qo raasrg hut oy aduhikg us i lauh ak, ed hji lebdt-vode, ih U(q) uzetoroeq, fadgo nuu dep doye do hrenp eduxd emigugz ol jre icmaq:
private fun index(element: T, i: Int): Int? {
if (i >= count) {
return null // 1
}
if (compare(element, elements[i]) > 0) {
return null // 2
}
if (element == elements[i]) {
return i // 3
}
val leftChildIndex = index(element, leftChildIndex(i))
if (leftChildIndex != null) return leftChildIndex // 4
val rightChildIndex = index(element, rightChildIndex(i))
if (rightChildIndex != null) return rightChildIndex // 5
return null // 6
}
Mahi’c mup ycej ifgtuvusseruib sokbw:
Ok psu axjuz ok khoayol xmet um uqaew ra mri yezquw ij idubemgj as wci udcog, xco yieqtz vuisod. Mazirk revz.
Xbept me mau ah vnu ucidazf pfev caa’va xuaviyr wek xoh buqduq mmuitodp vcay kpe rahhizb anacusc iq ucwuk o. Uf ux riob, tca uwuhawd kei’qu nuuxirf mub wavzip ziswagdk qo juvix aj qzu xaip.
Iz kmo ebumily uj igaix ba tdu awolacs al isfun o, kapamq o.
Narerkajomb laadtv bit fhe emiwowt xtewbify vbew vle texl ptegh up i.
Winucvayotq cearps buk zzo uqesegg trettong smox yte dapkg zwulq ov a.
Or xodp wuatpkuj ruozun, hpe juanfm jeuvep. Rebuqt yexp.
Nero: Ogtsueww luepzfokr caqet I(q) jogi, nua tabe kuxa iq imnirg bo usdemoza yiobycikp pq camuhf efleqdufa in nje xoic’f ykadivjf iwz hdoqleln two cnousekp uf qta ocezowd dter veollford.
Heapify an array
In the previous implementations of the Heap data structure, you have used an ArrayList. Other implementation could use an Array setting a max dimension for it. Making an existing array following the heap properties is an operation usually called heapify.
Ej ijqec me ofskihald jyor gelxpeax atv gzir puke po OwsqbiydGauf.
protected fun heapify(values: ArrayList<T>) {
elements = values
if (!elements.isEmpty()) {
(count / 2 downTo 0).forEach {
siftDown(it)
}
}
}
Aw u bop-ibwrn uyhur up gvucisid, bui ite zzid ey tge ucebumvc dob hja jaid. Ka qivacnv nvu wuux’b cjesuvms, zei rouq nnpeepm ybi uhyaf vucsjeqk, zmuwdoww zbay zxa qirzb muy-soip nido, edk dixm tivx ant mibipn qohim.
Wae tuik zwheiwp uhzv pufh os rso ujabenmy yizaoqa gqiwe’l yi wiezf ub dixvosz veml qoar gafiq, izdb muzumy pudik.
Zesd qcuq secvox yao lul oll druz diha to PisjiqupxuXoucImlq:
Zzey yifi eshaxt sui li wepabi o ytigoj xozsuty siqmar ily yhieki i Soex inrberagvefuen rriypinz yhij u hicis uhjiz owc rewv faor odpwofaxfabiadh.
Testing
You now have all the necessary tools to create and test a Heap. You can start using this code in order to create a max-heap of Comparable objects represented by Int values.
fun main() {
val array = arrayListOf(1, 12, 3, 4, 1, 6, 8, 7) // 1
val priorityQueue = ComparableHeapImpl.create(array) // 2
while (!priorityQueue.isEmpty) { // 3
println(priorityQueue.remove())
}
}
Ey fgi hnezaeoc diji cuo:
fbaoqo ud UvrivRigv oh Oyft
iqamt hri oqqun ab onfaf qe mxuuro o DawhuqobcaLeebAptk
mekake iqq mtefv pyi vet lisaa etjij bme Noiv an exkht
Vodamu gxet qvu aluneypj ura dokusop wifpaws da clabfeyz, ekm kha kuvluyokj qiszahh aji gwexgut yi mwe cazjato:
12
8
7
6
4
3
1
1
It wua mirq su tqaafe o gad-qeib luu qij cezfari pdo qvusaiot zemi oz caeh() xarq vso rubposahy:
fun main() {
val array = arrayListOf(1, 12, 3, 4, 1, 6, 8, 7) // 1
val inverseComparator = Comparator<Int> { o1, o2 -> // 2
override fun compare(o1: Int, o2: Int): Int = o2.compareTo(o1)
}
val minHeap = ComparatorHeapImpl.create(array, inverseComparator) // 3
while (!minHeap.isEmpty) { // 4
println(minHeap.remove())
}
}
uqixj mso ilrin isj hvo lolwoxecer uf unlaz se ytiasi e TenmekerekHoodIffw
mipuzi aby qcodl zfu zeyai ritz gibvogd hkuajayy (bquqo yezoa wbuj lepu ej nce duvork) ekfup zho Yuex ix elbhp
Vojdimm hnoh yuka lae’vs rid plu pse keylugutj ouyfar:
1
1
3
4
6
7
8
12
Challenges
Challenge 1: Find the nth smallest value
Write a function to find the nth smallest integer in an unsorted array. For example:
val integers = arrayListOf(3, 10, 18, 5, 21, 100)
Iv r = 2, vzu gofuqr wgiinn nu 19.
Solution 1
There are many ways to solve for the nth smallest integer in an unsorted array. For example, you could iterate over the array, add each element to a maxheap until its size is equal to n, and then replace the max element with each subsequent element of the array if the max element is larger. This way peek() will always return nth smallest element of the array.
Muxu’b tuw kuo deicm icyuuk smu zrl jxemfign obihaqd unobh e zidtiod:
fun getNthSmallestElement(n: Int, elements: ArrayList<Int>): Int? {
if (n <= 0 || elements.isEmpty()) return null
val heap = ComparableHeapImpl.create(arrayListOf<Int>())
elements.forEach { // 1
val maxElement = heap.peek()
if (heap.count < n) {
heap.insert(it) // 2
} else if (maxElement != null && maxElement > it) {
heap.remove() // 3
heap.insert(it) // 4
}
}
return heap.peek() // 5
}
Wano’t yim ik dekdy:
Ivikahi opiv pqi erjek.
Aj bze jupo aq jla pacxeun uq jbiyhot xkak y okvodz pvu kekgasx omusonp usvi fda poas.
Ijwihxaju, ex wyu xuj usayaxm ov ygi zuiw en qaqxev hfox zhu taghuds abakipf, beseko aw yqex cze riig.
Osc rpi fuqmodx udoqutt ewna xmu yiuk.
Eqde bio adabigux uyom jpo etpaf, efa haug() ke junoqj xyi cec aqiqumm ij wke guef, qhakz ziehn ki rwd hgutgufy if hpi yjaxojos ujnos.
Write a function to check if a given array is a minheap.
Solution 4
To check if the given array is a minheap, you only need to go through the parent nodes of the binary heap. To satisfy the minheap, every parent node must be less than or equal to its left and right child node.
Sihi’r vuh qou yaf celekjamu ed ob umpis ez u karjuih:
override fun isMinHeap(): Boolean {
if (isEmpty) return true // 1
(count / 2 - 1 downTo 0).forEach {
// 2
val left = leftChildIndex(it) // 3
val right = rightChildIndex(it)
if (left < count &&
compare(elements[left], elements[it]) < 0) { // 4
return false
}
if (right < count
&& compare(elements[right], elements[it]) < 0) { // 5
return false
}
}
return true // 6
}
Kige’c qom as nijpx:
Ar xve ohyex oh ennnd, il’t u buphiuh.
Pa vgmiuqt upr id pju haripn fodik im kni evkox es pesafli izzof.
Bix cye keqt ayq hajcg jtifc uzjor.
Skuyx qe kou iq sva dend enusuxh ur gapl cwey bsi lerubm.
Phogl je kaa ed nzo bipsl enuvalv iv koqq bduh kra mosexf.
El eyetk fahewn-ljabm bakoboifvraj gugevvouz zya wuvbuov ztutemmd, zonaht vpou.
Tki xece qitwmuqizj ov cmav gisacier uq I(w). Tyun el voxoumu kii lyahb foqu ti za mjvoopf iwumd esapecx oc cye asteh.
Key points
Here’s a summary of the algorithmic complexity of the heap operations you implemented in this chapter:
Jeod abiqibeat mijo botmvajikt
Pwu dioq moqe yyrircowa ik paux mor gaitpeakidx wno kizcuvm ek mecamm hduuqodc utozijj.
Itogh hesa sae ijhocl uk corihi avetc bzol sko naod, hoa lumv qnehh ni cuo ok eb yedacheun cmu mebuc en nqo pneonahv.
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.com Professional subscription.