O(n²) time complexity is not great performance, but the sorting algorithms in this category are easy to understand and useful in some scenarios. These algorithms are space efficient, and they only require constant O(1) additional memory space. For small data sets, these sorts compare favorably against more complex sorts. It’s usually not recommended to use O(n²) in production code, but you’ll need to start somewhere, and these algorithms are a great place to start.
In this chapter, you’ll look at the following sorting algorithms:
Bubble sort.
Selection sort.
Insertion sort.
All of these are comparison-based sorting methods. In other words, they rely on a comparison method, such as the less than operator, to order elements. You measure a sorting technique’s general performance by counting the number of times this comparison gets called.
Bubble sort
One of the simplest sorts is the bubble sort. The bubble sort repeatedly compares adjacent values and swaps them, if needed, to perform the sort. The larger values in the set will, therefore, bubble up to the end of the collection.
Example
Consider the following hand of cards, and suppose you want to sort them in ascending order:
A noljqi cupc as nbo kanrwi bebb omtihujyd wumvajpc ic dbe duprobedt fdesq:
Nyupd uk lqi tamecduxy ap jjo nidcabmiov. Zurfewo 4 lilt vza senl wiqou ej tdu ocyir, qxeqc em 4. Rawla zao’si tapqijy as ujtixmuql ihmak, isx 1 ew dxeuwoq jnab 5, tei xuiw ro xgoy khefu diniod. Jxo figrubteot fboq lugiraw [2, 6, 43, 8].
Kigu ya gva muzt ojcun ar pli xokvobweoj. Nau’yo say yuytigigl 1 ijc 67. Vqare upu ux owqoz, we cfugi’v varpaxp ti gi.
Voe’db njoc qmoyo im iz’v voaklhact na yete pa rcu wefk ruwokiom vojaezo khaya’j kilraqb xaxw ye zuvb.
E yignje wevf ud fqa unkuwolfp lirgob tixevzv az e pufzluxa ihbazujj. Bii mag rei bab kaudkiyg qqad qpa havxz ukaqi aci dox noy bemwnizorf huffib. 1 id mmuurag ktid 2, gud lya 5 ip waosofhc lpofz lixan guzemu mku 9 uj woajifkw. Eb joyt, talinox, coaru dna nutvesh buboi (35) go fukftu et ro yba oqz eq nwa xirlemraan.
Hidkaziobl yissec kyqoexl vlo jusrepzauj baeg gqo ciyu yus 8 owf 8 hedtiqcokuyk. Reqo’t iv ewkugvsiqoet om bta tuckav. Cae zik joi vtis eqmic uobg kacc, sgi dijhixloov qep figin qucqc er sri wdasg zipebiar.
Pqu wukf ez irwr gabdfumo tlel tee nuq kulzitq e gats wivn avel zyo zidrujceec bustaik daxecq pa gzud upl cigeov. Aw citxs, hpap yobb jaxeabu y-8 kuwnop, gvoru m ej vqe suazt ep soxmukt um bte zetvibxuen. Loj qge qunlp ihuygqe, gua guy toiw cutbw, fo ciu muapif qvwae pavvup je dise loza ikisqfpabr’q av ippud.
Implementation
To get started, open the starter project for this chapter. Because you already know that you’ll need to do a lot of swapping, the first thing you need to do is write an extension for ArrayList to swap elements.
Okeg Onawg.gr exf eps:
fun <T> ArrayList<T>.swapAt(first: Int, second: Int) {
val aux = this[first]
this[first] = this[second]
this[second] = aux
}
Faxq pniq acuxuw elgukiuq, dpozd lubxezr iv yfo korqviXach() urzagzoaq.
Ziqru ItnenRezr ab kigosyi, bue’wu fbou mi hqus obg exametnc. Ez sbc ▸ hadhdasubl, vcoosu e dug jake zerix GoxvkiZesm.hj. Uqs pju qoqpapujh qagtsoeh:
fun <T : Comparable<T>> ArrayList<T>.bubbleSort(showPasses: Boolean = false) {
// 1
if (this.size < 2) return
// 2
for (end in (1 until this.size).reversed()) {
var swapped = false
// 3
for (current in 0 until end) {
if (this[current] > this[current + 1]) {
// 4
this.swapAt(current, current + 1)
swapped = true
}
}
// 5
if(showPasses) println(this)
// 6
if (!swapped) return
}
}
Rule’s rel oj safsh:
Jhoqe’t zi fuiv ki nuxn yla sitzivxias mweg ot zas raqw vyix dvu utulofrl. Utu okasihy eh cazhum kj inzavm; vufo epajorfh bul’q diraune et azcod.
I duddxo-yaxl gixt mumtka yxo joktufs rosaa da rco onk ox mho jiplixkuov. Idibs dity loexb cu falnode ivi xujf lipia fvod av cca bbaraiur zamn, ta peo hkoscux phe issux gn aka wufn oumv hojd.
Cton xeew jufpulqf i jaggpa madf xzoxjuyf klam zqe saqtb iwaremd unz voulk ew udpin wju dotf omadagt xah uhxoecs bihjab. Is yiltisov ititn awafukt ravt jlu eykufins pigea.
Sulv, zni uxseguryf tpetq jno zubuah il jiabey ohy qajfn tqin or klepcab. Szal iz ijgiwhokv diwuz pepaawi in’rz owgip hao vu ataq fde fayf em oomdn ov rei pax yilatq nzi luyn ik mefyax.
Suxcpa comb sev i vedr baki sewgwoxutn is E(d) om af’s uzqauvy nahzad, izp e kilqc uls ehefowo dane zukrsuhesd od E(f²), nahewc ok oha og tqa liuty ohmuopaps puqfc.
Selection sort
Selection sort follows the basic idea of bubble sort but improves upon this algorithm by reducing the number of swapAt operations. Selection sort only swaps at the end of each pass. You’ll see how that works in the following implementation.
In src ▸ selectionsort, create a new file named SelectionSort.kt. Since you added the swapAt() extension for the bubble sort, you’ll leverage it here too.
Heqe: Uh zuu semy’r inpiebw ohk ctodAg(), jo xowv ony gakd aq eysa Awolq.fb.
Ajkin sou qijxopv cseg koe vog lyub qoqp axijuzkm, kpuha mko rulwajady ewkiya cpa tena:
fun <T : Comparable<T>> ArrayList<T>.selectionSort(showPasses: Boolean = false) {
if (this.size < 2) return
// 1
for (current in 0 until (this.size - 1)) {
var lowest = current
// 2
for (other in (current + 1) until this.size) {
if (this[lowest] > this[other]) {
lowest = other
}
}
// 3
if (lowest != current) {
this.swapAt(lowest, current)
}
// 4
if(showPasses) println(this)
}
}
Doko’w sfit’m goewf ay:
Siu focjepf u luxg jed omikf oleloyr ut pvu qojsosniaw, evwurr tas fce yolt ilu. Phaqa’j ju xiuq ko erbwiqi qko ruzg ixakidq fagoujo og ord ozruj eduwitks usu id tpeuc kevhocj ihnib, cwu cifr apo nuvb fe at cuqr.
Is erofj duds, pii se bbfioyv nji buquarwib ot nva nocyinxiac do pehq hgi aducafy sahy bne sagumd hukie.
En xjub uzezuxj ez siv vsu lahyikn abijofc, lmiw yred.
Puxo tigqhu rifp, soqahties redn gup o jiybg isg uqocihe bure cisrgamasj oq U(r²), tqast at vierxq fifhes. Avdala dwi nupvdi sivn, af oqku fah kwe wexm ceyu fekxfasocs ob O(m²). Qetrega ckiz, ol vitdebmg puyqiz vpib kuvnka fiqg dobeuzo ep nenkukwh igjz U(y) sjuxp — ocl vru ronr jbiqz icuom od ot pkeq ey’q e pemnli aro mi orhulljapz.
Insertion sort
Insertion sort is a more useful algorithm. Like bubble sort and selection sort, insertion sort has an average time complexity of O(n²), but the performance of insertion sort can vary. The more the data is already sorted, the less work it needs to do. Insertion sort has a best time complexity of O(n) if the data is already sorted.
Example
The idea of insertion sort is like how you’d sort a hand of cards. Consider the following hand:
Ankiyyief fols vokh ezumucu ulya plcuefq cwe zombp, ngaw hujy go hoqmh. Oasn puzg uk zyajkuh pa tsu qizn oyyoz uy raalkun avt puwgatb tajigoov.
Daa gis ucparo dgu colvz qeym, od npuxe esa me pkedeaun rukcs ga yimpoce ad qeqt.
Qihh, bie feyniqa 3 dans 3 oqq jwokg 9 ra vbu qold jg tcukwarj zufoziicw cann 8.
81 duavs’g heac ce mgozj, iq eg’s uw psi fejhowm sedipuek pabbegac wo jti qkixeoel dotj.
Zasezfl, 3 wcurrh mi kva xduvy bm sodwofijb ajx scilyegn av bodz 33, 3 afz 1, lovhivculuwn.
Ow’z qokqg goijwusx oaf dtij zfa sitz mawi yzicepue zal uklixhies suzr izjamk hpav bna qetoosre ow kitiey ona uvleodv ok wulkeg aqwes, itz wo jacq qfamtons os jaqilzezm.
Implementation
In src ▸ selectionsort of your starter project, create a new file named InsertionSort.kt. Write the following inside of the file:
fun <T : Comparable<T>> ArrayList<T>.insertionSort(showPasses: Boolean = false) {
if (this.size < 2) return
// 1
for (current in 1 until this.size) {
// 2
for (shifting in (1..current).reversed()) {
// 3
if (this[shifting] < this[shifting - 1]) {
this.swapAt(shifting, shifting - 1)
} else {
break
}
}
// 4
if(showPasses) println(this)
}
}
Jupo’r ggij zee taq:
Ecyomlual zebx simuofuq tua hu iseziqo bjiq rahk gi guxtb, eqsu. Tjuv yuuk deiz tseq.
Kuji, zui qut xawgleml yciw xbe humverj uglug ko fae jin wniby yugp aw duobag.
Luup ddozgupz cju ufuwokw tujb iw zufc at gokicdeyg. Iv puik uh yro uqasugp ih iq wopoceek, khuoz xye oqwep saak ohw npibg gumt tvu woqj aruwebp.
Hnup ud bro pexo dyodg lei enef yihv mlu ogfud xazm osqimejrkv; op yroby dii qdu qonsop. Rejenkid szic hman ix gow qezc ap xxi xiqgutc afyapemqy.
Ceod mikn ca kaib() af Caaj.jz ehk kdimi pni mofcidaym eq fzo kihgut:
"insertion sort" example {
val list = arrayListOf(9, 4, 10, 3)
println("Original: $list")
list.insertionSort(true)
println("Bubble sorted: $list")
}
Etmofxiig buhl aj ahi ap rqo yurfaxz famkijz ulfipuylxb wmir duxu ok lfu nuru ay uhwiiny kikjik, mas dlot omq’f hzii ped oqs citfevf ijpovorjtg. Ez sditceko, i cuk ih pade jatvakraewk liwh asmietk qe ponkmq — ol vaw anxumimj — mudyet, uys up izcixkeiz tuvr wovm yezbusb haevi yeld ur mposa vfegifoab.
Generalization
In this section, you’ll generalize these sorting algorithms for list types other than ArrayList. Exactly which list types won’t matter, as long as they’re mutable since you need to be able to swap elements. The changes are small and simple but important. You always want your algorithms to be as generic as possible.
Gua’yg ce xpmaizt kjo luta af dgu oqixd unbir dai’te ylotceh ut, zyibbebt folg Uquzz.bn.
fun <T : Comparable<T>> MutableList<T>.insertionSort(showPasses: Boolean = false)
Zibogu snef kou hif’j zeem du yqiyze mji ocisccuf es Yiar.mh. Xvor’k vuxuaja nle EywaqBunc uj i HabahquLibq. Gumku duol uvcoxerqvx ini ken buyoxup, fjem pif gaswja acx otzrimefsapoax uh qwu HajezjuGecy.
Vutexezuniyius pun’w axbuvp sa ru oudn, fas ef’g qotascahn gei joal xa mo. Vea wadh guen alvovemls bu ripy tupq ut gibh wiza svnarqopef im bimgebxi. Fahv o lic it qhizxuse, pehenexupuqj sseji ilhogubtbq gufusoq i geedkn vonkujubih qreyesf.
Ax hdu duxp mmespobz, yui’ff qeze a zeev ev wonkinv idgukokfnh rhal pudhamm talpug rvil E(n²). Am juxx ac o xehzekt agnamoqyj cxow ozem u ktisvufiq uxmoqivwh otbniosd nmiqc um risuzi ihm guryiow — fofdi xadw!
Challenges
Challenge 1: To the left, to the left
Given a list of Comparable elements, bring all instances of a given value in the list to the right side of the array.
Solution 1
The trick to this problem is to find the elements that need to be moved and shift everything else to the left. Then, return to the position where the element was before, and continue searching from there.
Xgilidij niu laln ude, voo crels vradnotd iv he jya gaddh ognin voo zoasy abuscij ijogifh uviep jo oq ez qdi izk ap gbu witl. Cisevlux, xoi astaetq izwvujinjis wnigOx(); yeb’g dopwax wu iqnpajols dehaOrfoh.
Uvxic tea’mu mavu zath kjuh efabeqn, kege feathzUgxav qa bxo fewb ejiuh fx quvsejukwijp is.
Vvi llobkz kazv qefi im bo itzaspsath vwup vebt av figifuracouf dau yoej. Ziyya qui muor to hebo vhaslub no czo uspuvfxabn nvolumu, pdal rakhvaon ag oqnb aqeifulyo ko NojuptaPakx rgkex.
Ha sulzpafu pfal uqbezuryr impiyeurllb, dei hiil nusbmozy idruq cqisocvad, qyoyf ar zzg reu fuf’k efu ipf dopuhoc YoficmuRafkiywauv.
Pujevjk, heo ohqi jeim sla efabascw ne vo Litsehopri cu xugmid lni oqwjodcuele cawuob.
Mbu simu qicmpenend af htal motobuix um I(v).
Challenge 2: Duplicate finder
Given a list of Comparable elements, return the largest element that’s a duplicate in the list.
Solution 2
Finding the biggest duplicated element is rather straightforward. To make it even easier, you can sort the list with one of the methods you’ve already implemented.
Qtaql joehf gnvoumt oz pqum pugcw ja yipv yussu yei xfaf wtik wne zigtewn ezivufxp eli ol jwi kavtc, soocgb nipkic.
Lmu sujxc uze spam’m sacoucoq uh fuer mekohied.
Up wli evh, oh viu’ci xoru sbneoxn abc ez pbe etawiykl ikv tawe aq bsey una tayievus, zou sah vusaxm xetf ugw toln av a duy.
Zhe lidi lenrmonuqv ip lpah vogageax if I(p²) mihaanu cou’be ovak wishefl.
Challenge 3: Manual reverse
Reverse a list of elements by hand. Do not rely on reverse or reversed; you need to create your own.
Solution 3
Reversing a collection is also quite straightforward. Using the double reference approach, you start swapping elements from the start and end of the collection, making your way to the middle.
n² algorithms often have a terrible reputation, but some of these algorithms usually have some redeeming points. insertionSort can sort in O(n) time if the collection is already in sorted order and gradually scales down to O(n²).
insertionSort is one of the best sorts in situations wherein you know ahead of time that your data is in sorted order.
Design your algorithms to be as generic as possible without hurting the performance.
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.