\(\)
\(%\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}{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}}\)
\(\newcommand{\Lo}{\mathit{lo}}\)
\(\newcommand{\Hi}{\mathit{hi}}\)
\(\newcommand{\Mid}{\mathit{mid}}\)
Describing algorithms
In an abstract view,
an algorithm can be seen as a method that transforms some input to output.
For instance, a sorting algorithm transforms an array of elements
into another array that contains the same elements but in a sorted order.
In order to communicate algorithms to other people,
we have to describe them in some way.
Of course, one could describe the algorithm
by giving its implementation in some concrete programming language.
However, in many cases,
it can be easier and beneficial to describe an algorithm
As an example, recall the binary search algorithm discussed in the CS-A1120 Programming 2 course.
In the input/output-level abstraction,
the algorithm fulfills the following specification:
Input: a sorted array \( A \) of elements and an element \( e \).
Output: “true” if \( e \) appears in \( A \), “false” otherwise.
In a natural language (English in this case),
we may describe the binary search algorithm
in the following structured way:
Initially, consider the whole array.
If the considered array is empty, return “false”
If the middle element of the array is \( e \), return “true”.
Otherwise, if the middle element is greater than \( e \),
go to step 2 but only consider the subarray from the beginning to (but not including) the middle element.
Otherwise, the middle element is less than \( e \) and we
go to step 2 but consider the subarray from (but not including) the middle element to the end of the array.
In pseudocode, we may describe the binary search algorithm as follows.
Note that the same algorithmic idea is given as a slightly different variant:
the helper function searches for the index in which the element must occur
if it occurs in \( A \) at all,
and terminates the search when the considered sub-array is of size 1.
// Search for the element \( e \) in the sorted array \( A \)
binary-search(\( A \), \( e \)):
return search(\( A \), \( e \), \( 1 \), \( \ALengthOf{A} \))
// A recursive helper function doing the actual search
search(\( A \), \( e \), \( \Lo \), \( \Hi \)):
if \( \Lo \le \Hi \): // sub-array of size 1 or more?
\( \Mid \Asgn \Lo + \Floor{(\Hi - \Lo) / 2} \)
if \( A[\Mid] \Eq e \): return true
else if \( e < A[\Mid] \): return search(\( A \), \( e \), \( \Lo \), \( \Mid-1 \))
else: return search(\( A \), \( e \), \( \Mid+1 \), \( \Hi \))
else:
return false
The pseudocode presentation only expresses the essential operations
needed to communicate the algorithm idea:
many details, such as type information, are simply omitted.
As in many programming languages (but not in Scala),
array indexing is denoted with the square brackets.
Please also notice that in many cases, array indexing starts
(i) from \( 1 \) in pseudocode and in mathematics but
(ii) from \( 0 \) in real programming languages.
Take this into account when implementing algorithms from books, wikipedia, scientific articles etc.
Of course, we may also describe an algorithm by giving
an implementation in some programming language.
For instance,
below is an implementation of the binary search algorithm in Scala.
def binarySearch[T <% Ordered[T]](arr: Array[T], e: T): Boolean = {
def inner(lo: Int, hi: Int): Boolean = {
if(lo <= hi) {
val mid = lo + ((hi - lo) / 2)
val cmp = e compare arr(mid)
if(cmp == 0) true // e == arr(mid)
else if(cmp < 0) inner(lo, mid-1) // e < arr(mid)
else inner(mid+1, hi) // e > arr(mid)
} else
false
}
inner(0, arr.length-1)
}
Similarly, the same implementation but in C++11:
#include <functional>
#include <vector>
template<typename T>
bool binarySearch(std::vector<T>& arr, T& e) {
std::function<int(int,int)> inner = [&](int lo, int hi) -> int {
if(lo <= hi) {
const int mid = lo + ((hi - lo) / 2);
if(e == arr[mid])
return true;
else if(e < arr[mid])
return inner(lo, mid-1);
else // e > arr[mid]
return inner(mid+1, hi);
} else
return false;
};
return inner(0, arr.size()-1);
}
These descriptions have the drawback
that a reader who is unfamiliar with the applied programming language
may get lost in language specific details when trying to understand
the core idea of the algorithm.
For instance, in the above Scala code,
is [T <% Ordered[T]]
relevant for the algorithm itself?
Similarly,
a reader who is not familiar with C++ may feel lost
when seeing the line
std::function<int(int,int)> inner = [&](int start, int end) -> int {
.