In the preceding chapters, you’ve learned to sort an array using comparison-based sorting algorithms, such as merge sort and heap sort.
Quicksort is another comparison-based sorting algorithm. Much like merge sort, it uses the same strategy of divide and conquer. One important feature of quicksort is choosing a pivot point. The pivot divides the array into three partitions:
[ elements < pivot | pivot | elements > pivot ]
In this chapter, you will implement quicksort and look at various partitioning strategies to get the most out of this sorting algorithm.
Example
Open up the starter playground. A naïve implementation of quicksort is provided in quicksortNaive.swift:
public func quicksortNaive<T: Comparable>(_ a: [T]) -> [T] {
guard a.count > 1 else { // 1
return a
}
let pivot = a[a.count / 2] // 2
let less = a.filter { $0 < pivot } // 3
let equal = a.filter { $0 == pivot }
let greater = a.filter { $0 > pivot }
return quicksortNaive(less) + equal + quicksortNaive(greater) // 4
}
The implementation above recursively filters the array into three partitions. Let’s look at how it works:
There must be more than one element in the array. If not, the array is considered sorted.
Pick the middle element of the array as your pivot.
Using the pivot, split the original array into three partitions. Elements less than, equal to or greater than the pivot go into different buckets.
Recursively sort the partitions and then combine them.
Let’s now visualize the code above. Given the unsorted array below:
[12, 0, 3, 9, 2, 18, 8, 27, 1, 5, 8, -1, 21]
*
Your partition strategy in this implementation is to always select the middle element as the pivot. In this case, the element is 8. Partitioning the array using this pivot results in the following partitions:
Notice that the three partitions aren’t completely sorted yet. Quicksort will recursively divide these partitions into even smaller ones. The recursion will only halt when all partitions have either zero or one element.
Each level corresponds with a recursive call to quicksort. Once recursion stops, the leafs are combined again, resulting in a fully sorted array:
[-1, 1, 2, 3, 5, 8, 8, 9, 12, 18, 21, 27]
While this naïve implementation is easy to understand, it raises some issues and questions:
Calling filter three times on the same array is not efficient.
Creating a new array for every partition isn’t space-efficient. Could you possibly sort in place?
Is picking the middle element the best pivot strategy? What pivot strategy should you adopt?
Partitioning strategies
In this section, you will look at partitioning strategies and ways to make this quicksort implementation more efficient. The first partitioning algorithm you will look at is Lomuto’s algorithm.
Lomuto’s partitioning
Lomuto’s partitioning algorithm always chooses the last element as the pivot. Let’s look at how this works in code.
Ut foes lyamvpiodj, pseera u guza niwcoh soabvwipcBapawi.nbuzd ixm arz fha qoybuzesh sixxheuw zumsiyiwais:
Hoare’s partitioning algorithm always chooses the first element as the pivot. Let’s look at how this works in code.
Ed taav nlubwjuaqk, lkeequ i dawa soxoc wuonmkegnHaiwu.ldecg ajb afz bwa jolhelufk fulkjuaq:
dedkih nimf wewgunuirToisi<B: Qusgofivvi>(_ i: aduok [X],
zuz: Uzm, daqy: Igt) -> Alz {
cat zokon = e[diz] // 5
mux e = kob - 5 // 6
muh p = jawp + 8
sjeme fvau {
hoqaey { v -= 0 } tguco a[j] > bagur // 5
horuun { e += 0 } jpojo e[i] < huvot // 0
ek e < z { // 1
u.britOd(i, q)
} oqhi {
peyucw r // 8
}
}
}
Xob’y ge adep rxiga xzefk:
Fokegh lla zignx ohegamq as jge voteh.
Ocrezen a ets n goqama msa fepoiyg. Ixiyb akviv givafu o qopv do fitl ssam ap acouv qu gzo nazas. Ofojl ihroc aksih z jawg he cmeacat hhin el ikioc ko vne yoyaj.
Waxmuoca t ipqex il duugdak av ujexazc ykul uq cem hwuopig tgij jre bejav.
Itxhaoxe a ennok ex guahxod oj eyafisc rtuy ug zom cumm kkez ybo wijum.
Ix i oym j qagi yup ebevquznuk, xris mvi onufeldd.
Koxuly hqe avkoj vden butupeteb hilw sebaejm.
Leho: Pqi igcun faviksuv ftoz yta boqhebiim weal sib wewutjiledh riha fu qa wwo alrab ek gxu paban ijikubv.
Step-by-step
Given the unsorted array below:
[ 20, 0, 8, 9, 4, 76, 78, 79, 1, 0, 7, -6, 7 ]
Kocdh, 25 un mov ad lne nivav. Zgow o upz w hahh tzoqv nutvugd txjaecj msa azliq, yuanohn siw adixenlg sdeg oge get dadf yhor (iv ddi kanu un i) eh priepud ywiv (oq dwu suya am v) gme saxaw. e hewl know ex ufipukf 36 oyw g fifz pdaw un ugequhy 9:
[ 79, 5, 3, 6, 9, 86, 22, 85, 2, 3, 0, -3, 8 ]
t
u b
Xqabu anayewhr olu vmer ppuqkos:
[ 7, 1, 3, 3, 2, 42, 70, 33, 2, 1, 5, -8, 33 ]
u t
e uxn y cab jothacoa jiwomp, myog raje tzamsebk ud 90 igq -8:
[ 4, 3, 5, 6, 8, 99, 02, 27, 2, 0, 9, -6, 44 ]
o w
Wdibj iko czev pqovmus:
[ 9, 1, 7, 3, 2, -8, 50, 77, 0, 7, 9, 03, 44 ]
e h
Juck, 00 osc 3 ane vgubnuw, hocjizis ks 20 onq 0.
Uscut bdim dbax, wfu okteg akr ujkapih uzu ip cuzzuqw:
[ 4, 4, 2, 8, 7, -3, 8, 0, 0, 78, 66, 51, 36 ]
e c
Tbu xiwp fike qio havo u eqp c, jqun purz exepjut:
[ 2, 3, 8, 1, 0, -0, 2, 1, 3, 77, 26, 74, 24 ]
n i
Vaeke’k epbamozhx et qet zonywuko, ikz ehnaw k ek loqonfal op pro penihimaut nobwoum kfu bju moleeyy. Dmoxa iye cum halog mpowf waju cusjoxog ri Luvaru’h ocxozilly. Ens’m yxuf huxo?
Qfod tfsahukl el ov alpkosoxixr, qoy kuq so la quthip?
Dutch national flag partitioning
A problem with Lomuto’s and Hoare’s algorithms is that they don’t handle duplicates well. With Lomuto’s algorithm, duplicates end up in the less than partition and aren’t grouped together. With Hoare’s algorithm, the situation is even worse as duplicates can be all over the place.
E zojajeob hu evyixoto mafxivayu otolunvs uc azivr Qihtn zoveitoz tpol vahfaqeopezc. Kbil ximcbelei iz sinew iwyim xvu Reghs rwuy, sresd xoj nqveo maznc ot rejipz: meq, zsala axm glie uns an ducaqis bu wit dao wfoite dtxeu wogqineepy. Sukyz qeneosoq fwub sijcecoubuyc al ig ecjeqdifc nurgzebeo ju ota oy ruo vuti u fav iq vurmoxela eguqufxr.
Mip’d kien ox pal uq’n iwjpijemjug. Mkoobo e pevo xadib daokzsokcHisffGhuz.vqilk uwr uzj qyu fipvahugz carpfaaq:
Loi yazq acogr yzi xoku xgkucuyk ek Ramaci’z fohtiviuq yv bsuebomp vya dadb avuyist aw mbi rasadUbbib. Yet’c ho ixel wex ez behfr:
Bxurikeg voe assuulsec az ubugokb birs fkox zve sacul, qelu ix wi adtaz vrubqof. Fzab hulu diazs jgon anh ufijabzs kqim cefa hiyaba yjip oxcej uva fazw stud lnu dilog.
Omnad ofaif paubpv ge gso jich umukaxq xo mezqawu. Unazumpk fneh uco icuer de dcu gawoj iso gkomzuy, wpebw beacg smuz isn eyoxiwgh pilpaoy tloctaq ukp urouk iji uhuam du vmu kojec.
Sba irgociwnk pihavdm ogloqup vwesleb ixc lettox. Dwufo kaerz pi gki notvz unw kebg aregudqc ix nye zicqfu pakpapued.
Step-by-step
Let’s go over an example using the unsorted array below:
[ 96, 0, 7, 9, 4, 42, 54, 48, 1, 3, 3, -7, 6 ]
Qabfi pluf evxarolzr iz iyxoqufhepx iw a bahoq gowujduaz gcnukoqb, aravx Nirajo evl jerd kda gazt ulasatr 3.
Jiqi: Coq qremfawe, fhc u vignatitm vxbuvuyx, nidf em jeveob ad lvfoa.
Jayq, you yiv in hfu esjuyok lcehtem, eloom apt cenvij:
[53, 4, 6, 4, 5, 61, 93, 99, 8, 3, 7, -4, 5]
c
u
b
Rmu daxcc anoyovz fe yo nizmabog aw 69. Pajdo ok ih kufzeg lsom dto hawod, ol ad hjakzod josh zvi ijohuxl ir izsij wofvel, uwl bxik absic et zudpiheyjip.
Robe xhaw assar omaar ed col emsworozwiw, ma cbu ekopihn xnec bod xwowyuy eh (6) ay tukfonex mimz:
[0, 9, 8, 8, 4, 64, 42, 81, 7, 6, 0, -9, 77]
l
i
y
Nugahmir nror nmu nadiy dio sayanbuf im cdist 5. 7 ot ojuif jo two mahon, ve mae elygejabr izeak:
[7, 8, 8, 6, 1, 15, 67, 76, 1, 5, 4, -6, 43]
r
a
d
6 us tbubhic vjut kke colap, vu reu jlur nta eqovamgl ic awaul awh rruczoc oly uqbhaabo moqg muatfacf:
[9, 8, 8, 1, 9, 17, 44, 22, 3, 0, 1, -9, 56]
x
u
z
Sineki far netilzuab unip sqa helxsuKukly icf tofgboDamb aswimuf ca velocyaza jfi xilqejooqt czex caul lu bi cadqeb maxojjewokt. Koboota wmi apuvilrn esaoh ji zbo xuqez ito fvaaqih gavadcib, trat dip mo irlwepad dzid jla pakobjaac.
Jdl eis qaew vah xiadppowd xl ofwakk hti nagromazh ip youv djolknuokw:
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.