\(\) \(%\newcommand{\CLRS}{\href{https://mitpress.mit.edu/books/introduction-algorithms}{Cormen et al}}\) \(\newcommand{\CLRS}{\href{https://mitpress.mit.edu/books/introduction-algorithms}{Introduction to Algorithms, 3rd ed.} (\href{http://libproxy.aalto.fi/login?url=http://site.ebrary.com/lib/aalto/Doc?id=10397652}{Aalto access})}\) \(%\newcommand{\CLRS}{\href{https://mitpress.mit.edu/books/introduction-algorithms}{Introduction to Algorithms, 3rd ed.} (\href{http://libproxy.aalto.fi/login?url=http://site.ebrary.com/lib/aalto/Doc?id=10397652}{online via Aalto lib})}\) \(\newcommand{\SW}{\href{http://algs4.cs.princeton.edu/home/}{Algorithms, 4th ed.}}\) \(%\newcommand{\SW}{\href{http://algs4.cs.princeton.edu/home/}{Sedgewick and Wayne}}\) \(\) \(\newcommand{\Java}{\href{http://java.com/en/}{Java}}\) \(\newcommand{\Python}{\href{https://www.python.org/}{Python}}\) \(\newcommand{\CPP}{\href{http://www.cplusplus.com/}{C++}}\) \(\newcommand{\ST}[1]{{\Blue{\textsf{#1}}}}\) \(\newcommand{\PseudoCode}[1]{{\color{blue}\textsf{#1}}}\) \(\) \(%\newcommand{\subheading}[1]{\textbf{\large\color{aaltodgreen}#1}}\) \(\newcommand{\subheading}[1]{\large{\usebeamercolor[fg]{frametitle} #1}}\) \(\) \(\newcommand{\Blue}[1]{{\color{flagblue}#1}}\) \(\newcommand{\Red}[1]{{\color{aaltored}#1}}\) \(\newcommand{\Emph}[1]{\emph{\color{flagblue}#1}}\) \(\) \(\newcommand{\Engl}[1]{({\em engl.}\ #1)}\) \(\) \(\newcommand{\Pointer}{\raisebox{-1ex}{\huge\ding{43}}\ }\) \(\) \(\newcommand{\Set}[1]{\{#1\}}\) \(\newcommand{\Setdef}[2]{\{{#1}\mid{#2}\}}\) \(\newcommand{\PSet}[1]{\mathcal{P}(#1)}\) \(\newcommand{\Card}[1]{{\vert{#1}\vert}}\) \(\newcommand{\Tuple}[1]{(#1)}\) \(\newcommand{\Implies}{\Rightarrow}\) \(\newcommand{\Reals}{\mathbb{R}}\) \(\newcommand{\Seq}[1]{(#1)}\) \(\newcommand{\Arr}[1]{[#1]}\) \(\newcommand{\Floor}[1]{{\lfloor{#1}\rfloor}}\) \(\newcommand{\Ceil}[1]{{\lceil{#1}\rceil}}\) \(\newcommand{\Path}[1]{(#1)}\) \(\) \(%\newcommand{\Lg}{\lg}\) \(\newcommand{\Lg}{\log_2}\) \(\) \(\newcommand{\BigOh}{O}\) \(%\newcommand{\BigOh}{\mathcal{O}}\) \(\newcommand{\Oh}[1]{\BigOh(#1)}\) \(\) \(\newcommand{\todo}[1]{\Red{\textbf{TO DO: #1}}}\) \(\) \(\newcommand{\NULL}{\textsf{null}}\) \(\) \(\newcommand{\Insert}{\ensuremath{\textsc{insert}}}\) \(\newcommand{\Search}{\ensuremath{\textsc{search}}}\) \(\newcommand{\Delete}{\ensuremath{\textsc{delete}}}\) \(\newcommand{\Remove}{\ensuremath{\textsc{remove}}}\) \(\) \(\newcommand{\Parent}[1]{\mathop{parent}(#1)}\) \(\) \(\newcommand{\ALengthOf}[1]{{#1}.\textit{length}}\) \(\) \(\newcommand{\TRootOf}[1]{{#1}.\textit{root}}\) \(\newcommand{\TLChildOf}[1]{{#1}.\textit{leftChild}}\) \(\newcommand{\TRChildOf}[1]{{#1}.\textit{rightChild}}\) \(\newcommand{\TNode}{x}\) \(\newcommand{\TNodeI}{y}\) \(\newcommand{\TKeyOf}[1]{{#1}.\textit{key}}\) \(\) \(\newcommand{\PEnqueue}[2]{{#1}.\textsf{enqueue}(#2)}\) \(\newcommand{\PDequeue}[1]{{#1}.\textsf{dequeue}()}\) \(\) \(\newcommand{\Def}{\mathrel{:=}}\) \(\) \(\newcommand{\Eq}{\mathrel{=}}\) \(\newcommand{\Asgn}{\mathrel{\leftarrow}}\) \(%\newcommand{\Asgn}{\mathrel{:=}}\) \(\) \(%\) \(% Heaps\) \(%\) \(\newcommand{\Downheap}{\textsc{downheap}}\) \(\newcommand{\Upheap}{\textsc{upheap}}\) \(\newcommand{\Makeheap}{\textsc{makeheap}}\) \(\) \(%\) \(% Dynamic sets\) \(%\) \(\newcommand{\SInsert}[1]{\textsc{insert}(#1)}\) \(\newcommand{\SSearch}[1]{\textsc{search}(#1)}\) \(\newcommand{\SDelete}[1]{\textsc{delete}(#1)}\) \(\newcommand{\SMin}{\textsc{min}()}\) \(\newcommand{\SMax}{\textsc{max}()}\) \(\newcommand{\SPredecessor}[1]{\textsc{predecessor}(#1)}\) \(\newcommand{\SSuccessor}[1]{\textsc{successor}(#1)}\) \(\) \(%\) \(% Union-find\) \(%\) \(\newcommand{\UFMS}[1]{\textsc{make-set}(#1)}\) \(\newcommand{\UFFS}[1]{\textsc{find-set}(#1)}\) \(\newcommand{\UFCompress}[1]{\textsc{find-and-compress}(#1)}\) \(\newcommand{\UFUnion}[2]{\textsc{union}(#1,#2)}\) \(\) \(\) \(%\) \(% Graphs\) \(%\) \(\newcommand{\Verts}{V}\) \(\newcommand{\Vtx}{v}\) \(\newcommand{\VtxA}{v_1}\) \(\newcommand{\VtxB}{v_2}\) \(\newcommand{\VertsA}{V_\textup{A}}\) \(\newcommand{\VertsB}{V_\textup{B}}\) \(\newcommand{\Edges}{E}\) \(\newcommand{\Edge}{e}\) \(\newcommand{\NofV}{\Card{V}}\) \(\newcommand{\NofE}{\Card{E}}\) \(\newcommand{\Graph}{G}\) \(\) \(\newcommand{\SCC}{C}\) \(\newcommand{\GraphSCC}{G^\text{SCC}}\) \(\newcommand{\VertsSCC}{V^\text{SCC}}\) \(\newcommand{\EdgesSCC}{E^\text{SCC}}\) \(\) \(\newcommand{\GraphT}{G^\text{T}}\) \(%\newcommand{\VertsT}{V^\textup{T}}\) \(\newcommand{\EdgesT}{E^\text{T}}\) \(\) \(%\) \(% NP-completeness etc\) \(%\) \(\newcommand{\Poly}{\textbf{P}}\) \(\newcommand{\NP}{\textbf{NP}}\) \(\newcommand{\PSPACE}{\textbf{PSPACE}}\) \(\newcommand{\EXPTIME}{\textbf{EXPTIME}}\)

Selection sort

Selection sort is an elementary sorting algorithm based on the following simple idea:

  • ​ Find a smallest element in the array and swap it with the first one.

  • ​ Repeat the previous step when considering the sub-array obtained by excluding the first element, until the sub-array consists of only one element.

A straightforward Scala implementation of the algorithm for integer arrays is given below.

def selectionSort(a: Array[Int]): Unit = {
  val n = a.length
  var i = 0 // The first index of the sub-array considered
  while(i < n-1) {
    // Find the smallest element in the sub-array a(i..n-1)
    var smallest_element = a(i)
    var smallest_index = i
    var j = i + 1
    while(j < n) {
      if(a(j) < smallest_element) {
	smallest_element = a(j)
	smallest_index = j
      }
      j += 1
    }
    // Swap the elements at a(i) and a(smallest_index)
    a(smallest_index) = a(i)
    a(i) = smallest_element
    i += 1
  }
}

Example: Selection sort

The animation below shows how selection sort works on a smallish array of integers. Observe how the example illustrates that selection sort is not stable: the relative positions of the elements 10 swap in the sorting process.

Note that only the current minimum element candidate is stored outside the array at any point of time. Thus the amount of extra memory required is \(\Theta(1)\) and the algorithm works in place.

Running time analysis

Running time analysis of the algorithm is quite straightforward. The body of the inner while-loop takes constant time. So do all the all instructions, excluding the inner while-loop, in the outer while-loop. For the outer while-loop index \(i\), the inner loop body is executed \(n-1-i\) times. Thus the running time is \(\sum_{i=0}^{n-2}(c + (n-1-i)d)\), where \(c\) and \(d\) are constants capturing the constant running times of the outer while-loop body (excluding the inner while-loop) and the inner while-loop body. The sum expands to \((n-1)c + d((n-1) + (n-2) + ... + 1) = (n-1)c + d \frac{(n-1)(n-2)}{2} = \Theta(n^2)\). The asymptotic best-case and worst-case running times are the same as the outer and inner while-loops are always run to the end regardless of the array contents.

Experimental evaluation

The plots below shows the performance of the above Scala selection sort code when compared to the sorted method of the Array class. When inspecting the latter plot having logarithmic y-axis, we can see that the gap between the lines increases when the array sizes increase. Thus there is more than a constant time factor difference between the running times of the codes. Based on the plots, it is obvious that the sorted method implements an algorithm that scales much better than selection sort.

_images/selection-vs-sorted.jpg _images/selection-vs-sorted-logy.jpg

Generics vs fixed types

By using generics (see also this, this and this), it is easy to build a generic selection sort implementation that works for any comparable object type (floating point numbers, strings etc). Using an Ordering ord for the array elements, we only have to change two lines in the code: the method declaration and the comparison line.

def selectionSort[A](a: Array[A])(implicit ord: math.Ordering[A]): Unit = {
  val n = a.length
  var i = 0
  while(i < n-1) {
    // Find the smallest element in the sub-array a(i..n-1)
    var smallest_element = a(i)
    var smallest_index = i
    var j = i + 1
    while(j < n) {
      if(ord.lt(a(j), smallest_element)) {  // CHANGED
	smallest_element = a(j)
	smallest_index = j
      }
      j += 1
    }
    // Swap the elements at a(i) and a(smallest_index)
    a(smallest_index) = a(i)
    a(i) = smallest_element
    // The sub-array a(0..i) is now sorted
    i += 1
  }
}

Unfortunately, with current Scala compiler, the generality does not come without a performance penalty. As an example, consider applying the generic selection sort to arrays of integers. To apply the lt comparison method in the Ordering trait, each integer fetched from the array is “boxed” (wrapped into an object), causing a memory allocation and then later garbage collection. In addition, several virtual function invocations are performed. We can see the practical effect of this by comparing the running times of our first selection sort working only on integer arrays to those of the generic selection sort algorithm above.

_images/selection-int-vs-generic.jpg

If we use logarithmic scale on the \(y\)-axis, we can see that the running times of the generic version are a constant factor (of roughly ten) slower than those of the non-generic integer array version.

_images/selection-int-vs-generic-logy.jpg