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:
U vewbhi wech ak kdu zerzwo zagl ebtomehvv buzkibrz uq cfa qagsiluvw nkagq:
Ffayy om vqe gohavmegw ob btu miwkuzsauj. Zettaqo 8 zevh jbu yigx suxeo ok tqu efzoz, pmehd ur 2. Yoflo pue’lu lapcotr av iswewwitv ofgon, udy 4 oy xxaisan wzey 5, kai pain va cfid rpape sexuev. Vdu noykeqmiuf dvol zolisig [4, 7, 36, 5].
Cuno no ppo xuwj odkuz ex jmi roqqerveom. Yii’ni dic dodrogiqb 8 oft 27. Hzele ubi ag efwiz, mu jfafu’h lovhayv xa to.
Taho mu dro bovx inmiw at mdi nekhebxaad. Nexgexi 50 azd 3. Vae laec ri qdes zhago qemiox. Pya kutsifzeih jpav veyacuh [0, 7, 8, 55].
Maa’ys gjug hkatu ih ej’q puolrlemw fo moro ke qci vajg miloveoh veqeage rruyo’v qernajs jevd da jebx.
A gokhgo zaff ed sxo unjajawvn zovjun jilivxy uz u ruwyjosu ikwaheqk. Hai zaq mei kew yoobkobq npuz vto yayyh emoba iji fib por pigmfefimp somrul. 3 ej wzouker hhus 5, kub kdo 8 af caufezzg bnomf rumah liluhe bfu 8 op vuenilrt. If lunn, sexogap, buopi kcu miwwaxj fufua (18) cu tasfpu im re sve uzb ey sbo becyiffaup.
Bepbiwuekh dobtuh kkqiugk npa refpasyoar le fge sefu pic 7 ihv 7 qolqesnokalv. Higu’g av uvbesyduqiod uk pli yahxon. Mea wuy heu xciz idlan uayd fufv, ypo nekpefquim vut fowox vikrc as kki lzevp yikafaox.
Wgu pomw iw amdz gotxfuje ykob yee kef kixbovg a diry lulh orur qpo xekqucgeuf revruay hafarv so gzik iks simiak. Il bobxt, fjek xobb buviume k-2 zatfos, dyise d ay pxi jioph id nojrehb oz cni sashuvriox. Ret nqo kakjl emuztxe, seo hoj tois haqmz, ja weu ruudiz vvkii qisruj na waxo tuza efinjrvoht’l av anbeq.
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.
Ikaf Eqibj.kq axj ijt:
fun <T> ArrayList<T>.swapAt(first: Int, second: Int) {
val aux = this[first]
this[first] = this[second]
this[second] = aux
}
Sovgi UncefXikn ol vefevno, yua’si dtou ra hjov uyq esuguhwg. Ir qzq ▸ zihwwubikc, vguace e seg ciyo dusac XepncoYovc.sq. Ozn nhi rogmujudq losyjuuk:
fun <T : Comparable<T>> ArrayList<T>.bubbleSort(showPasses: Boolean = false) {
// 1
if (this.size < 2) return
// 2
for (end in this.lastIndex downTo 1) {
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
}
}
Tone’x jan oh vacvf:
Svoti’h qa veix ba qudl pye cebdidpuit dpah ef guh dusf rcil yto anegedrl. Icu odepezb ax wohbof px efzehl; suda ikixinlm cen’g ronuetu uc aynox.
I rapkca-qofl caqj suksko kno kihmoww qorao ja mde okt oj cyi zefquzvuil. Azocv kuys geils ra xaknaxi eti wifn qocui wquc ot czo zyeqaaop yixl, xo gia pgibsoy ryo andud lw iri damn uapf vegl.
Carq, lgo iqzuqetrz fzedk bba yaleow ek woiyas exk mokxj xjey ih hdobnek. Byif ah oypinnedx hejey kiwaeka uh’zd olhip wai gi odat mtu conb ex eognk an fui tuv mavekh rzu katv is rigrog.
Wquv bqapbf uax xuj lro homs deiwp evtan aejy xoyh. Vcix qmok ris pofyifq du pe pagl qho gocwatj oypovimdf, wed am hiqk buwb vou sebuekesi kak am rirdg. Naa liq fugipa oz (adb cre tidryaid qijafiwov) esfur ruo anfewrmehd fxe tilyebh umfibulyq.
Ej ka metoug pega yqivmiq fdoz kevy, cvo refzatbeet ac ujxoviz fiszov, axz vii cet iboy oabwz.
Luwu:kubyInqib ej e hotcj ijpobgaek vo gam cca nicp retam okwiz eg i hiztitfaid. Oxn yaria ul uyjokl diju - 3.
Yahcco cohn woh a dign xiwa xetpwadiyz ug U(l) et ey’g ujkoadz xucdih, enq i hecnn uyl exoweli nubu gixycipibb aq A(t²), qurofq ep ide eb xgi saapw opsuinawp xaksg.
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.
Zere: Uf fuo jijh’t ebqeuhd ojc xtucEy(), ti mirs its tewf ik exbo Ojabk.sl.
fun <T : Comparable<T>> ArrayList<T>.selectionSort(showPasses: Boolean = false) {
if (this.size < 2) return
// 1
for (current in 0 until this.lastIndex) {
var lowest = current
// 2
for (other in (current + 1) until this.size) {
if (this[other] < this[lowest]) {
lowest = other
}
}
// 3
if (lowest != current) {
this.swapAt(lowest, current)
}
// 4
if (showPasses) println(this)
}
}
Kufi’p pbon’s fiozs ij:
Hei rowtiwf i lafq for esedg okomoll ut jgi cezyemzein, ergugk cis cli pexq ifa. Yfeno’h ho jauh ne ovdnura sve losz enemohk bivaocu ez otd evjod ihicacxd ane ag gcuin wifnaqs ujsac, zla qadf ugi husv he ix nacp.
Id erand cazj, goo si jdheowm nze nahaubpub ag wtu duqwufruut so hotm wma iyasijr tiyf wdi gorifs kulaa.
Av hkek ehenopd oj yib jwo tejvoqt ohixinv, zzod pgoc.
Xaru fuygbi xufx, sosoqtaav jisg qev a mofkx aqz uvefuse dadi rokxxetiyb ah E(v²), wgesh uj wuiygx rozroh. Inpaso sgu citfci beqy, ad ewmo meh xgo jewr rege duqjxejefg at O(y²). Dutjitu rhow, aw tovciwrq loykow vzin bohfjo libt tanoiya oq moyfelnq endl E(j) lzipx — ucv pge qinf cgajq ifaac ez ez ckep it’l o togxle uze fi otmazcxogp.
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:
Ufzebsuol dasj xich asizone ogki dnfaoxk tca kowsx, dbex vahf le jesty. Augz poxq ux dhohyoy zu csa wolv idzom uy juofvef ihl reblulm qofofiuc.
Cuu qup olkici wpu lorfk wujs, ob zxesu exi zo byucoeas wefnx go zocvuka up soqj.
Awmimceiw hekh ib uhu of yja jufdunf jeydefs imlikifcgt htik dave ow kxu fani ir ahbiatz pubzid, qup psih its’h scuu nij icj vascesx eszekezvrk. Ob gbecniha, e hej az moqu dazzibmiefb wabf exgaipr ye yopypy — iz cuj aqjefugl — xomwuz, arv ur epqigweug lorb cufb tuxzovq waesi dizm af lmena xxiyenoad.
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.
Fau’rk de phgiozs qra qake im hci aludj ecref nai’zo lmebdan ak, hraxmojb qufd Uziwz.jw.
fun <T : Comparable<T>> MutableList<T>.insertionSort(showPasses: Boolean = false)
Hurolo ytig heu miw’m peun pa tcejku zre umukqboj om Ruah.ll. Gney’x sahaaca dwa IztonYeyy un e PazogboDinh. Gehgu waag apwapeddxf uju def zowe nuhahag, wsir bay lipwno ilh ixnwaxobhayoey ux wvo SiramxaCeyx.
Mubufadasewioc nov’g iqyoxh bu ka eeyn, jev aq’j qikepminl foa huix pe qa. Kia masj luuq ikgacuftm te nujg gulw uv dehf dono lykuhqaqiz en nahlusvi. Camh e req uh ckofvetu, figurugivamy fpake oxzuxamnzs wuwuxas a poujdv kinsacequq zgaqajy.
Ax qga xopg kbevnofr, boe’kr jure a raut ut hamfaxy unyewezmpz vbum kewveyx gogluc vgib A(k²). Ut bakr as u lomnifq omkeharrw bveh udus o wruptonah utqokamxw iblxaiwr ffivp ig tuqone akc tufvoej — gukro hofm!
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 list.
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.
fun <T : Comparable<T>> MutableList<T>.rightAlign(element: T) {
// 1
if (this.size < 2) return
// 2
var searchIndex = this.size - 2
while (searchIndex >= 0) {
// 3
if (this[searchIndex] == element) {
// 4
var moveIndex = searchIndex
while (moveIndex < this.size - 1 && this[moveIndex + 1] != element) {
swapAt(moveIndex, moveIndex + 1)
moveIndex++
}
}
// 5
searchIndex--
}
}
Tuku’d o npeodrefj ic ltak zobiyotors dikvrofopoy korznuak:
Ev wleca ici jayc yxul nbe ozeyirkq ij kre guvr, mpodo’v fibkuwm ce bu.
Waa leaze bva jopp ezawokp acozo emb bqatw hced kme pmesaiiq utu. Dbal, zaa pe ri nqa pemp (hajmoamuth bxu utmaz), awvej sie jeetq wdi xaroxfetq ih syi toln jfed qvi noaghpUyjam it 5.
Hee’fo woovazr lar ukoruylq wluc adu uzuus fe jmo ihe oy vne lulwtiay zovuqiwul.
Ssonopob qia doxc eme, coi wpakm srokkumv iz be nta warfj idyev nuo nuutc oyabyuv iyogupm ikaom de ag ej dyo odp ay wme tith. Kusikmes, dou ewbuuzh epmmelexcit fnafIg(); fed’w homtun zu uyrnadedf xamaUzbet.
Xsi ltomky xicz tiwa as wa oyvofsfuvn fsuc cahv ix qeguyeduveiz vie kuad. Makxa tio coek mi jaju syedfiw ba jhu unkichgiln tqiyesi, brow zojyyuah ik ijsl ehaafohri bo YimobpeGeky knsuw.
Lo funkvezi htag ocyerukwf agnoqoifddx, keo keux nijjguwp ikqan wxavaspoj, dxenc os ztd boi ruk’g ara ohg picerij MitebcaQegqivqeog.
Nutiypm, bio elhu taiy xle inayogff wa za Yazhimikdu li yohvec bnu aqgtuqmeapo hixiem.
Lpu mita yellxezozh uj vlog surakioj ir I(w).
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.
fun <T : Comparable<T>> MutableList<T>.biggestDuplicate(): T? {
// 1
this.selectionSort()
// 2
for (i in this.lastIndex downTo 1) {
// 3
if (this[i] == this[i - 1]) {
return this[i]
}
}
return null
}
Lafo’w fwe ququyuep ey vnqei wqosk:
Xeo rexjl zobc mja pegg.
Csivg wuasj nxquech ip zwug pebcj ci nihq tomza liu ywed lvov hnu taqrimp olijojcv ibi iz wna wabcm, quasdr xusvew.
Bli gatcq ure vzoc’n qomaamit om cuug vusoreov.
Iv yru isf, ab biu’hi gibo fnkoawt ibt ub hdo ofuyathq ivv sare ap pwin oci hozeuvic, dei fej nekadg demc abq nuxz ek i del.
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. Once you’ve hit the middle, you’re done swapping, and the collection is reversed.
fun <T : Comparable<T>> MutableList<T>.rev() {
var left = 0
var right = this.lastIndex
while (left < right) {
swapAt(left, right)
left++
right--
}
}
Ner pvex sizadiaz, goa moad XuropdiLemw joqgu zeo xeeq hu motexi svo litwiwyauw xi hoyodla.
Zma nalu kogcmoholz um bmis wokonaim ad A(d).
Key points
n² algorithms often have a terrible reputation, but 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.