0% found this document useful (0 votes)
119 views

Afshine Amidi, Shervine Amidi - Algorithms & Data Structures_ Super Study Guide (2022)

The 'Super Study Guide' by Afshine and Shervine Amidi covers foundational concepts in algorithms and data structures, including algorithm types, complexities, and mathematical concepts. It also addresses classic problems, various data structures like arrays, stacks, and trees, as well as sorting and search algorithms. The book is structured into sections that progressively build on these topics to aid in understanding and application.

Uploaded by

RafiullahOmar
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
119 views

Afshine Amidi, Shervine Amidi - Algorithms & Data Structures_ Super Study Guide (2022)

The 'Super Study Guide' by Afshine and Shervine Amidi covers foundational concepts in algorithms and data structures, including algorithm types, complexities, and mathematical concepts. It also addresses classic problems, various data structures like arrays, stacks, and trees, as well as sorting and search algorithms. The book is structured into sections that progressively build on these topics to aid in understanding and application.

Uploaded by

RafiullahOmar
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 139

Super Study Guide

Algorithms &
Data Structures

Afshine Amidi and Shervine Amidi


Cover Design: Afshine Amidi and Shervine Amidi

© 2022 Afshine Amidi and Shervine Amidi


All rights reserved. No part of this publication may be reproduced, stored in a re-
trieval system, or transmitted, in any form or by any means (electronic, mechanical,
photocopying, recording or otherwise), without the prior written permission of the
authors.

First edition
We would like to dedicate this book to our beloved grandparents,
Dr. Mohammad Sadegh Azimi and Dr. Atieh Azimi,
who will always stay in our hearts.

A lifetime flies as quickly as the pages


of a book flipped through by the wind.
Enjoy it as much as you can.
– Saeb Tabrizi
Super Study Guide Contents

Contents

1 Foundations 1
1.1 Algorithmic concepts . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1 Overview . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.2 Types of algorithms . . . . . . . . . . . . . . . . . . . . . . . 3
1.1.3 Complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
1.2 Mathematical concepts . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.1 Combinatorics . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 Mathematical analysis . . . . . . . . . . . . . . . . . . . . . . 12
1.2.3 Bit manipulation . . . . . . . . . . . . . . . . . . . . . . . . . 13
1.3 Classic problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.1 Traveling salesman . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.2 Knapsack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
1.3.3 N -Queens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
1.3.4 Coin change . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

2 Data structures 26
2.1 Arrays and strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.1.1 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.1.2 Strings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
2.2 Stacks and queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.2.1 Stacks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.2.2 Queues . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.3 Hash tables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.3.1 General concepts . . . . . . . . . . . . . . . . . . . . . . . . . 37
2.3.2 Collisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.4 Advanced hash tables . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.4.1 Bloom filters . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2.4.2 Count-min sketches . . . . . . . . . . . . . . . . . . . . . . . 42
2.5 Linked lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
2.5.1 Singly linked lists . . . . . . . . . . . . . . . . . . . . . . . . . 45
2.5.2 Doubly linked lists . . . . . . . . . . . . . . . . . . . . . . . . 49

3 Graphs and trees 53


3.1 Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.1.1 General concepts . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.1.2 Graph traversal . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.1.3 Shortest path . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
3.2 Advanced graph algorithms . . . . . . . . . . . . . . . . . . . . . . . 69

i
Super Study Guide Contents

3.2.1 Spanning trees . . . . . . . . . . . . . . . . . . . . . . . . . . 69


3.2.2 Components . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
3.3 Trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
3.3.1 General concepts . . . . . . . . . . . . . . . . . . . . . . . . . 75
3.3.2 Binary trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
3.3.3 Heaps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
3.3.4 Binary search trees . . . . . . . . . . . . . . . . . . . . . . . . 84
3.3.5 N -ary trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
3.4 Advanced trees . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
3.4.1 Self-balancing trees . . . . . . . . . . . . . . . . . . . . . . . . 90
3.4.2 Efficient trees . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

4 Sorting and search 101


4.1 Sorting algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.1.1 General concepts . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.1.2 Basic sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4.1.3 Efficient sort . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
4.1.4 Special sort . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
4.2 Search algorithms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
4.2.1 Basic search . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
4.2.2 Binary search . . . . . . . . . . . . . . . . . . . . . . . . . . . 119
4.2.3 Substring search . . . . . . . . . . . . . . . . . . . . . . . . . 122

Index 131

ii
Super Study Guide Foundations

SECTION 1

Foundations

In this first section, we will start with the basic concepts of algorithms, along
with mathematics notions that are used in many problems.

1.1 Algorithmic concepts


In this part, we will study the main types of algorithms and learn how to quantify
their complexities.

1.1.1 Overview
❒ Algorithm – Given a problem, an algorithm A is a set of well-defined instructions
that runs in a finite amount of time and space. It receives an input I and returns
an output O that satisfies the constraints of the problem.

I O

As an example, a problem can be to check whether a number is even. In order to


do that, an algorithm could be to check whether the number is divisible by 2.
❒ Iteration – An iterative algorithm is an algorithm that runs through a sequence
of actions. It is characterized by either a for or a while loop.

Suppose we want to return the sum of all of the elements of a given list. An example
of an iterative algorithm would be to sequentially add each element of the list to a
variable, and return its final value.
❒ Recursion – A recursive algorithm uses a function that calls itself. It is composed
of the following components:

• Base case: This is the set of inputs for which the outputs are known.

• Recursive formula: The answer of the current step is based on function calls
relying on previous steps, eventually using the base case answer.

1
Super Study Guide Foundations

...
... ...

...
...
...

A classic problem is to compute the power of a number xn without explicitly using


the power operation. In order to do that, a recursive solution could rely on the
following cases:

0
x =1
 is known
n n
n
x = x2 × x2 when n ∈ N∗ is even
 n−1 n−1
when n ∈ N∗ is odd
 n
x =x×x 2 ×x 2

❒ Call stack – In a recursive algorithm, the space used by function calls ci is called
the stack space.
cn
...

c2
c1

❒ Stack overflow – The problem of stack overflow occurs when a recursive algo-
rithm uses more stack space than the maximum allowed N .
cN+1
cN
...

c2
c1

A solution to circumvent this bottleneck is to convert the code from being recursive
to being iterative so that it relies on memory space, which is typically bigger than
stack space.
❒ Memoization – Memoization is an optimization technique aimed at speeding up
the runtime by storing results of expensive function calls and returning the cache
when the same result is needed.

2
Super Study Guide Foundations

...

1.1.2 Types of algorithms


❒ Brute-force – A brute-force approach aims at listing all the possible output
candidates of a problem and checking whether any of them satisfies the constraints.
It is generally the least efficient way of solving a problem.

To illustrate this technique, let’s consider the following problem: given a sorted
array A, we want to return all pairs of elements that sum up to a given number.

• A brute-force approach would try all possible pairs (ai , aj ) and return those
that sum up to that number. This method produces the desired result but
not in minimal time.

• A non-brute-force approach could use the fact that the array is sorted and
scan the array using the two-pointer technique.

❒ Backtracking – A backtracking algorithm recursively generates potential solu-


tions and prunes those that do not satisfy the problem constraints. It can be seen
as a version of brute-force that discards invalid candidates as soon as possible.

As an example, the N -Queens problem aims at finding a configuration of N queens


on a N × N chessboard where no two queens attack each other. A backtracking
approach would consist of placing queens one at a time and prune solution branches
that involve queens attacking each other.
❒ Greedy – A greedy algorithm makes choices that are seen as optimal at every
given step. However, it is important to note that the resulting solution may not be
optimal globally. This technique often leads to relatively low-complexity algorithms
that work reasonably well within the constraints of the problem.

3
Super Study Guide Foundations

t
0 N

To illustrate this concept, let’s consider the problem of finding the longest path
from a given starting point in a weighted graph. A greedy approach constructs
the final path by iteratively selecting the next edge that has the highest weight.
The resulting solution may miss a longer path that has large edge weights "hidden"
behind a low-weighted edge.

❒ Divide and conquer – A divide and conquer (D&C) algorithm computes the
final result of a problem by recursively dividing it into independent subproblems:

• Divide: The problem is divided into several independent subproblems.

P P1 P2

• Conquer: Each subproblem is solved independently.

Pi → Si

• Combine: The result of each subproblem is combined to find the final answer.

S S1 S2

Algorithms following the D&C principle include sorting algorithms such as merge
sort and quick sort.

❒ Dynamic Programming – Dynamic programming (DP) is a method of problem


resolution that relies on finding answers to overlapping subproblems.

P P1 P2

A common example of problem resolution using DP is the computation of Fibonacci


numbers.
?
F0 F1 F2 ... Fn−2 Fn−1 Fn ...

There are two main approaches:

4
Super Study Guide Foundations

Top-down This approach finds the target value by recursively computing previ-
ous values.
• Step 1 : Try computing the desired value Fn and notice that it is based on
previous values.

F0 F1 F2 ... Fn−2 Fn−1 Fn ...

• Step 2 : Try computing the previous values, which themselves rely on earlier
values, some of which may have already been computed. In this case, we can
use memoization to avoid duplicate operations.

F0 F1 F2 ... Fn−2 Fn−1 Fn ...

• Step 3 : Use the newly computed values to deduce Fn .


F0 F1 F2 ... Fn−2 Fn−1 Fn ...

Bottom-up This approach starts from already-known results and iteratively com-
putes succeeding values until it reaches the target value.
• Step 1 : Compute F0 , F1 , F2 , etc. in a predetermined way. These values are
typically stored in an array.
F0 F1 F2 ... Fn−2 Fn−1 Fn ...

• Step 2 : Deduce Fn .
F0 F1 F2 ... Fn−2 Fn−1 Fn ...

In summary, the two main ways of solving a problem with DP are:


Top-down Bottom-up
Recursive approach Iterative approach

F0 F1 ... Fn F0 F1 ... Fn

Remark: A key difference between DP and D&C strategies is that DP relies on


overlapping subproblems whereas D&C bases itself on independent subproblems.

5
Super Study Guide Foundations

1.1.3 Complexity
❒ Definition – The concept of complexity is used to quantify the efficiency of an
algorithm. There are two types of complexities:

• Time: How many operations are made?

• Space: How much extra space is needed?

Both measures are usually given as a function of the input size n, although other
parameters can also be used.
❒ Notations – The complexity f of an algorithm can be described using a known
function g with the notations described in the table below:

Notation Definition Meaning Illustration

g
f = o(g) ∀ϵ > 0, ∃n0 , ∀n ⩾ n0 Negligible compared to g

"little oh of g" |f (n)| ⩽ ϵ|g(n)| f (n) ≪ g(n) ϵ⋅g


n→+∞
n0 f

c⋅g
f = O(g) ∃c > 0, ∃n0 , ∀n ⩾ n0 Upper-bounded by g f
g
"big oh of g" |f (n)| ⩽ c|g(n)| f (n) ⩽ g(n)
n→+∞
n0

f = Ω(g) ∃c > 0, ∃n0 , ∀n ⩾ n0 Lower-bounded by g f


g
f (n) ⩾ g(n)
"omega of g" |f (n)| ⩾ c|g(n)| n→+∞ c⋅g
n0

c2 ⋅ g
∃c1 , c2 > 0, ∃n0 , ∀n ⩾ n0 Similar to g
f = Θ(g)
|f (n)| ⩾ c1 |g(n)| f g
c1 ⋅ g
"theta of g" f (n) ∼ g(n)
n→+∞
|f (n)| ⩽ c2 |g(n)|
n0

Remark: The big oh notation is frequently used to describe the time and space
complexity of a given algorithm.
❒ Orders of magnitude – The table below highlights the main kinds of runtime
complexities T (n) as a function of the input size n:

6
Super Study Guide Foundations

T (n) for different n


Complexity Example of application
101 102 103

O(1) Hash table lookup: Accessing the


1 value of a given key does not depend
"constant" on the size of the hash table.

O(log(n)) Binary search: The search space is


1 2 3
"logarithmic" divided by 2 at each iteration.

O(n) Linear search: All elements of the


101 102 103
"linear" input are visited.

O(n log(n)) Merge sort: The array is broken down


101 2 × 102 3 × 103 by two at every step and each step
"linearithmic" requires all elements to be checked.

O(n2 ) Bubble sort: Each pair of elements of


102 104 106
"quadratic" the input array is checked.

0/1 knapsack problem: The naive


O(2n ) approach would consist of trying out
∼ 103 ∼ 1030 ∼ 10301
"exponential" every combination of items in the
final set.

O(n!) Traveling salesman problem: The naive


∼ 106 ∼ 10158 ∼ 102567 approach consists of trying all possible
"factorial" permutations of cities to visit.

The following graph shows the difference in their evolutions:

T(n) n! 2n n 2 n log(n)
n

log(n)
n

As a rule of thumb, we have:

O(1) < O(log(n)) < O(n) < O(n log(n)) < O(n2 ) < O(2n ) < O(n!)

7
Super Study Guide Foundations

❒ Master theorem – The master theorem gives an explicit solution of a runtime


T (n) that satisfies a recursive relationship of the form below:
n
T (n) = aT + Θ(nd )
b
The solution depends on the relationship between a ∈ N∗ , b ∈ N∗ \{1} and d ⩾ 0:

Case Solution T (n) Example of application

Binary tree traversal: After a node is visited, we move


on to its left and right subtrees.
d < logb (a) Θ(nlogb (a) )
n
 
T (n) = 2T −→ T (n) = Θ(n)
2

Binary search: The search space is divided by 2 at


d each pass.
d = logb (a) Θ(n log(n))
n
 
T (n) = T −→ T (n) = Θ(log(n))
2

n
 
d > logb (a) Θ(nd ) T (n) = 4T + Θ(n3 ) −→ T (n) = Θ(n3 )
2

❒ Problem complexity – Problems can be divided into classes that quantify how
hard it is to solve them and to verify whether a proposed solution works. The table
below presents two well-known classes:

Problem class Description Example


The problem can be Sorting: An array of n elements can be
P
solved in polynomial
polynomial time sorted in O(n2 ) time with selection sort.
time.
Traveling salesman problem: Given a
NP A solution to the path of n cities and a target value, we
nondeterministic problem can be verified can determine whether the length of
polynomial time in polynomial time. the path is lower or equal than the
target value in O(n) time.

We note the following:

• P ⊆ NP: If it is possible to find a solution in polynomial time, then it is


possible to verify a candidate in polynomial time by computing the solution
directly.

8
Super Study Guide Foundations

?
• P ⊇ NP: It is unclear whether being able to verify a solution in polynomial
time implies that we can solve the problem in polynomial time. The general
consensus is that P ̸⊇ NP (hence P ̸= NP) but this has not been formally
proven yet.

Any problem in NP can be reduced in polynomial time to the following set of


problems:

• NP-hard problems, that are at least as hard as NP problems

• NP-complete problems, that are NP-hard problems in NP

"harder"
P ?

NP
NP-complete
NP-hard

1.2 Mathematical concepts


In this part, we will dig into important mathematical notions and results spanning
the fields of combinatorics and mathematical analysis, along with some basics on bit
manipulation.

1.2.1 Combinatorics
❒ Factorial – The factorial n! of a given integer n is defined as follows:

n! ≜ n × (n − 1) × ... × 2 × 1

Remark: By convention, 0! = 1.
 
n
❒ Binomial coefficient – For given integers 0 ⩽ k ⩽ n, the notation is read
k
"n choose k" and is called a binomial coefficient. It is defined as follows:
 
n n!

k k!(n − k)!

For k, n ∈ N∗ , Pascal’s rule gives a relationship between neighboring coefficients:


     
n n−1 n−1
= +
k k k−1

9
Super Study Guide Foundations

The evolution of binomial coefficients with respect to k and n can be visualized


using Pascal’s triangle:
k
1
1 1
1 2 1
1 3 3 1
n 1 4 6 4 1
1 5 10 10 5 1

❒ Binomial theorem – The binomial theorem states that for x, y ∈ R and n ∈ N,


we have:

n  
n
X n
(x + y) = xn−k y k
k
k=0

We note that when x = y = 1, we have:


n  
X n
= 2n
k
k=0

❒ Combination – A combination is an arrangement of k objects from a pool of


n objects where the order does not matter. The number of such arrangements is
given by C(n, k):
 
n n!
C(n, k) = =
k k!(n − k)!

For k = 2 choices among n = 3 elements, there are C(3, 2) = 3 possible combina-


tions.

❒ Pigeonhole principle – Suppose n items in m containers, with n > m. The


pigeonhole principle states that at least one container has more than one item.

10
Super Study Guide Foundations

❒ Permutation – A permutation is an arrangement of k objects from a pool of


n objects where the order matters. The number of such arrangements is given by
P (n, k):

n!
P (n, k) = C(n, k) × k! =
(n − k)!

For k = 2 choices among n = 3 elements, there are P (3, 2) = 6 possible permuta-


tions.

1 2 1 2 1 2

2 1 2 1 2 1

Remark: For 0 ⩽ k ⩽ n, we have P (n, k) ⩾ C(n, k).


❒ Lexicographic ordering – Given a set of n elements, the lexicographic ordering
sorts the resulting P (n, n) = n! permutations in an alphabetical way.

To illustrate this, the lexicographic ordering of the 3! permutations of {2, 6, 7} is as


follows:
2 6 7 2 7 6 6 2 7 6 7 2 7 2 6 7 6 2

We note that this way of ordering permutations can be applied to sets containing
any type of ordered elements, such as integers and characters.
❒ Next lexicographic permutation – Given an array A = [a0 , ..., an−1 ] rep-
resenting a permutation of the set of n elements {a0 , ..., an−1 }, the goal of this
problem is to find the next permutation in the lexicographic order.

2 3 8 6 5 2 ?
0 1 2 3 4 5

The solution is found in O(n) time and O(1) space as follows:

• Step 1 : Find the largest index k, such that ak < ak+1 .

2 3 8 6 5 2
0 1 2 3 4 5
k

11
Super Study Guide Foundations

If there is no such k, go directly to Step 4 and use k = −1.

• Step 2 : Find the largest index l > k, such that ak < al .

2 3 8 6 5 2
0 1 2 3 4 5
k l

• Step 3 : Swap ak and al .

2 3 8 6 5 2 2 5 8 6 3 2
0 1 2 3 4 5 0 1 2 3 4 5

• Step 4 : Reverse the subarray that starts from index k + 1.

2 5 8 6 3 2 2 5 2 3 6 8
0 1 2 3 4 5 0 1 2 3 4 5

1.2.2 Mathematical analysis


❒ Statistics – Given a set of ordered observations x1 ⩽ ... ⩽ xn , we can compute
the following quantities:

Mean Median Mode


n is odd n is even
x1 + ... + xn
x n2 + x n2 +1 argmax #xi
n x n+1 xi
2 2
Sum of values divided by Value that occurs the
Middle value of the sorted
the number of values. most frequently in the
set of observations.
Also known as average. set of observations.

❒ Euclidean division – The Euclidean division of a ∈ N by b ∈ N∗ finds the


unique values of q ∈ N and r ∈ {0, ..., b − 1} such that:

12
Super Study Guide Foundations

a = bq + r
a is called the dividend, b the divisor, q the quotient and r the remainder.

This can be written in a different way using the modulo notation:

a ≡ r [b]

which is read "a is equal to r modulo b".


❒ Fibonacci sequence – Fibonacci numbers Fn are a sequence defined by:

Fn = Fn−1 + Fn−2 with F0 = 0 and F1 = 1

They can be geometrically represented as follows:

F2
F3
F0 F1
...
F4

Remark: The first ten numbers of the sequence are 0, 1, 1, 2, 3, 5, 8, 13, 21, 34.

1.2.3 Bit manipulation


❒ Integer representation – The decomposition of an integer n in base b is unique
and given by:

+∞
X
n= nk bk where nk ∈ {0, ..., b − 1}
k=0

The representation of n in base b can be equivalently written as:

13
Super Study Guide Foundations

n = (...nk ...n3 n2 n1 n0 )b

The most commonly used bases are summarized in the table below:

Representation
Base b Possible values nk Application
of n = 154
2
{0, 1} (10011010)2 Logic
"binary"
10
{0, ..., 9} (154)10 Day-to-day life
"decimal"
16
{0, ..., 9, A, ..., F } (9A)16 Memory allocation
"hexadecimal"

❒ Binary number – A binary number is the representation of an integer n in base


2. It is expressed as follows:

+∞
X
n= nk 2k where nk ∈ {0,1}
k=0

Each nk is called a binary digit, often abbreviated as bit. We note that:

• If n0 = 0, then n is an even number.

• If n0 = 1, then n is an odd number.

❒ Bit notations – A binary number represented with N bits has the following
bit-related notations:

Notation Description Illustration with N = 10


29 28 27 26 25 24 23 22 21 20
Least significant bit Right-most bit.
0 0 1 1 0 1 1 1 0 0
Right-most bit that 29 28 27 26 25 24 23 22 21 20
Lowest set bit
has a value of 1. 0 0 1 1 0 1 1 1 0 0
Left-most bit that 29 28 27 26 25 24 23 22 21 20
Highest set bit
has a value of 1. 0 0 1 1 0 1 1 1 0 0
29 28 27 26 25 24 23 22 21 20
Most significant bit Left-most bit.
0 0 1 1 0 1 1 1 0 0

Remark: The abbreviation "LSB" is sometimes ambiguous as it may either refer to


the least significant bit or the lowest set bit.

14
Super Study Guide Foundations

❒ Bitwise operators – The truth table of the bitwise operators OR, XOR, AND
between x ∈ {0, 1} and y ∈ {0, 1} is given below:

OR XOR AND
x y x|y x∧y x&y
0 0 0 0 0
1 0 1 1 0
0 1 1 1 0
1 1 1 0 1

❒ Bit shifting – Shifting operations change the binary representation of a number


as follows:

Operation Description Illustration

Binary representation of x 10001100011011110


x≫y
moved by y bits to the >> 3
"right shift"
right. 00010001100011011

Binary representation of x 00001100011011110


x≪y
moved by y bits to the << 2
"left shift"
left. 00110001101111000

❒ Tricks – The table below shows a few bit-related tricks that are useful in estab-
lishing non-trivial results with minimal effort:

Operation Result Illustration


10000000000000000
0
x & (x − 1) AND 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
⇐⇒ x is a power of 2
00000000000000000
10001100011011110
0
x&1 AND 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
⇐⇒ x is divisible by 2
00000000000000000
00000000000000001
1≪j 2j << 5
00000000000100000

15
Super Study Guide Foundations

n−1
X
Remark: The first trick stems from the fact that 2n − 1 = 2k .
k=0

❒ Integer overflow – The problem of integer overflow occurs when the number of
bits needed to encode an integer n exceeds the number of available bits N . More
precisely, binary numbers encoded on N bits cannot exceed 2N − 1.

2N−1 29 28 27 26 25 24 23 22 21 20
... X X 1 ... 0 0 1 1 0 1 1 1 0 0

This issue can sometimes be alleviated by changing the order in which operations
are performed. For example, a safe way to average two integers a and b is shown
below:

Naive Safe
a+b b−a
Formula a+
2 2

si is the operation done at step i.

Order of s1 = b − a
s1 = a + b s1
operations s1 s2 =
s2 = 2
2 s3 = a + s2

To illustrate this, we take a = b = 231 and max = 232 − 1.

s1 = 231 + 231 = 232 s1 = 231 − 231 = 0


Example 232 0
s2 = = 231 s2 = = 0
2 2
Step 1 led to integer overflow s3 = 231 + 0 = 231
because s1 > 232 − 1. There was no integer overflow.

1.3 Classic problems


This part covers classic computer science problems and presents detailed approaches
to solve them.

1.3.1 Traveling salesman


Given n cities c1 , ..., cn , the traveling salesman problem (TSP) is a classic problem
that aims at finding the shortest path that visits all cities exactly once and then
returns to the starting city.

16
Super Study Guide Foundations

The distance between each pair of cities (ci , cj ) is noted di,j and is known.
di,j
ci cj

A naive approach would consist of enumerating all possible solutions and finding
the one that has the minimum cumulative distance. Given a starting city, there are
(n − 1)! possible paths, so this algorithm would take O(n!) time. This approach is
impracticable even for small values of n.

Luckily, the Held-Karp algorithm provides a bottom-up dynamic programming ap-


proach that has a time complexity of O(n2 2n ) and a space complexity of O(n2n ).

Suppose c1 is the starting city. This arbitrary choice does not influence the final
result since the resulting path is a cycle. We define the following quantities:
• Ck contains all sets of k distinct cities in {c2 , ..., cn }:
n o
for k ∈ [[0, n − 1]], Ck = Ck |Ck ⊆ {c2 , ..., cn }, #Ck = k
 
n−1
We note that Ck has a size of .
k

17
Super Study Guide Foundations

• g(Ck , cj ) is the distance of the shortest path that starts from c1 , goes through
each city in Ck ∈ Ck exactly once and ends at cj ∈ / Ck .

Ck ∈ 𝒞k
c1
g(Ck, cj) cj

The solution is found iteratively:

• Initialization: The shortest path between the starting city c1 and each of the
other cities cj with no intermediary city is directly given by d1,j :

∀j ∈ [[2, n]], g(C0 , cj ) = d1,j

C0 = Ø
c1
g(C0, cj)
cj

• Compute step: Suppose that for all Ck−1 ∈ Ck−1 and ci ̸∈ Ck−1 , we know the
distance of the shortest path g(Ck−1 , ci ) between c1 and ci via the k − 1 cities
in Ck−1 .

Let’s take Ck ∈ Ck . For all ci ∈ Ck , we note that Ck \{ci } ∈ Ck−1 and that
(naturally) ci ̸∈ Ck \{ci }, which means that g(Ck \{ci }, ci ) is known.

We can deduce the distance of the shortest path between c1 and cj ̸∈ Ck via
k cities by taking the minimum value over all second-to-last cities ci ∈ Ck :
n o
∀Ck ∈ Ck , ∀cj ∈
/ Ck , g(Ck , cj ) = min g(Ck \{ci }, ci ) + di,j
ci ∈Ck

Ck \{ci} ∈ 𝒞k−1
c1 di,j
g(Ck \{ci}, ci) ci cj

Each step k has the following complexities:

18
Super Study Guide Foundations

Time Space
   
n−1 n−1
k × (n − 1 − k) × (n − 1 − k) ×
k k

• Final step: The solution is given by g({c2 , ..., cn }, c1 ).

Since Cn−1 = {c2 , ..., cn }, the last step of the algorithm gives:
n o
g(Cn−1 , c1 ) = min g(Cn−1 \{ci }, ci ) + di,1
i∈[[2,n]]

g(Cn−1\{ci}, ci)
Cn−1\{ci} ∈ 𝒞n−2
c1
di,1
ci

1.3.2 Knapsack
The 0/1 knapsack problem is a classic problem where the goal is to maximize the
sum of values of items put in a bag that has a weight limit. Here, "0/1" means that
for each item, we want to know whether we should include it (1) or not (0).

v1 v2 vn
...
C w1 w2 wn

More formally, we have a bag of capacity C and n items where each item i ∈ [[1, n]]
has value vi and weight wi . We want to find a subset of items I ⊆ {1, ..., n} such
that:
X X
I = argmax vi with wi ⩽ C
I⊆[[1,n]] i∈I i∈I

A naive approach would consist of trying out every combination of the n items and
take the one with the maximum value which also satisfies the cumulative weight
constraint. Such an approach has a time complexity of O(2n ).

Luckily, this problem can be solved with a bottom-up dynamic programming ap-
proach that has a time complexity of O(nC) and a space complexity of O(nC).

19
Super Study Guide Foundations

We note Vi,j the maximum value of a bag of capacity j ∈ [[0, C]] that contains items
among {1, 2, ..., i}. Our objective is to get Vn,C .

0 ... j ... C
0
...
i Vi,j
...
n Vn,C

• Initialization: The maximum values of the following bags are known:

– Bags of capacity 0: Vi,0 = 0.


– Bags with 0 item: V0,j = 0.

0 1 ... C
0 0 0 0 0
1 0
... 0
n 0

• Compute maximum value: Starting from i = 1 and j = 1, we iteratively fill


the 2D array of maximum values from left to right, top to bottom.

0 1 ... C
0
1
...
n

In order to determine the maximum value Vi,j of a bag of capacity j containing


items among {1, ..., i}, we need to choose between two hypothetical bags:

– Bag 1: contains item i. VB1 is found by adding the value vi of item i to


the maximum value of the bag of capacity j − wi containing items among
{1, ..., i − 1}.
VB1 = Vi−1,j−wi + vi

20
Super Study Guide Foundations

... j−wi ... j ...


...
i−1
i VB1 vi
...

– Bag 2: does not contain item i. VB2 is already known: it is the maximum
value of the bag of capacity j containing items among {1, ..., i − 1}.

VB2 = Vi−1,j

... j−wi ... j ...


...
i−1
i VB2
...

We choose the best potential bag:

Vi,j = max(VB1 , VB2 )

When the 2D array is filled, the desired value is Vn,C .


• Get final items: In order to know which items were selected, we start from
position (i, j) = (n, C) of the 2D array and traverse it iteratively:
– Case Vi,j ̸= Vi−1,j : This means that item i was included. We go to
position (i − 1, j − wi ) and include item i in the set of included items.

Vi−1,j−wi vi Vi,j

j − wi wi
j

– Case Vi,j = Vi−1,j : This means that item i was not included. We go to
position (i − 1, j).

Vi−1,j Vi,j

j
j

We repeat this process until retrieving all items.

21
Super Study Guide Foundations

1.3.3 N -Queens

Given a chessboard of size N × N , the N -Queens problem aims at finding a config-


uration of N queens such that no two queens attack each other.

A queen is a chess piece that can move horizontally, vertically or diagonally by any
number of steps.

The solution can be found using a backtracking approach:

• Step 1 : Put the 1st queen on the chessboard.

• Step 2 : Put the 2nd queen in the second column of the chessboard. As long as
the partial configuration is invalid, keep changing its position until conditions
are satisfied.

22
Super Study Guide Foundations

• Step 3 : If there is nowhere to place the next queen, go back one step and try
the next partial configuration.

• Step 4 : Continue this process until finding a valid solution.

1.3.4 Coin change

Given an unlimited number of coins of values {c1 , .., ck }, the coin change problem
aims at finding the minimum number of coins that sum up to a given amount S.
By convention, we assume that c1 < ... < ck .

$2 $5 $10 $20 $50 $100

1¢ 5¢ 10¢ 25¢ 50¢ $1

c1 c2 c3 c4 c5 c6 c7 c8 c9 c10 c11 c12

23
Super Study Guide Foundations

In other words, we want to find the minimum value of x = x1 + ... + xk such that
k
X
S= x i ci
i=1

The bottom-up dynamic programming solution iteratively computes the minimum


amount of coins ds for each amount s ∈ {1, ..., S}.

?
d0 d1 ... dS
0 1 S

This approach runs in O(S) time and takes O(S) space:

• Initialization:

– Initialize array D = [d0 , ..., dS ] with zeros.


– For each i ∈ [[1, ..., k]], set dci to 1 since we already know that the corre-
sponding amount only needs 1 coin.

1 1 1
c1 ... c2 ... ck ... S

• Compute step: Starting from s = c1 + 1, we iteratively fill array D.

c1 ... c2 ... ck ... S

In order to obtain amount s, we distinguish two cases:

– Case ∃i, ds−ci > 0: We look back at valid values ds−ci and see which
coin ci minimizes the total number of coins needed to obtain amount s.
n o
ds = min ds−ci + 1
i∈[[1,k]]
ds−ci >0

min +1
... ds−ci ... ds−c1 ds ...
s − ci s − c1 s S

– Case ∀i, ds−ci = 0: We cannot obtain amount s using coins ci .

24
Super Study Guide Foundations

At the end of this step, amounts s ∈ {1, .., S} with ds = 0 cannot be obtained
with the given coins.

The answer to this problem is given by dS .

25
Super Study Guide Data Structures

SECTION 2

Data structures

In this second section, we will go through the most common data structures
that are used in everyday algorithms, such as arrays, strings, queues, stacks, hash
maps, linked lists and more.

2.1 Arrays and strings

In this part, we will learn about arrays and strings along with common tricks such
as Kadane’s algorithm and the sliding window trick.

2.1.1 Arrays

❒ Definition – An array A is a data structure of fixed size n that stores elements


a0 , ..., an−1 in a sequential way. Each element ai can be accessed by its index i.

a0 a1 ... an−1
0 1 ... n−1

Most programming languages start their indices from 0. The resulting arrays are
said to be 0-indexed.

❒ Operations – The main operations that can be performed on an array are


explained below:

Type Time Description Illustration


Using index i, we can directly access the i th
a0 a1 a2 a3
Access O(1)
element of the array with A[i] = ai . 0 1 2 3

We need to search the array by checking


a0 a1 a2 a3
Search O(n) each element one by one until finding the
0 1 2 3
desired value.
1. Elements at indices i and up are moved
to the right. a2′
2. The new element is inserted at index i. a0 a1 a2 a3
Insertion O(n) 0 1 2 3
Note that if there is no space for the
new element to be added in the existing
a0 a1 a2 a2′ a3
array, we need to create a bigger array
0 1 2 3 4
and copy existing elements over there.

26
Super Study Guide Data Structures

Type Time Description Illustration


a0 a1 a2 a3 a4
1. Elements at indices i + 1 and up are
0 1 2 3 4
moved to the left.
Deletion O(n)
2. The former last element of the array
a0 a1 a3 a4 a4
is either ignored or removed.
0 1 2 3 4

❒ Kadane’s algorithm – Kadane’s algorithm computes the maximum subarray


sum of a given array A.

a0 ... ai1 ... ai2 ... an−1


0 ... i1 ... i2 ... n−1

i2
S max = max

ai
i1,i2
i=i1

Trick Scan the array and keep track of the following quantities:
• Sicurrent : the maximum subarray sum of A up to index i that contains ai .

• Simax : the maximum subarray sum of A up to index i.

Algorithm The solution is found in O(n) time and O(1) space:

• Initialization: Set S0current = S0max = a0 .

a0 ...
0 ...

S0current

• Update step: For each index i ∈ [[1, n − 1]], compute Sicurrent by distinguishing
two cases:
current
– Case Si−1 < 0: Appending ai to the currently tracked subarray sum
is worse than starting over. We set Sicurrent = ai .
... ai−1 ai ...
... i − 1 i ...

Sicurrent

current
– Case Si−1 ⩾ 0: It is worth continuing on the current subarray and
appending ai to it. We set Sicurrent = Si−1
current
+ ai .

27
Super Study Guide Data Structures

... ... ai−1 ai ...


... ... i−1 i ...

Sicurrent

Then, the maximum subarray sum seen so far is updated if a new maximum
is detected:
Simax = max Si−1
max
, Sicurrent


In the end, we output the maximum subarray sum of the array:

S max = Sn−1
max

Remark: In order to guarantee a space complexity of O(1), values of S current and


S max are updated in place at each step i.

❒ Merge intervals – The merge intervals problem is a classic problem that aims at
producing an array of non-overlapping intervals based on an array I = [I0 , ..., In−1 ]
of intervals that are potentially overlapping.

We note:

• I = [I0 , ..., In−1 ] the input array of potentially-overlapping intervals, with:

Ik = [ak , bk ] and ak ⩽ bk for k ∈ [[0, n − 1]]

a0 a1 an−1
... ...
a0 a1 b0 b1 an−1 bn−1 b0 b1 bn−1
0 1 ... n−1

• I ′ = [I0′ , ..., Im−1



] the output array of non-overlapping intervals, with:

Ik′ = [a′k , b′k ] and a′k ⩽ b′k for k ∈ [[0, m − 1]] with m ⩽ n

a′0 a′m−1
... ...
a′0 b′0 a′m−1 b′m−1 b′0 b′m−1
0 ... m−1

The output array I ′ is obtained in O(n log(n)) time and O(n) space:

• Initialization:

28
Super Study Guide Data Structures

– The input array I is sorted with respect to the lower bound ak of each
interval Ik in O(n log(n)) time. Without loss of generality, we renumber
intervals so that a0 ⩽ ... ⩽ an−1 .
– The output array is initialized with I0′ = I0 .

... a′0
... ...
a′0 = a0 b0 b0
0 ...

• Update step: Starting from k = 1, we would like to incorporate Ik = [ak , bk ]


into the output array. By noting [a′i , bj ] the last added interval of I ′ , we have
the following cases:

– Case bk < bj : Ik does not add any new information since it is entirely
overlapping with the last added interval. We discard Ik .
... a′i
... ... ... ... ...
a′i ak bk bj bj
... i ...

– Case ak ⩽ bj ⩽ bk : Ik is partially overlapping with the last added


interval, so we update the current upper bound to bk .
a′i
... ... ... ... ...
a′i ak bj bk bk
... i ...

– Case bj < ak : There is no overlap between Ik and the last added interval,
so we add Ik to the final array I ′ at index i + 1.
a′i a′i+1
... ... ... ... ...
a′i b′i = bj a′i+1 = ak bk b′i bk
... i i+1 ...

We repeat the update step for all intervals Ik in a sequential way to obtain the
output array of non-overlapping intervals I ′ .

2.1.2 Strings
❒ Definition – A string s is a data structure that can be seen as an array of
characters s0 , ..., sn−1 .

s0 s1 ... sn−1
0 1 ... n−1

29
Super Study Guide Data Structures

The following table summarizes a few categories of strings that have special prop-
erties:

Type Description Example


Palindrome Reads the same backward and forward. kayak, madam, radar
Forms another string using a
Anagram listen −→ silent
rearrangement of its letters.
Remains the same when rotated by
Strobogrammatic 69, 818, 1001
180 degrees.

❒ Longest substring – Given a string s0 ...sn−1 , the goal of this problem is to find
the length of the longest substring that does not contain repeated characters.

s0 s1 ... si1 ... si2 ... sn−1


0 1 ... i1 ... i2 ... n−1

i2 − i1 + 1 max s.t ∀i ∈ [[i1, i2]], si unique

A naive approach would check the constraint on every possible substring using a
nested for-loop in O(n2 ) time. However, there is a more efficient approach that
uses the sliding window trick.

Trick Use a left pointer l and a right pointer r to delimit a window which has the
constraint to not have repeated characters. We have the following rules:

• Constraint is not violated: The current solution may have not reached its full
potential yet. The r pointer advances until the constraint is violated.

l r l r r

• Constraint is violated: The current solution is not valid anymore, so we need


to trim the solution down. The l pointer advances until the constraint is no
longer violated.

l r l l r

30
Super Study Guide Data Structures

Algorithm The solution is found in O(n) time and O(n) space:

• Initialization:

– Initialize an empty hash set that aims at storing the characters that are
part of the current substring.
– Set a global counter c to 0, which keeps track of the maximum substring
length encountered so far.
– Set the left pointer l and right pointer r to 0.

c u t e b e a r
lr h

• Compute step: sr is the new character proposed to be added to the current


substring.

?
c u t e b e a r c u t e b
l r h

– If sr is already in the hash set, then adding it again would make us have
sr twice in the substring.
c u t e b e a r c u t e b
l r h

Increment l and remove sl from the hash set until sr is not there anymore.
The substring gets trimmed down.
c u t e b e a r c u t e b
l l r h

– Now, the updated substring is ready to have sr without violating the


constraint. Add sr to the hash set.
c u t e b e a r e b
l r h
– Check whether the length r − l + 1 of the resulting substring is greater
than the counter c. If it is, store the new maximum in c.
>c?
c u t e b e a r e b
l r h

31
Super Study Guide Data Structures

– Increment r by 1.
c u t e b e a r e b
l r r h

Repeat this process until r reaches the end of the string.

• Final step: c is the length of the biggest substring without repeated characters.
Remark: The sliding window trick is found in many string problems.

2.2 Stacks and queues


In this part, we will look at stacks and queues, along with their respective operations.

2.2.1 Stacks
❒ Definition – A stack s is a data structure that deals with elements s1 , ..., sn in
a Last In First Out (LIFO) order.

sn
...

s2
s1

In order to do that, it uses the push and pop operations:

Push Pop
Insert an element on the top of Remove the element from the top
the stack. of the stack and return its value.
s4 s4
s3 s3
s2 s2
s1 s1

As a result, the last element added to the data structure will be the first one
removed.
Remark: Stacks are commonly used for Depth-First Search (DFS) graph traversals.
❒ Operations – The main operations that can be performed on a stack are ex-
plained below:

32
Super Study Guide Data Structures

Type Time Description Illustration


Top of the stack
O(1)
Read the top element. s3
Access Any other position s2
O(n) Depending on the desired position, the stack s1
needs to be popped at most n times.

s3
Depending on whether the desired value gets
Search O(n) found right away, the stack needs to be popped s2
at most n times. s1

Top of the stack


O(1)
Push the new element.
s4
Any other position
1. Pop elements until accessing the desired s3
Insertion
position and push them into an auxiliary stack. s2
O(n)
2. Push the new element. s1
3. Pop elements from the auxiliary stack and
push them into the initial stack.
Top of the stack
O(1)
Pop the existing element.
s4
Any other position
1. Pop elements until accessing the desired s3
Deletion
position and push them into an auxiliary stack. s2
O(n)
2. Pop the existing element. s1
3. Pop elements from the auxiliary stack and
push them into the initial stack.

❒ Daily temperatures – Suppose we are given an array T = [t0 , ..., tn−1 ] of


temperatures. For each day i, the goal is to find the number of days di until there
is a warmer temperature.

ti
i
di

In other words, for each day i ∈ [[0, n − 1]], we want to find the number of days di

33
Super Study Guide Data Structures

such that:

di = min (j − i)
j>i,tj >ti

A naive approach would compute the answer for each day in isolation. By scanning
up to O(n − i) elements for each day i, this approach leads to an overall time
complexity of O(n2 ).

However, a more efficient approach uses stacks and computes the solution in O(n)
time and O(n) space:

• Initialization:
– Initialize an empty stack s.
– Initialize the output array D = [d0 , ..., dn−1 ] with zeros.

0 0 ... 0
0 1 ... n−1 s

• Compute step: We use the monotonic stack trick:


– The stack s tracks days k and their associated temperatures tk .
– Elements added to the stack are days for which we want to find a greater
temperature. They are popped as soon as a future warmer day is found.
– Temperature values are popped in a monotonically non-decreasing man-
ner. This ensures that all relevant values are popped for a given warmer
day.

ti
kj ... k1 i

We start from day 0. For each day i, we have the following cases:
– If the temperature tk at the top of the stack is lower than the current
temperature ti , pop (k, tk ) from the stack, and write the corresponding
day difference in the final array:

dk = i − k

34
Super Study Guide Data Structures

Repeat the popping procedure until the condition is not satisfied any-
more.
k1 tk1

...
kj tkj
j ... 1 0 ... 0

...
kj ... k1 i ... n−1 s

– Add the current item (i, ti ) to the stack s.


i ti

... s

Repeat this process until reaching the end of the array T .

The array D contains the final answer. Entries with zeros correspond to days that
do not have future warmer days.

2.2.2 Queues
❒ Definition – A queue q is a data structure that deals with elements q1 , ..., qn
in a First In First Out (FIFO) order, where its tail has the most recently arrived
elements and its head has elements that arrived the earliest.

q1 q2 ... qn
head tail

In order to do that, it uses the enqueue and dequeue operations:

Enqueue Dequeue
Insert element at the tail of Remove element from the head
the queue. of the queue.
q1 q2 q3 q4 q1 q2 q3 q4

As a result, the first element added to the data structure will be the first one to be
removed.
Remark: Queues are commonly used for Breadth-First Search (BFS) graph traver-
sals.
❒ Operations – The main operations that can be performed on a queue are ex-
plained below:

35
Super Study Guide Data Structures

Type Time Description Illustration


Head of the queue
O(1)
Read the front element.
Access Any other position q1 q2 q3
O(n) Depending on the desired position, the queue
needs be dequeued at most n times.
Depending on whether the desired value gets
Search O(n) found right away, the queue needs to be q1 q2 q3
dequeued at most n times.
Tail of the queue
O(1)
Enqueue the new element.
Any other position
1. Initialize a new empty queue.
2. Dequeue elements from the current queue
Insertion and enqueue them to the new queue up until q1 q2 q3 q4
O(n) right before the desired position of insertion.
3. Enqueue the new element in the new queue.
4. Dequeue the remaining elements from the
current queue and enqueue them to the new
queue.
Head of the queue
O(1)
Dequeue the existing element.
Any other position
1. Initialize a new empty queue.
2. Dequeue elements from the current queue
Deletion and enqueue them to the new queue up until q1 q2 q3 q4
right before the desired position of deletion.
O(n)
3. Dequeue the element to be removed from
the current queue and ignore it.
4. Dequeue the remaining elements from the
current queue and enqueue them to the new
queue.

2.3 Hash tables

In this part, we will go through the basics of hash tables along with methods to
resolve collisions.

36
Super Study Guide Data Structures

2.3.1 General concepts


❒ Hash set – A hash set is a data structure that stores a collection of unique values
{s1 , ..., sn } with fast search times.

s1 s2 ... sn

A hash function f is used to link an element s to the address f (s) of the bucket
that contains it. f (s) is called hash value of s.

f
1
s

f (s) ...
s
...

❒ Hash table – A hash table, also called hash map, is a data structure used to
index large amounts of data with fast key search times. It consists of an unordered
collection of key-value pairs {(k1 , v1 ), ..., (kn , vn )}.

k1 → v1 k2 → v2 ... kn → vn

A hash function f is used to link a key k to the address f (k) of the bucket containing
the associated value v. f (k) is called hash value of k.

f
1
k
...

f (k)
k v
...

An ideal hash function fulfills the following properties:

• Be easy to compute, to minimize runtimes

• Have a resolution method if two distinct keys have the same hash value, i.e.
form a hash collision

37
Super Study Guide Data Structures

• Have a uniform distribution of hash values to ensure a minimal amount of


collisions

Remark: Basic hash functions include summing the ASCII representation of char-
acters, whereas more sophisticated ones use advanced algebraic properties.

❒ Operations – The following operations can be performed on a hash table with


a reasonably good hash function:

Type Time Description Illustration


k1 f (k2) k2 v2
Using the key k, compute the
k2 f (k1)
Access O(1) associated bucket f (k) and access k1 v1
the corresponding value v.

For each key k:


k1 f (k2) k2 v2
1. Compute the associated hash
k2 f (k1)
Search O(n) value f (k). k1 v1
2. Check if the value v is in the
bucket associated with f (k).

k1 f (k2) k2 v2
Given the key k, compute the
k2 f (k1)
Insertion O(1) corresponding bucket f (k) and add k1 v1
k3 f (k3)
the value v there. k3 v3

k1 f (k2) k2 v2
Given the key k, compute the
k2 f (k1)
Deletion O(1) corresponding bucket f (k) and k1 v1
k3 f (k3)
remove the value v from there. k3 v3

Remark: It is crucial to have a good hash function to ensure the O(1) runtimes.

2.3.2 Collisions
❒ Definition – A hash collision happens when two different keys have the same
hash value. When this is the case, we need a way to resolve it.
1
k1
...

f (k1)
f (k2)
k2
...

38
Super Study Guide Data Structures

❒ Load factor – The load factor ρ of a hash table is defined based on the total
number of items stored n = #{(ki , vi )} and the number of buckets b as follows:

n
ρ≜
b
Remark: If there are more items than there are buckets, then we have ρ > 1. In
this situation, the pigeonhole principle guarantees the presence of hash collisions.
❒ Collision resolution – The most common resolution methods can be put into
two categories:

• Open addressing: We try to find an open spot by looking at other buckets.


This works best when ρ is close to 0.

Method Description Illustration

Linear f (k) k0 v0
probing Tries to put the value in the next k0
p(1)

...
p(x) = x
bucket until there is no more collision. k
? ?
p(2)

...
Quadratic Tries to put the value in buckets that
are further and further away in a ? ?
probing ... ...
quadratic fashion, until there is no
p(x) = x2 more collision.

f1 f2
f1(k) k0 v0
Double
Uses a secondary hash function if the
k0
hashing
...

first one leads to a collision. k k


f1 , f 2 ? ?
f2(k)
...

• Closed addressing: We append the value to the existing bucket. This works
best when when ρ is close to 1.

Method Description Illustration


f (k) k0 v0
Uses linked lists to sequentially
k0
next k v
Chaining add values assigned to the same
bucket. k

The risk of collisions highlight the importance of choosing a hash function that leads
to a uniform distribution of hash values.

39
Super Study Guide Data Structures

2.4 Advanced hash tables


In this part, we will focus on space-efficient data structures such as bloom filters
and count-min sketches, which use tricks based on hash functions.

2.4.1 Bloom filters


❒ Definition – A bloom filter B is a space-efficient data structure that can check
whether a given element x is:
• either potentially in the set

x ✓ ⟹ x ∈? B

• or definitely not in the set

x × ⟹ x∉B

It is a data structure composed of:

• k independent hash functions f0 , f1 , ..., fk−1 with uniform distributions over


integer values between 0 and m − 1

• an array B of size m of elements b0 , b1 , ..., bm−1 initially all equal to 0

f0
f1
b0 b1 b2 ... bm−1
...
fk−1

Element bj of the array is set to 1 when an element x is inserted and verifies fi (x) = j
for a given i ∈ [[0, k − 1]], with j ∈ [[0, m − 1]].

The false positive rate ϵ quantifies how unreliable a positive prediction given by the
bloom filter is:

# elements falsely predicted to be in the set


ϵ=
# elements predicted to be in the set
It is approximated using the number of bits m, the number of hash functions k and
the number of elements n that we want to insert as follows:

40
Super Study Guide Data Structures

 kn
k
ϵ ≈ 1 − e− m

We want ϵ to be as small as possible. Given m and n, the optimal value of the


number of hash functions k is such that:

m
k= loge (2)
n

Application Suppose we would like to check whether an element belongs to a set


of data stored on a disk that is expensive to call.
• A naive approach would consist of calling the disk every time we want to check
whether an element is there. This process would be very slow.
• Now, let’s assume we have a bloom filter that checks whether the element is
in the set (fast operation) and only lets us call the disk if it is predicted to
be there. We would be cutting the number of unnecessary call requests, thus
making the process faster.

x ✓ ⟹ x
x
x × ⟹ ×

❒ Operations – An initialized bloom filter has all the bits of its array B set to 0.

Insertion In order to insert an element x to the bloom filter, we follow the steps
below:

• Compute step: Compute hash values f0 (x), ..., fk−1 (x).


• Update step: Update the m-bit array B by turning on the corresponding bits:

for i ∈ [[0, k − 1]], bfi (x) ←− 1

f0
x f1
1 1 ... 1
...
fk−1

41
Super Study Guide Data Structures

Deletion Deleting an element from the bloom filter is not possible, since removing
associated bits may inadvertently remove other elements.

Search In order to check whether an element x is potentially in the bloom filter,


we follow the steps below:

• Compute step: Compute hash values f0 (x), ..., fk−1 (x).

• Check step: We have two possible outcomes:

– All corresponding bits are on: This means that the element x is possibly
in the set. It can also not be, in which case the prediction is a false
positive.
f0
f1
x 1 1 ... 1 ⟹ x ∈? B
...
fk−1

– One of the bits is off : This means that the element is definitely not in
the set, and we are sure of it.
f0
f1
x 1 0 ... 1 ⟹ x∉B
...
fk−1

2.4.2 Count-min sketches


❒ Definition – A count-min sketch C is a space-efficient data structure that pro-
c for the number of times #x a given element x appeared.
vides an upper bound #x

x #x ̂ ≳ #x

It is a data structure composed of:

• k independent hash functions f0 , ..., fk−1 with uniform distributions over val-
ues between 0 and m − 1

• a 2D array C with k × m elements ci,j initially all equal to 0, with:

42
Super Study Guide Data Structures

– k rows, corresponding to the output of each of the k hash functions


– m columns, corresponding to the m different values that each hash func-
tion can take

f0 c0,0 c0,1 c0,2 ... c0,m−1


f1 c1,0 c1,1 c1,2 ... c1,m−1
... ...
fk−1 ck−1,0 ck−1,1 ck−1,2 ... ck−1,m−1

Each element ci,j is an integer that corresponds to the number of times an element
x was inserted and verified fi (x) = j for a given i ∈ [[0, k − 1]], with j ∈ [[0, m − 1]].

k and m are hyperparameters that are chosen when initializing the data structure.
The bigger they are, the more accurate the approximation will be, but also the more
space the data structure will take and the more time each operation will take.

Application Suppose we would like to check how many times an element ap-
peared in a large stream of data.

• A naive approach would consist of keeping track of the stream of data and
counting the number of times each element appeared in a large entry table.
The space taken by this approach is as big as the number of distinct elements
seen. This could be very big.

• Now let’s assume we add a count-min sketch that approximately counts each
element. We would keep a fixed-size data structure that could handle large
streams of data and provide reasonable approximations.

N N

x ... #x x #x ̂ ≳ #x

N≫1 k×m

❒ Operations – An initialized count-min sketch has its 2D array C filled with


zeros.

43
Super Study Guide Data Structures

Insertion In order to insert an element x to the count-min sketch, we follow the


steps below:

• Compute step: Compute hash values f0 (x), ..., fk−1 (x).

• Update step: Update the 2D array C by incrementing the corresponding


counts:
for i ∈ [[0, k − 1]], ci,fi (x) ←− ci,fi (x) + 1

f0 ... +1
x f1 +1 ...
... ...
fk−1 +1 ...

Deletion Deleting an element from the count-min sketch is not possible, since
removing associated counts may inadvertently remove other elements.

Search In order to compute an estimate of the number of times a given element


x was previously inserted into the count-min sketch, we follow the steps below:

• Compute step: Compute hash values f0 (x), ..., fk−1 (x).

• Check step: Take the minimum across all the corresponding counts:


c = min c0,f (x) , ..., ck−1,f (x)
#x 0 k−1

This estimate is an upper bound because it might have been inflated by hash colli-
sions.

f0 ... c0,m−1
f1 c1,2 ...
x
... ...
fk−1 ck−1,0 ...

2.5 Linked lists


In this part, we will learn about singly and doubly linked lists, along with tricks to
identify cycles and related applications.

44
Super Study Guide Data Structures

2.5.1 Singly linked lists


❒ Definition – A singly linked list is a data structure composed of nodes, where
each node carries the information of:
• a value v
• a next field, that points to the next node

v next

The first node of a singly linked list is called the head. The overall data structure
is represented as follows:

v1 next
v2 next ... next
vn next
NULL
head

❒ Operations – The main operations that can be performed on a singly linked list
are explained below:

Type Time Description Illustration


Starting from the head, the linked
list is traversed in a sequential way
Access O(n) to access the desired node. If it is v1 v2 NULL
next next
the last element, the whole linked
list is traversed.
Starting from the head, the linked
list is traversed in a sequential way
Search O(n) to search for the desired node. If v1 v2 NULL
next next
the node is not found, the whole
linked list is traversed.
1. Assign the next pointer of the
previous-to-be element to the v1 v2 NULL
next next
Insertion O(1) newly-inserted node.
2. Assign the next pointer of the new v1′
next next
element to the following element.
Assign the next pointer of the v1 v2 NULL
Deletion O(1) previous node to the following node next next

of the soon-to-be-removed node. next

❒ Floyd’s algorithm – Floyd’s algorithm, also known as the tortoise and hare
algorithm, is able to detect the starting point of a cycle in a linked list.

45
Super Study Guide Data Structures

v6
v5 v7
pT
v1 v2 v3 v4 D v8
pH
v11 v9
v10
a

It finds the start of the cycle using the two-pointer method in 3 steps, with a time
complexity of O(n) and a space complexity of O(1):

• Meeting step: We define two pointers:

– A slow pointer (tortoise) pT that goes 1 step at a time.


– A fast pointer (hare) pH that goes 2 steps at a time.

The tortoise and the hare both start from the head of the linked list and
respectively travel distances dT and dH until they meet. At the meeting
point, these quantities verify dH = 2dT .

We note ∆1 the difference between the distances traveled by the hare and the
tortoise:
∆1 ≜ dH − dT = dT

v6
v5 d v7
...
v1 v2 v3 v4 v8
... pT
v11 v9
v10 pH

Given that the two animals necessarily meet somewhere in the cycle, we can
also write: 
dT = a + d + n1 D
where n2 > n1
dH = a + d + n2 D
This means that we can rewrite ∆1 as:

∆1 ≜ dH − dT = (n2 − n1 )D

46
Super Study Guide Data Structures

Hence, we have:
dT = (n2 − n1 )D

• Restart step: Now, we keep the hare at the meeting point and place the
tortoise back to the head of the linked list.
v6
v5 v7
pT
v1 v2 v3 v4 v8

v11 v9
v10 pH

Since the tortoise has moved back by a distance dT , the new distance ∆2
between them is such that:

∆2 ≜ ∆1 + dT = 2dT = 2(n2 − n1 )D

Hence, we have:
∆2 = kD with k ∈ N∗

• Detection step: We make each animal move 1 step at a time, thus keeping the
distance ∆2 between them constant.

v6
v5 v7
pT
v1 v2 v3 v4 v8
pH
v11 v9
v10

Since ∆2 is a multiple of the length D of the cycle, the new meeting point
between the two animals will precisely be the start of the cycle.
Remark: This algorithm can be used to detect cycles, length of portions of linked
lists, among other use cases.
❒ Duplicate number – Given an array A = [a0 , ..., an ] of size n + 1 where each
element ai is in the range [[1, n]], suppose there exists exactly two elements ai1 , ai2

47
Super Study Guide Data Structures

such that ai1 = ai2 ≜ adup . All other elements are assumed to be distinct. The
goal of the problem is to find the duplicate value adup .
adup
a0 ... ai1 ... ai2 ... an
0 ... i1 ... i2 ... n

A naive approach would sort the array and scan the result to find the two consecutive
elements that are equal. This would take O(n log(n)) time.

However, a more efficient approach takes O(n) time and leverages Floyd’s algorithm.

Trick Suppose we represent the array by n + 1 distinct nodes that compose one
or more linked lists. Each element ai of the array A is seen as a node i that points
to node ai .

ai
... ... ⟺ i ai
i

We make the following observations:


• For i ∈ [[0, n]], ai covers all values between 1 and n. As a result:

– The n + 1 nodes point to exactly n of its nodes, which guarantees that


each resulting linked list has a cycle.
– No node points to node 0, which means that the cycle in the linked list
starting from node 0 is not circular. Linked lists that do not contain
node 0 (if any) are necessarily circular.

0 a0

• Both the nodes i1 and i2 point to node adup . As a result:

– The desired duplicate number is at the start of a cycle.

i1 adup i2

– The cycle containing the duplicate number is necessarily non-circular.

From the two points above, we deduce that the linked list starting from node 0
contains adup at the start of its cycle.

48
Super Study Guide Data Structures

Algorithm The duplicate number is found in O(n) time and O(1) space:

• Initialization: Think of array A in terms of linked list(s) using the trick above.
Note that no extra-space is needed. For a given element ai , the next element
can be accessed via aai .

0 2 3 4
2 4 3 5 3 1 ⟺
0 1 2 3 4 5
5 1

• Compute step: Apply Floyd’s algorithm from node 0, which takes O(n) time
and O(1) space.

0 2 3 4 0 2 3 4
...
5 1 5 1

• Final step: The start of the cycle is the duplicate number adup .

0 2 3 4
⟺ 2 4 3 5 3 1
0 1 2 3 4 5
5 1

2.5.2 Doubly linked lists


❒ Definition – A doubly linked list is a data structure composed of nodes, where
each node carries the information of:

• a value v

• a next pointer that points to the next node

• a prev pointer that points to the previous node


prev
v
next

The first and last nodes of a doubly linked list are called the head and tail respec-
tively. The overall data structure is represented as follows:

49
Super Study Guide Data Structures

prev prev prev prev


NULL v1 v2 ... vn NULL
next next next next
head tail

❒ Operations – The main operations that can be performed on a doubly linked


list are explained below:

Type Time Description Illustration


Starting from the head, the
linked list is traversed in a
sequential way to access the prev prev
Access O(n) NULL v1 v2 NULL
desired node. If it is the last next next
element, the whole linked list
is traversed.
Starting from the head, the
linked list is traversed in a
sequential way to search for prev prev
Search O(n) NULL v1 v2 NULL
the desired node. If the node next next
is not found, the whole linked
list is traversed.
1. Assign the next pointer of:
a. The previous-to-be element
to the newly-inserted node.
b. The new element to the prev prev
NULL v1 v2 next NULL
following element. next
Insertion O(1) prev prev
2. Assign the prev pointer of:
v1′
a. The new element to the next next
previous-to-be element.
b. The following element to
the new element.
1. Assign the next pointer of
the previous node to the prev prev
NULL v1 v3 next NULL
following node. next
Deletion O(1) prev prev
2. Assign the prev pointer of v2
the following node to the next next
previous node.

❒ LRU cache – A Least Recently Used (LRU) cache of capacity C is a bounded-


size structure that keeps track of the C most recently used items. An item is said
to be "used" if it is either accessed or inserted.

50
Super Study Guide Data Structures

e1 → e1
prev prev prev prev
NULL e1 e2 ... eC NULL ...
eC → eC
next next next next

It is a classic example of an efficient combination of doubly linked lists with hash


tables, where:

• The doubly linked list provides an ordering of elements with respect to their
last use, with the most recently used ones being closer to the head.

• The hash table maps values to nodes and enables access times to existing
nodes in O(1).

Access The retrieval operation is done in O(1) time as follows:

• Identification step: Identify the node using the hash table.

...
ei → ei
prev prev prev prev
NULL e1 ... ei ...
next next next
...

• Update step: Convey the fact that the node is now the most recently used
element.

– Connect the neighbors of the element between them.

prev
prev prev prev prev
NULL e1 ... ei ...
next next next
next

– Bring the element to the front of the doubly linked list.

prev prev prev


NULL e1 ... ...
prev prev next next
ei
next

51
Super Study Guide Data Structures

Insertion This operation is achieved in O(1) time and depends on the situation:

• Inserted element already in cache. When a cached element is inserted, that


element is taken to the front of the linked list in the same fashion as when the
element is accessed.

• Inserted element not in cache. When a new element e is inserted, we have the
following cases:

– If the cache is below capacity: Insert the new element at the front of the
doubly linked list and add it to the hash table.
prev prev prev
NULL e1 ... ek NULL ...
e → e
prev prev next next next
e
next

– If the cache is at capacity: Remove the least recently used element to


welcome the new node.
∗ Removal step: Remove from the hash table the entry corresponding
to the element that was the least used and update the tail of the
doubly linked list.
prev prev prev
NULL e1 ... eC NULL ...
eC → eC
next next next
next

∗ Insertion step: Insert the new element at the front of the doubly
linked list and add it to the hash table.
prev prev
NULL e1 ... NULL ...
e → e
prev prev next next
e
next

52
Super Study Guide Graphs and Trees

SECTION 3

Graphs and trees

In this third section, we will go through the basics of graphs along with useful
graph traversal algorithms. Then, we will focus on both standard trees along with
trees that have special properties, such as binary search trees and tries.

3.1 Graphs

In this part, we will study the main notions of graphs and graph traversal algorithms
such as BFS, DFS and topological sorting. We will learn about the main shortest
paths algorithms such as Dijkstra’s, A⋆ , Bellman-Ford, and Floyd-Warshall algo-
rithms.

3.1.1 General concepts

❒ Definition – A graph G is defined by its vertices V and edges E and is often


noted G = (V, E). The following table summarizes the two main types of graph:

Undirected graph Directed graph


Edges do not have a direction Edges have a direction
Eij Eij
Vi Vj Vi Vj

Remark: The terms "node" and "vertex" can be used interchangeably.

❒ Graph representation – Let’s consider the following graph:

VA VB

VD VC

It can be represented in two different ways:

53
Super Study Guide Graphs and Trees

Type Description Illustration

Adjacency
Collection of unordered lists where each VA → {VB, VD} VB → Ø
VC → {VA} VD → Ø
entry Vi maps to all the nodes Vj such
list
that Eij exists in the graph.

Matrix of boolean values where the VA VB VC VD


entry (i, j) indicates whether Eij is VA 0 1 0 1
Adjacency VB
present in the graph. 0 0 0 0
matrix VC
In an undirected graph, this matrix is 1 0 0 0
always symmetric. VD 0 0 0 0

❒ Degree – A node can have the following characteristics based on its adjacent
edges:

Graph Notation Definition Illustration


Number of connected
Undirected Degree
edges

Number of connected
In-Degree
inbound edges
Directed
Number of connected
Out-Degree
outbound edges

Remark: Nodes of even (resp. odd) degree are called even (resp. odd) nodes.

❒ Handshaking lemma – In an undirected graph, the sum of node degrees is an


even number.

Indeed, every edge links two nodes, therefore responsible for increasing the sum of
degrees by 2 (one per node). We have the following formula:
X
deg(v) = 2|E|
v∈V

54
Super Study Guide Graphs and Trees

Remark: A consequence of the above formula is that there is an even number of odd
nodes.

❒ Cyclicity – The cyclicity of a graph is a property that depends on whether the


graph has a cycle:

Acyclic Cyclic
Does not contain a cycle Contains at least a cycle

Remark: A directed acyclic graph is commonly abbreviated as DAG.

❒ Properties – A graph can have any of the following properties:

Type Description Illustration

Every pair of vertices is


Complete
connected by an edge.

There exists a path between


Connected
every pair of nodes.

Vertices can be split into two


disjoint sets such that every
Bipartite
edge of the graph links a vertex
from one set to one in the other.

3.1.2 Graph traversal

❒ Breadth-First Search – Breadth-First Search (BFS) is an algorithm that ex-


plores a graph level by level.

55
Super Study Guide Graphs and Trees

1 VA

2 VB 3 VC 4 VD

5 VE 6 VF 7 VG

The graph traversal is performed in O(|V | + |E|) time and O(|V |) space:

• Initialization: The following quantities are tracked:

– Queue q that keeps track of the next nodes to potentially visit. In the
beginning, the first node is the only element inside.
– Hash set hv that keeps track of visited nodes. It is initially empty.

VA

VB VC VD
VA
VE VF VG q hv

• Update step: Dequeue element from q. We have the following cases:

– Node already visited: Skip it.


– Node not visited yet: Add it to the set hv of visited nodes, and look at
its neighbors: if they were not visited before, enqueue them in q.

VA

VB VC VD VA
VB VC VD
VE VF VG q hv

• Final step: Repeat the update step until the queue gets empty. The set hv
represents the set of visited nodes.

56
Super Study Guide Graphs and Trees

VA

VB VC VD VA VB VC VD
VE VF VG
VE VF VG q hv

❒ Depth-First Search – Depth-First Search (DFS) is an algorithm that explores


a graph by prioritizing depth.

1 VA

6 VB 3 VC 2 VD

7 VE 5 VF 4 VG

The graph traversal is performed in O(|V | + |E|) time and O(|V |) space:

• Initialization: The following quantities are tracked:

– Stack s that keeps track of the next nodes to potentially visit. In the
beginning, the first node is the only element inside.
– Hash set hv that keeps track of visited nodes. It is initially empty.

VA

VB VC VD
VA
VE VF VG s hv

• Update step: Pop element from s. We have the following cases:

– Node already visited: Skip it.


– Node not visited yet: Add it to the set hv of visited nodes, and look at
its neighbors: if they were not visited before, push them to s.

57
Super Study Guide Graphs and Trees

VA
VD
VB VC VD VC VA
VB
VE VF VG s hv

• Final step: Repeat the update step until the stack gets empty. The set hv
represents the set of visited nodes.

VA

VB VC VD VA VD VC VG
VF VB VE
VE VF VG s hv

Remark: DFS can also be implemented recursively.

❒ Graph traversal summary – The table below highlights the main differences
between BFS and DFS:
Breadth-First Search (BFS) Depth-First Search (DFS)
Mindset Level by level Depth first
Possible - Iteration using a stack
Iteration using a queue
approaches - Recursion

1 1

Illustration 2 3 4 6 3 2

5 6 7 7 5 4

❒ Number of islands – A classic application of graph traversal algorithms is to


count the number of islands in an m × n grid, where each cell is either marked as
1 (land) or 0 (water).

Trick Perform a BFS starting from each unvisited land cell of the grid to explore
the associated island and skip cells that have already been visited.

58
Super Study Guide Graphs and Trees

Algorithm The entire grid is explored in O(m × n) time and O(m × n) space as
follows:

• Initialization: Set the counter c that tracks the number of islands to 0.

• Explore step: For each cell of the grid, we have the following situations:

– Water not visited: Skip this cell as it is not part of any island.
X X X X X 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 1 1 0 0 1 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0
0 0 0 0 0 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0
0 1 0 0 0 0 1 1 1 1 1 1 1 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 1 1 1 1 1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 1 0 0

– Land not visited: Perform a BFS that starts from that cell and visits all
neighboring land cells. This process uses a temporary queue q to track
nodes to visit, and marks visited nodes by changing their values to X.
X X X X X X X X X X X X 0 0 0 0 0 0 0 0
0 0 0 0 0 X X X X X X X X 0 0 X 0 0 0 0
0 0 0 0 0 X X X X X X X X X X 0 0 0 0 0
0 0 0 0 0 X X X X X X X X X X 0 0 0 0 0
0 0 0 0 0 X X X X X X X X X 0 0 0 0 0 0
0 1 0 0 0 0 X X X X X X X X 0 0 0 0 0 0
0 0 1 0 0 0 0 0 X X X X X 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 X 0 0 1 1 0 0

At the end of the exploration, add 1 to the number of islands c.


– Water/Land already visited: Skip this cell as it was already explored.

• Final step: The number of islands in the grid is equal to c.

X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X
X X X X X X X X X X X X X X X X X X X X

59
Super Study Guide Graphs and Trees

We note that:
• Using an iterative graph traversal algorithm is helpful in preventing stack
overflow in case the exploration of an island leads to too many stack calls.

• It would have been equally possible to make island explorations using DFS.

❒ Robot room cleaner – The goal of this classic problem is to clean a room
composed of n cells using a robot cleaner that can only perform the 3 following
actions:

Turn right Move cell Clean cell


Move forward by one cell in the
Rotate 90◦ to the right while
current direction, provided that Clean current cell
staying in the same cell.
there is no obstacle.

The configuration of the room is not known by the robot but is assumed to be of
finite size. It has obstacles along the way that the robot needs to avoid. We assume
that the robot initially starts in a cell with no obstacle.

Trick
• Perform a DFS using an exploration strategy that consists of trying all possible
directions and that backtracks if an obstacle is found.
?

• Rotating to the right can be done by keeping track of the direction (dx , dy )
that the robot is pointing towards.

60
Super Study Guide Graphs and Trees

y⃗
x⃗
dx x ⃗ + dy y ⃗ dy x ⃗ − dx y ⃗

Algorithm The robot cleans the room in O(n) time and O(n) space as follows:

• Initialization: We take the convention that:

– The initial cell of the robot is (x, y) = (0, 0).


– The robot is initially facing up: (dx , dy ) = (0, 1).

A hash set is used to keep track of the coordinates of the cells visited by the
robot.

• Explore step: This is the main step where the robot cleans the cell and moves
to an unvisited cell.

The robot performs the following actions:

– It cleans the cell.

– It tries to visit each of the 4 neighboring cells by right-rotating and trying


to move there. If the neighboring cell is an obstacle or has already been
visited, the robot skips it.

– When it does not have any more new cells to visit, the robot backtracks
by turning right twice, moving and turning right twice again to be point-
ing back to the initial direction.

61
Super Study Guide Graphs and Trees

• Final step: When the robot cleaned all cells, it backtracks to its original cell.

❒ Topological sort – In a given directed graph, a topological sort (topsort) is


a linear ordering of the nodes such that for all directed edges Eij = (Vi , Vj ), Vi
appears before Vj .

VA VB VC VA VB VD
VB
topsort
⟺ VC VA or
VD VC VA VD VB
VD VC

We note that:

• A graph can have multiple topological orderings.

• A DAG always has a topological sort.

• If a graph contains a cycle, then it cannot have a valid ordering.

❒ Kahn’s algorithm – Kahn’s algorithm aims at finding a topological ordering of


a given DAG. It finds a solution in O(|V | + |E|) time and O(|V |) space:

• Initialization: Initialize an empty array that keeps track of the final ordering
of vertices.

• Compute step: Repeat the following procedure until all nodes are visited:

– Pick a random node of in-degree 0 and visit it.

62
Super Study Guide Graphs and Trees

VA VB
VC
VD VC

– Decrement by 1 the in-degrees of the nodes that the newly-visited node


is connected to.

VA VB
VC
VD VC

• Final step: When all nodes are visited, the array contains a valid ordering of
vertices.

VA VB
VC VA VB VD
VD VC

3.1.3 Shortest path

❒ Weighted graph – A weighted graph is a graph where each edge Eij has an
associated weight wij .
Remark: When the weight is interpreted as a distance, it is often noted di,j .

❒ Dijkstra’s algorithm – Dijkstra’s algorithm is a greedy algorithm that aims at


finding the shortest paths between a source node VS and all other nodes j ∈ V of
a weighted graph that has no negative edge weights.

The algorithm finds a solution in O(|E| log(|V |)) time and O(|V |) space:

• Initialization:

– Set the distances dVS , j from VS to each of the other nodes j ∈ V to +∞


to indicate that they are initially unreachable.
– Set the distance dVS , VS from VS to itself to 0.
– Initialize a hash table hp that keeps track of the chosen predecessor for
each node. By convention, the predecessor of VS is itself.

63
Super Study Guide Graphs and Trees

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 +∞
5 1 5 2
3
2 VS → VS VA → ? VB → ?
VA VC VE hp
VC → ? VD → ? VE → ?

• Compute step: Repeat the following procedure until all nodes are visited:

– Pick the unvisited node i with the lowest distance dVS , i and visit it.

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 +∞
5 1 5 2
3
2 VS → VS VA → ? VB → ?
VA VC VE hp
VC → ? VD → ? VE → ?

– Look at the unvisited nodes j that have an edge coming from the newly-
visited node i.
∗ Case dVS , j > dVS , i + di,j : This means that the distance of the path
from node VS to j via node i is smaller than the current distance.
We update the corresponding distance:

dVS , j ←− dVS , i + di,j

Also, we update hp to indicate that the predecessor of j is now i.


∗ Case dVS , j ⩽ dVS , i + di,j : This means that the proposed path does
not improve the current distance. We do not make any updates.

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 5 2 +∞
5 1 5 2
3
2 VS → VS VA → VS VB → VS
VA VC VE hp
VC → ? VD → ? VE → ?

• Final step:

– The final distances are those of the shortest paths between VS and each
of the other nodes j ∈ V of the graph.
– The associated shortest paths can be reconstructed using hp .

64
Super Study Guide Graphs and Trees

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 5 2 5 3 7
5 1 5 2
3
2 VS → VS VA → VS VB → VS
VA VC VE hp
VC → VD VD → VB VE → VC

Remark: Given that choices are based on local optima, Dijkstra’s algorithm does not
guarantee a correct solution when there are negative edge weights. Indeed, if such
an edge were to be found "too late" in the exploration process, the algorithm may
visit a node earlier than desired and produce a suboptimal path.

❒ A⋆ algorithm – The A⋆ algorithm is an extension of Dijkstra’s algorithm that


is used when the goal is to find the shortest path between a source node VS and a
fixed target node VT .

To better direct the search towards the target node, it modifies the distance used
to select the next node by using a heuristic hj, VT that approximates the remaining
distance between unvisited nodes j and the target node VT .

dVS,j hj,VT
VS j VT


The distance used to determine the next node j to visit is given by d A :

dVAS , j = dVS , j + hj, VT

Remark: The A⋆ algorithm heavily relies on a good choice of heuristic h.


❒ Bellman-Ford algorithm – The Bellman-Ford algorithm aims at finding the
shortest paths between a source node VS and all other nodes j ∈ V of a weighted
graph.

A solution is found in O(|V | × |E|) time and O(|V |) space:

• Initialization:

– Set the distances dVS , j from VS to each of the other nodes j ∈ V to +∞


to indicate that they are initially unreachable.
– Set the distance dVS , VS from VS to itself to 0.
– Initialize a hash table hp that keeps track of the chosen predecessor for
each node. By convention, the predecessor of VS is itself.

65
Super Study Guide Graphs and Trees

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 +∞
5 1 5 2
3
2 VS → VS VA → ? VB → ?
VA VC VE hp
VC → ? VD → ? VE → ?

• Compute step: Repeat the following procedure until there is no more updates,
which is at most |V | − 1 times:

For each node i of the graph, look at its neighbors j and update the distance
dVS , j between the source node VS and the node j depending on the following
situations:
– Case dVS , j > dVS , i + di,j : This means that the distance of the path from
node VS to j via node i is smaller than the current distance. We update
the corresponding distance:

dVS , j ←− dVS , i + di,j

Also, we update hp to indicate that the predecessor of j is now i.


– Case dVS , j ⩽ dVS , i + di,j : This means that the proposed path does not
improve the current distance. We do not make any updates.

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 5 2 7 3 9
5 1 5 2
3
2 VS → VS VA → VS VB → VS
VA VC VE hp
VC → VB VD → VB VE → VC

• Final step:
– The final distances are those of the shortest paths between VS and each
of the other nodes j ∈ V of the graph.
– The associated shortest paths can be reconstructed using hp .

di,j VS VA VB VC VD VE
VS 2 VB 1 VD
VS 0 5 2 5 3 7
5 1 5 2
3
2 VS → VS VA → VS VB → VS
VA VC VE hp
VC → VD VD → VB VE → VC

66
Super Study Guide Graphs and Trees

Compared to Dijkstra’s algorithm, Bellman-Ford’s algorithm has the advantage


of supporting negative edge weights. However, it cannot run on a graph having
negative cycles, i.e. cycles with a negative sum of edge weights.

❒ Floyd-Warshall algorithm – The Floyd-Warshall algorithm aims at finding


the shortest paths between all pairs of nodes of a weighted graph.

A solution is found in O(|V |3 ) time and O(|V |2 ) space:

• Initialization: Initialize a matrix of distances of size |V | × |V |, where each cell


(i, j) represents the distance of the shortest path from node i to node j. We
know that:
– The distance of a node to itself is 0, so each element of the diagonal is
equal to 0.
– The distances of nodes linked by edges are given.

di,j VS VA VB VC VD VE
VS 0 5 2 +∞
2 1
VS VB VD VA +∞ 0 +∞
5 1 5 2
3
VB +∞ 0 5 1 +∞
VA VC 2 VE VC 1 +∞ 0 +∞ 2
VD +∞ 2 0 +∞
VE +∞ 3 0

• Update step: For each intermediary node k ∈ V , loop through start nodes
i ∈ V and end nodes j ∈ V :
– Case di,j > di,k + dk,j : This means that the distance from node i to j
via node k is smaller than the current distance. We make the following
update:
di,j ←− di,k + dk,j

di,j VS VA VB VC VD VE
VS 0 5 2 +∞
2 1
VS VB VD VA +∞ 0 +∞
5 1 5 2
3
VB +∞ 0 5 1 +∞
VA VC 2 VE VC 1 +∞ 0 +∞ 2
VD 3 +∞ 2 0 +∞
VE +∞ 3 0

67
Super Study Guide Graphs and Trees

– Case di,j ⩽ di,k + dk,j : This means that the proposed path does not
improve the current distance. We do not make any updates.

di,j VS VA VB VC VD VE
VS 0 5 2 +∞
2 1
VS VB VD VA +∞ 0 +∞
5 1 5 2
3
VB +∞ 0 5 1 +∞
VA VC 2 VE VC 1 +∞ +∞ 0 +∞ 2
VD 3 +∞ +∞ 2 0 +∞
VE +∞ 3 0

• Final step: The resulting matrix gives the shortest path between each pair of
nodes i and j.

di,j VS VA VB VC VD VE
VS 0 5 2 5 3 7
2 1
VS VB VD VA +∞ 0 +∞
5 1 5 2 VB 4 9 0 3 1 5
3
VA VC 2 VE VC 1 6 3 0 4 2
VD 3 8 5 2 0 4
VE 6 11 8 5 3 0

This algorithm allows for negatively weighted edges but it does not allow for negative
cycles like the other shortest path algorithms.
Remark: In the same fashion as in Dijkstra’s and Bellman-Ford’s algorithms, we
can reconstruct the resulting shortest paths by keeping track of nodes during updates.

❒ Shortest path summary – The differences between the main shortest paths
algorithms are summarized below:

Shortest path Negative Complexity


Algorithm
between weights Time Space
Dijkstra VS and all other nodes No O(|E| log(|V |)) O(|V |)

A VS and VT No O(|E| log(|V |)) O(|V |)
Bellman-Ford VS and all other nodes Yes O(|V | × |E|)) O(|V |)

Floyd-Warshall All pairs of nodes Yes O(|V |3 ) O(|V |2 )

68
Super Study Guide Graphs and Trees

3.2 Advanced graph algorithms


In this part, we will learn about more advanced graph concepts such as minimum
spanning trees and connected components.

3.2.1 Spanning trees


❒ Definition – A spanning tree of an undirected graph G = (V, E) is defined as a
subgraph that has the minimum number of edges E ′ ⊆ E required for all vertices
V to be connected.

Remark: In general, a connected graph can have more than one spanning tree.
❒ Cayley’s formula – A complete undirected graph with N vertices has N N −2
different spanning trees.

For instance, when N = 3, there are 33−2 = 3 different spanning trees:

❒ Minimum spanning tree – A minimum spanning tree (MST) of a weighted


connected undirected graph is a spanning tree that minimizes the total edge weight.

2 1

5 1 5 2
3
2

For example, the MST shown above has a total edge weight of 11.
Remark: A graph can have more than one MST.
❒ Prim’s algorithm – Prim’s algorithm aims at finding an MST of a weighted
connected undirected graph.

69
Super Study Guide Graphs and Trees

A solution is found in O(|E| log(|V |)) time and O(|V | + |E|) space with an imple-
mentation that uses a min-heap:

• Initialization: Pick an arbitrary node in the graph and visit it.

2 1

5 1 5 2
3
2

• Compute step: Repeat the following procedure until all nodes are visited:

– Pick an unvisited node that connects one of the visited notes with the
smallest edge weight.
– Visit it.

2 1

5 1 5 2
3
2

• Final step: The resulting MST is composed of the edges that were selected
by the algorithm.

2 1

5 1 5 2
3
2

❒ Kruskal’s algorithm – The goal of Kruskal’s algorithm is to find an MST of a


weighted connected undirected graph.

A solution is found in O(|E| log(|E|)) time and O(|V | + |E|) space:

• Compute step: Repeat the following procedure until all nodes are visited:

– Pick an edge that has the smallest weight such that it does not connect
two already-visited nodes.

70
Super Study Guide Graphs and Trees

2 1

5 1 5 2
3
2

– Visit the nodes at the extremities of the edge.


2 1

5 1 5 2
3
2

• Final step: The resulting MST is composed of the edges that were selected
by the algorithm.

2 1

5 1 5 2
3
2

3.2.2 Components
❒ Connected components – In a given undirected graph, a connected component
is a maximal connected subgraph.

❒ Strongly connected components – In a given directed graph, a strongly


connected component is a maximal subgraph for which a path exists between each
pair of vertices.

71
Super Study Guide Graphs and Trees

❒ Kosaraju’s algorithm – Kosaraju’s algorithm aims at finding the strongly con-


nected components of a directed graph.

The solution is found in O(|V | + |E|) time and O(|V |) space:

First-pass DFS On the original graph, perform a DFS.


• Initialization: The following quantities are used:

– An initially empty hash set hv that keeps track of the visited nodes.
– An initially empty stack s that keeps track of the order at which the
nodes have had their neighbors visited.

VC VD VH
VA
VB VE VF VG

s hv

• Compute step: Perform the following actions:

– Pick an arbitrary node and visit it by adding it to hv . Recursively visit


all its neighboring nodes.
VE VB
VC VD VH
VC VD
VA
VB VE VF VG

s hv

– Once a visited node has all its neighbors visited, push the visited node
into the stack s.
VE VB
VC VD VH
VC VD
VA
VB VE VF VG
VD
s hv

The recursive procedure adds the remaining nodes to the stack s.

72
Super Study Guide Graphs and Trees

VE VE VB
VC VD VH
VB VC VD
VA
VC
VB VE VF VG
VD
s hv

The same process is successively initiated on the remaining unvisited nodes of the
graph until all nodes are visited.

VG
VC VD VH VH
VE VB
VA VF
VC VD
VG VA
VB VE VF VA VG

...
VD VH VF
s hv

Second-pass DFS On the reverted graph, perform a DFS to determine the


strongly connected components.

• Initialization: The following quantities are used:

– The stack s obtained from the first-pass DFS.


– An initially empty hash set hv that keeps track of the visited nodes.

VG
VC VD VH VH
VA VF
VG VA
VB VE VF
...

VD
s hv

• Compute step: Pop a node from the stack.

– Node is not visited: This node is the representative of a new strongly


connected component. Add it to hv .

73
Super Study Guide Graphs and Trees

VG
VC VD VH VH
VG
VA VF
VG VA
VB VE VF

...
VD
s hv

Perform a DFS starting from that node. For the nodes that are being
explored:
∗ DFS node is not visited: Add it to hv and add the node to the hash
set identifying the strongly connected component.
∗ DFS node is visited: Skip it.
– Node is visited: Skip it.

Repeat this process again...

VC VD VH
VG VH
VA
VF VA
VG VA
VB VE VF
...

VD
s hv

• Final step: ...until the stack is empty.

VC VD VH
VG VH
VA
VF VA
VB VE VF VG VE VD
VC VB
s hv

3.3 Trees
In this part, we will learn about trees and focus on various types of binary trees,
including heaps and binary search trees. Then, we will focus on applications of
generalized types of trees such as tries.

74
Super Study Guide Graphs and Trees

3.3.1 General concepts


❒ Definition – A tree is a DAG with the following properties:

• Incoming edge:

– There is exactly one node that has no incoming edge, and that node is
called the root.
– Each of the other nodes has exactly one incoming edge.

• Outgoing edge: A node cannot have an outgoing edge that points to itself.

o o o

o o o

There are two types of nodes in a tree:

• Internal node: Node that has one or more children.

• Leaf : Node that does not have any children.

i i l

l l l

Here are some examples of graphs that are not trees, along with the associated
reason:

Self-loop Multiple parents Cycle Multiple roots

75
Super Study Guide Graphs and Trees

Remark: By construction, a tree with N nodes has N − 1 edges.

❒ Notations – Names given to nodes follow a family-like vocabulary. The most


common ones are presented in the table below:

Concept Description Illustration


The parent p of a node n is the p
Parent predecessor of that node.
n
n has at most one parent.

The grandparent g of a node n is the g


predecessor of the predecessor of that
Grandparent node.

n has at most one grandparent. n

A child c of a node n is a successor n


Child of that node.
c c
n can have 0, 1 or multiple children.

A sibling s of a node n is a different r


Sibling node with the same parent as n.

n can have 0, 1 or multiple siblings. s n

An uncle u of a node n is a child of n’s


Uncle grandparent that is not its parent. u
n can have 0, 1 or multiple uncles. n

❒ Depth – The depth of a given node n, noted depth(n), is the number of edges
from the root r of the tree to node n.

0 r
1

76
Super Study Guide Graphs and Trees

Remark: The root node has a depth of 0.

❒ Height – The height of a given node n, noted height(n), is the number of edges
of the longest path from node n to the deepest leaf.

3
2
1
0 l l l

l l

The height of a tree is the height of its root node.


Remark: Leaf nodes have a height of 0.

❒ Lowest Common Ancestor – In a given tree, the lowest common ancestor of


two nodes p and q, noted LCA(p, q), is defined as being the deepest node that has
both p and q as descendants. The examples below use s ≜ LCA(p, q).

s
s q

p q p

Remark: For the purposes of this definition, we may consider a node to be a de-
scendant of itself.

❒ Node distance – The distance d(p,q) between two nodes p and q is the minimum
number of edges between these two nodes. If we note s ≜ LCA(p,q), we have the
following formula:

d(p,q) = depth(p) + depth(q) − 2 × depth(s)

0 r
1 s

2 p

3 q

77
Super Study Guide Graphs and Trees

For instance, nodes p and q have a distance of 3 in the tree above.

❒ Serialization – Tree serialization is a method to encode a tree into a generic


format (e.g. string) without losing information.

VA
VB VC VA VB VC VD VE

VD VE

Remark: There can be more than one way to serialize a tree. The figure above
illustrates a level-by-level approach in the case of binary trees.

3.3.2 Binary trees

❒ Definition – A binary tree is a tree where each node has at most 2 children.
The table below sums up possible properties of binary trees:

Type Description Illustration

Every node has either


Full
0 or 2 children.

All levels are completely


filled, except possibly
Complete
the last one where all
nodes are to the left.

All internal nodes have


Perfect 2 children and all leaves
are at the same level.

Remark: A perfect tree has exactly 2h+1 − 1 nodes with h the height of the tree.

❒ Diameter – The diameter of a binary tree is the longest distance between any
of its two nodes.

78
Super Study Guide Graphs and Trees

For example, the binary tree above has a diameter of 5.

❒ Main tree traversals – The table below summarizes the 3 main ways to recur-
sively traverse a binary tree:

Traversal Algorithm Illustration Example

1. Visit the root 1


1
2 4
Pre-order 2. Visit the left subtree
2 3
3. Visit the right subtree 3 5

1. Visit the left subtree 3


2
1 4
In-order 2. Visit the root
1 3
3. Visit the right subtree 2 5

1. Visit the left subtree 5


3
2 4
Post-order 2. Visit the right subtree
1 2
3. Visit the root 1 3

Remark: Pre-order, in-order and post-order traversals are all variations of DFS.

❒ Balanced tree – A binary tree is said to be balanced if the difference in height


of each node restricted to its left and right subtrees is at most 1.

79
Super Study Guide Graphs and Trees

Balanced Not balanced

3 2 3 2

1 2 1 1 0 2 1 1

1 0 1 0
0 0 0 0 0 0 0 0 0 0

0 0 0 0

3.3.3 Heaps
❒ Definition – A heap is a complete binary tree with an additional property that
makes it either a min-heap or a max-heap:

Type Description Illustration Example


The value of each node is
v 2
lower than its children’s,
5 3
Min-heap if any.
By definition, the root has ⩾v ⩾v 6 8 4
the lowest value.
The value of each node is
higher than its children’s, v 8
if any. 5 6
Max-heap
By definition, the root has ⩽v ⩽v 4 2 3
the highest value.

Since a heap is a complete binary tree, it is convenient to represent it with an array


of size n, where n is the number of nodes.

VA
VB VC ⟺ VA VB VC VD VE VF
0 1 2 3 4 5
VD VE VF

For a given node of index i ∈ [[0, n − 1]],


 
i−1
• its parent has an index of
2
• its left child has an index of 2i + 1 and its right child has an index of 2i + 2

80
Super Study Guide Graphs and Trees

The following parts will use max-heaps for consistency. However, one could also
use min-heaps to obtain the same results.

❒ Heapify up – Let’s suppose that the last child is potentially not fulfilling the
properties of the max-heap. The heapify up operation, also called bubble up, aims
at finding the correct place for this node.

24
10 12

5 3 15
?

This operation is done in O(log(n)) time:

• Update step: While the node’s parent has a lower value than the node’s, swap
the two.

• Final step: We now have a valid max-heap.

24 24
10 12 10 15
...
5 3 15 5 3 12

❒ Heapify down – Let’s suppose that the root is potentially not fulfilling the
properties of the max-heap. The heapify down operation, also called bubble down,
aims at finding the correct place for this node.

2
10 ? 15

5 3 12

This operation is done in O(log(n)) time:

• Update step: While the highest-value child of the node has a higher value than
the node’s, swap the two.

• Final step: We now have a valid max-heap.

81
Super Study Guide Graphs and Trees

2 15
10 15 ... 10 12

5 3 12 5 3 2

❒ Operations – The main operations that can be performed on a max-heap are


explained below:

Search We distinguish two cases:

• Maximum value: Look at the value corresponding to the root of the heap. It
takes O(1) time.

24 24
10 ? 15 10 15

5 3 12 5 3 12

• Any other value: Traverse the tree, given that we have no information as to
where each node is. It takes O(n) time.

24 24
10 15 10 15

5 3 12 5 3 12
?

Insertion It takes O(log(n)) time.

24 ?
10 15 20
5 3 12

• Placeholder step: Add new node as the last child.

• Heapify up step: Heapify up the child to its final position.

82
Super Study Guide Graphs and Trees

24 24
10 15 heapify up 10 20
...

5 3 12 20 5 3 12 15
?

Deletion It takes O(log(n)) time.

• Swap step: Swap node with last child and remove new last child.

24
10 15

5 3 12

• Heapify step: Move the newly-placed node to its final position depending on
the situation:

– Node’s value is higher than parent’s: Heapify up to its final position.


– Node’s value is lower than highest of its children: Heapify down to its
final position.
– Node’s value is lower than parent’s and higher than highest of its children:
There is nothing to do.

12 15
10 15 heapify 10 12
? ...

5 3 5 3

❒ k smallest elements – Given array A = [a0 , ..., an−1 ], the goal is to find the k
smallest elements, with k ⩽ n.

a0 ... ai1 ... ai2 ... aik ... an−1


0 ... i1 ... i2 ... ik ... n−1
...
k smallest
Trick Use a max-heap of size k.

83
Super Study Guide Graphs and Trees

Algorithm The solution is found in O(n log(k)) time and O(k) space:

• Initialization:

– Set an empty max-heap that will keep track of the k smallest elements.
– Add the first k elements into the max-heap.

At any given point, we note M the value of the root of the max-heap, i.e. its
maximum value.

a0 ... ak−1 ... an−1


M
0 ... k−1 ... n−1

⩽M ⩽M

• Update step: We need to see whether any of the remaining elements ai∈[[k,n−1]]
is potentially part of the k smallest elements:

– If ai < M , pop the max-heap and insert ai in O(log(k)) time.

... ai ... an−1


M
i ... n−1

⩽M ⩽M

– If ai ⩾ M , it means that the element is greater than any of the current


k smallest elements. We do not do anything. The check is done in O(1)
time.

... ai ... an−1


M
i ... n−1

⩽M ⩽M

• Final step: The max-heap contains the k smallest elements.

Remark: Similarly, the k largest elements can be retrieved using a min-heap.

3.3.4 Binary search trees


❒ Definition – A binary search tree (BST) is a binary tree where each node has
the following properties:

84
Super Study Guide Graphs and Trees

• Its value is greater than any node values in its left subtree

• Its value is less than any node values in its right subtree

<v >v

We note h the height of the BST, where h can go anywhere between:

Best case Worst case


Description Tree is balanced Every node has at most 1 child
Height h ≈ log(n) h=n

12 24
5 24 15

3 10 15 12
Illustration
10

❒ Operations – The main operations that can be performed on a BST each have
a time complexity of O(h) and are explained below:

Search Starting from the root, compare the node value with the value v we want
to search for.

12
5 24

3 10 15
?

• Node value different than v:

85
Super Study Guide Graphs and Trees

– If it is higher than v, go to its left child.


– If it is lower than v, go to its right child.

10 < 12 12
5 24 5 24

<
10
3 10 15 3 10 15

• Node value equal to v: We found the target node.

12
5 24

3 10 15

Continue this process recursively until either finding the target node or hitting the
end of the tree, in which case v is not present in the BST.

Insertion Let’s suppose we want to insert a node of value v in the BST.

12 ?
5 24 11
3 10 15

In order to do that, we check if there is already a node with the same value in O(h)
time. At the end of the search, there are two possible situations:

• Node is found: There is nothing else to do, since the element that we want to
insert is already in the tree.

• Node is not found: By construction, the last node seen during the search is a
leaf.

– If v is higher than the value of the last node, add it as its right child.
– If v is lower than the value of the last node, add it as its left child.

86
Super Study Guide Graphs and Trees

11 < 12 12
5 24 5 24
<
11

3 10 15 3 10 15
<
11

11

Deletion The process depends on the number of children of the node to be


deleted. We have the following situations:

• 0 child: Delete the node.

12 12
5 24 5 24

3 10 15 3 15

• 1 child: Replace the node with its child.

12 12
5 24 5 15

3 10 15 3 10

• 2 children: Replace the node with the max of its left subtree, which is also
equal to the in-order predecessor.

12 10
5 24 5 24

3 10 15 3 15

3.3.5 N -ary trees

❒ Definition – An N -ary tree is a tree where each node has at most N children.

87
Super Study Guide Graphs and Trees

V1 ... VN

❒ Trie – A trie, also called prefix tree, is an N -ary tree that allows for efficient
storing and fast retrieval of words. The path from the root node to another given
node forms a word that is deduced by concatenating the characters along that path.

c1 c2 ... ck

Each node n is defined by the following quantities:

• A character c.

• A hash table h = {(c1 , n1 ), ..., (ck , nk )} gathering the children of that node,
where ci is the child’s character and ni its associated node.

• A boolean that indicates whether the word formed by the path leading to that
node is in the dictionary a or not a .

By convention, the root is a node for which c is an empty string.

b k p

e i o

a d n e

r d m

Note that even though all characters of a word are present in the correct order, it
does not necessarily mean that the word itself is present in the trie. In order to

88
Super Study Guide Graphs and Trees

ensure that the word is indeed in the trie, an important additional condition is for
the boolean of the last character to be set to a .

The operations that can be done with a trie are described below:

Search We would like to know whether a word w is in the trie.

• Character step: Starting from the root, traverse the trie by checking one
character of w at a time. If any character is missing, it means that the word
is not present.

• Word step: Check whether the boolean of the node corresponding to the last
character is set to a . If it is not, it means that the word is not present.

b k p

b e a r e i o

a d n e

r d m

Insertion We would like to insert a word w in the trie.

• Character step: Starting from the root, traverse the trie one character of
w at a time. Starting from the first missing character (if applicable), add
corresponding nodes until completing the word.

• Word step: Set the boolean of the last character to a .

b k p

p o e t e i o

a d n e

r d m t

89
Super Study Guide Graphs and Trees

Deletion We would like to remove a word w from the trie.

• Character step: Traverse the trie one character of w at a time.

• Word step: Set the boolean of the last character to a .

b k p

p o e m e i o

a d n e

r d m

Remark: Use cases of a trie include searching for a word starting with a given prefix.

3.4 Advanced trees


In this part, we will learn about self-balancing trees such as red-black trees and
AVLs, along with more advanced trees that can handle range sum queries such as
binary indexed trees and segment trees.

3.4.1 Self-balancing trees


❒ Definition – A self-balancing tree is a BST that keeps its height in O(log(n))
by maintaining specific properties. Examples of such trees include red-black trees
and AVL trees.

v
O(log n)
<v >v

❒ Tree rotations – Most self-balancing tree operations involve tree rotations,


which are done by changing the shape of the BST while keeping its properties
satisfied. There are two kinds of rotations: left and right rotations. We give the
recipe to do both below.

We arbitrarily focus on the three following nodes, which are useful during a tree
rotation:

90
Super Study Guide Graphs and Trees

• Pivot p: Node that becomes the new root.

• Original root r: Root of the original tree.

• Moving child c: Node that changes parents.

Tree rotations have a time complexity of O(1).

Left rotation
Before After
Pivot Right child of root New root
Original root Root Left child of pivot
Moving child Left child of pivot Right child of original root

r p

p r
Illustration <r >p

c >p <r c

Right rotation
Before After
Pivot Left child of root New root
Original root Root Right child of pivot
Moving child Right child of pivot Left child of original root

r p

p r
Illustration >r <p

<p c c >r

Remark: Performing tree rotations does not change the in-order traversal of the
BST.

❒ Red-black tree – A red-black tree is a self-balancing tree with height O(log(n))


that maintains the following properties:

• Coloring:

91
Super Study Guide Graphs and Trees

– Each node is either red or black.


– The root and leaves (NIL) are black.
– If a node is red, then its children are black.

• Count: All paths from a given node to any of the leaves have the same number
of black nodes, often referred to as the black-height.

12
5 24

3 10 15 NIL

NIL NIL NIL NIL NIL NIL

❒ AVL tree – An Adelson-Velsky and Landis (AVL) tree is a self-balancing tree


with height O(log(n)). Its key property is that any of its nodes must have the
heights hL , hR of their child subtrees TL , TR differ by at most 1:

|hL − hR | ⩽ 1

hL TL TR hR

3.4.2 Efficient trees

❒ Range query – Given an array A = [a0 , ..., an−1 ], a range query qf (A, i, j) is
defined as being an operation f over elements ai , ..., aj .

a0 ... ai ... aj ... an−1


0 ... i ... j ... n−1

qf (A, i, j) = f(ai, …, aj)

The following are the most common types of range queries:

92
Super Study Guide Graphs and Trees

Minimum Maximum Sum


Operation min(ai , ..., aj ) max(ai , ..., aj ) ai + ... + aj

5 4 9 0 1 3 5 4 9 0 1 3 5 4 9 0 1 3
Illustration i j i j i j

0 9 5+4+9+0+1+3=22

In the case of a range sum query qS , we note that for 0 ⩽ i ⩽ j < n, we have:

qS (A, i, j) = qS (A, 0, j) − qS (A, 0, i − 1)

a0 ... ai ... aj ... an−1 qS(A, i, j)


a0 ... ai ... aj ... an−1 qS(A, 0, j)
a0 ... ai ... aj ... an−1 qS(A, 0, i − 1)

This means that if we can compute qS (A, 0, i) for all i, then we can handle any
range sum query on array A.

❒ Prefix sum array – A prefix sum array P = [p0 , ..., pn−1 ] is an array that
computes range sum queries on A in O(1) time.

Construction It is built in O(n) time as follows:

p0 = a0 and ∀i ∈ [[1, n − 1]], pi = pi−1 + ai

a0 ai−1 ai

p0 pi−1 pi
0 i−1 i

By convention, we set p−1 ≜ 0.

Access The computation of a range sum query takes O(1) time as follows:

qS (A, i, j) = pj − pi−1

a0 ... ai−1 ai ... aj ... an−1

p0 ... pi−1 pi ... pj ... pn−1


0 ... i−1 i ... j ... n−1

93
Super Study Guide Graphs and Trees

Update If we update ai ←− ai + x, it takes O(n) time to update P :

∀k ∈ [[i, n − 1]], pk ←− pk + x

+x +x +x +x +x

ai ⟺ pi pi+1 ... pn−1


i i i+1 ... n−1

❒ Binary indexed tree – A binary indexed tree B, also called Fenwick tree, is a
data structure that efficiently handles range sum queries on array A = [a0 , ..., an−1 ].
Updates in A are reflected in B in O(log(n)) time.

a0 a1 a2 a3 a4 b1 b2 b4
0 1 2 3 4

b3 b5

Rescaling A prerequisite is to map the input array indices from [[0, n − 1]] to
[[1,n]]. We rewrite A as A′ = [0, a′1 , ..., a′n ] with a′i = ai−1 .
a0 a1 ... ai−1 ... an−1

0 a′1 a′2 ... a′i ... a′n


0 1 2 ... i ... n

Intuition Elements of the binary indexed tree leverage the binary representations
of their indices i ∈ [[1,n]]. In order to do that, it relies on the concept of lowest set
bit l. We note li the lowest set bit of i.
2li
(i)10 ( . . . 1 0 . . . 0 )2
(1)10 (0 0 1)
12
(2)10 (0 1 0)2
(3)10 (0 1 11)2
(4)10 1 0 0)2
(1
(5)10 (1 0 11)2

94
Super Study Guide Graphs and Trees

A given element bi of the binary indexed tree B is said to be responsible for the
range of indices [[i − 2li + 1, i]] and we have:

i
X
bi = a′j
j=i−2li +1

The data structure can be represented either as an array or as a tree:

Array Tree
- Ranges of indices are next to nodes
Ranges of indices are indicated
- Parent of bi is bi−2li
by horizontal lines
- Tree depth is O(log(n))
{0}
b4 b0
b2
{1} {1,2} {1,2,3,4}
b0 b1 b3 b5 b1 b2 b4
0 a′1 a′2 a′3 a′4 a′5
{3} {5}
0 1 2 3 4 5 b3 b5

Construction The binary indexed tree is built iteratively in O(n) time:

• Initialization: Initialize B with the values of A′ :

∀i ∈ [[0, n]], bi ←− a′i

b4
b2
b0 b1 b3 b5
0 a′1 a′2 a′3 a′4 a′5
0 1 2 3 4 5

• Compute step: For each index i ∈ [[1,n]], the relevant partial sums are propa-
gated through the overlapping ranges of responsible values:

If i + 2li ⩽ n, bi+2li ←− bi+2li + bi

95
Super Study Guide Graphs and Trees

b4
b2
b0 b1 b3 b5
0 a′1 a′2 a′3 a′4 a′5
0 1 2 3 4 5

Access In order to compute qS (A′ , 0, i), we need to sum relevant elements of B


in O(log(n)) time:

• Initialization: We start by initializing the sum S to bi .

{0}
b4 b0
b2 {1} {1,2} {1,2,3,4}
b0 b1 b3 b5 ⟺ b1 b2 b4
0 a′1 a′2 a′3 a′4 a′5
{3} {5}
0 1 2 3 4 5 b3 b5

• Compute step: Starting from j ←− i − 2li and then by further decrements of


2lj , we update S until j is equal to 0:

S ←− S + bj

{0}
b4 b0
b2 {1} {1,2} {1,2,3,4}
b0 b1 b3 b5 ⟺ b1 b2 b4
0 a′1 a′2 a′3 a′4 a′5
{3} {5}
0 1 2 3 4 5 b3 b5

Update If we update a′i ←− a′i + x, it takes O(log(n)) time to update B:

• Initialization: We start by updating bi as follows:

bi ←− bi + x

96
Super Study Guide Graphs and Trees

b4
b2
b0 b1 b3 b5
0 a′1 a′2 a′3 a′4 a′5
0 1 2 3 4 5

• Compute step: We continue the same update process with j ←− i + 2li , and
then by further increments of 2lj until reaching the maximum index of the
array:
bj ←− bj + x

b4
b2
b0 b1 b3 b5
0 a′1 a′2 a′3 a′4 a′5
0 1 2 3 4 5

❒ Segment tree – A segment tree S is a data structure represented by a binary


tree of nodes s[[i,j]] that is designed to support the main types of range queries
(minimum, maximum, sum) on an array A = [a0 , ..., an−1 ]. It has an update time
complexity of O(log(n)).

s[[0,4]]

s[[0,2]] s[[3,4]]
a0 a1 a2 a3 a4
0 1 2 3 4 s[[0,1]] s{2} s{3} s{4}

s{0} s{1}

Intuition Each node of the segment tree is responsible for a range [[l, r]] of indices
of the original array. In particular, each node s[[l,r]] falls into one of the following
categories:
 
l+r
• Case l ̸= r: It has two children. By noting m = :
2
– Its left child is responsible for indices [[l, m]].
– Its right child is responsible for indices [[m + 1, r]].

97
Super Study Guide Graphs and Trees

s[[l,r]]

l m r s[[l,m]] s[[m+1,r]]

• Case l = r: It has 0 child and it is responsible for index {l}.

s{l}
l r

The root node is responsible for indices [[0, n − 1]]. The segment tree has a height
of O(log(n)).

s[[0,n−1]]
O(log n)
s[[0,m]] s[[m+1,n−1]]

For illustrative purposes, we are going to focus on range sum queries so that oper-
ations of the segment tree can be easily compared to those of the data structures we
saw previously.

Construction The segment tree is built recursively in O(n) time, where the value
of each node depends on where it is located:

• Node has no children: Its value is the corresponding value in the original
array:
s{e} = ae

⟺ s{e}
e

• Node has two children: Its value is the sum of the values of its child nodes:

s[[l,r]] = s[[l,m]] + s[[m+1,r]]

98
Super Study Guide Graphs and Trees

s[[l,r]]

s[[l,m]] s[[m+1,r]]

Access The operation qS (A, i, j) is done with a complexity of O(log(n)) time.

a0 ... ai ... aj ... an−1


0 ... I ... n−1

Given an interval I = [[i, j]], we start from the root node of the segment tree and
make recursive calls.

At node sIN , there are 3 possible situations:

• Case IN ∩ I ̸= ∅: There is an overlap.

– Sub-case IN ⊆ I: There is a total overlap, so the information of that


node is added to the total sum.

sIN
sIN
I

– Sub-case IN ̸⊆ I: There is a partial overlap, so we are going to look at


both children of the node.

sIN
sIN
I
sIN sIN
1 2

• Case IN ∩ I = ∅: There is no overlap, the answer is not there. We don’t look


further.

sIN
sIN
I

99
Super Study Guide Graphs and Trees

Update If we update ai ←− ai + x, it takes O(log(n)) time to update S.

We start at the root node and make recursive calls. We check whether i is in the
interval IN = [[l, r]] associated with the node:

• Case i ∈ IN : The node contains information related to ai . We update the


node by adding x:
sIN ←− sIN + x
 
l+r
By noting m = :
2
– Sub-case i ⩽ m: i is also located in its left child.

sIN
sIN
i m

sIN sIN
1 2

– Sub-case i > m: i is also located in its right child.

sIN
m i
sIN

sIN sIN
1 2

• Case i ∈
/ IN : The node does not contain information related to ai .

sIN
sIN
i

100
Super Study Guide Sorting and Search

SECTION 4

Sorting and search

In this last section, we will go through the most common sorting algorithms,
along with useful search techniques that are used to solve many problems.

4.1 Sorting algorithms

In this part, we will learn about common sorting algorithms such as bubble sort,
insertion sort, selection sort, merge sort, heap sort and quick sort. We will also
study sorting algorithms such as counting sort and radix sort that are particularly
efficient in some special cases.

4.1.1 General concepts

In this part, arrays of n elements are visually represented as histograms. The height
of each bar represents the value of the associated element in the array.

❒ Sorting algorithm – A sorting algorithm takes an unsorted array A = [a0 , ..., an−1 ]
as input and returns a sorted array A′ = [a′0 , ..., a′n−1 ] as output. A′ is a permutation
of A such that:
a′0 ⩽ a′1 ⩽ ... ⩽ a′n−1

A A′

❒ Stability – A sorting algorithm is said to be stable if the order of tied elements


is guaranteed to remain the same after sorting the array.

101
Super Study Guide Sorting and Search

stable

? unstable

Remark: Examples of stable sorting algorithms include merge sort and insertion
sort.

The next sections will go through each sorting algorithm into more detail.

4.1.2 Basic sort

❒ Bubble sort – Bubble sort is a stable sorting algorithm that has a time com-
plexity of O(n2 ) and a space complexity of O(1).

Intuition Compare consecutive elements and swap them if they are not in the
correct order.

Algorithm

• Compute step: Starting from the beginning of the array, compare the element
al at position i with the element ar at position i + 1.

– Case al ⩽ ar : They are already in the correct order. There is nothing to


do.
– Case al > ar : They are not in the correct order. Swap them.

Repeat this process until reaching the end of the array.

102
Super Study Guide Sorting and Search

...

• Repeat step: Repeat the compute step until no swap can be done.

We note that:

• At the end of the k th pass, the last k elements of the array are guaranteed to
be in their final positions.

• The algorithm finishes in at most n − 1 passes. In particular:

– If the input array is already sorted, then the algorithm finishes in 1 pass.
– If the input array is reverse sorted, then the algorithm finishes in n − 1
passes.

❒ Insertion sort – Insertion sort is a stable sorting algorithm that has a time
complexity of O(n2 ) and a space complexity of O(1).

Intuition Incrementally build a sorted subarray by successively inserting the next


unsorted element at its correct position.

Algorithm

• Initialization: The first element of the unsorted array can be interpreted as a


subarray of one element that is already sorted.

103
Super Study Guide Sorting and Search

• Compute step: Starting from position i = 1, we want to insert element ai into


the current sorted subarray of size i. In order to do that, we compare ai with
its preceding element ap :
– As long as ap > ai , we iteratively swap ap with ai .
– This process ends with either ai verifying ap ⩽ ai or ai being at position
0 of the array.
At the end of this step, the sorted subarray is of size i + 1.

• Repeat step: Repeat the compute step until the sorted subarray reaches a size
of n.

...

❒ Selection sort – Selection sort is a stable sorting algorithm that has a time
complexity of O(n2 ) and a space complexity of O(1).

Intuition Incrementally build a sorted subarray by successively inserting the min-


imum value among the remaining elements.

104
Super Study Guide Sorting and Search

Algorithm

• Initialization:

– Find the minimum value of the unsorted array.


– Swap it with the element at the beginning of the array. The first element
can now be interpreted as a sorted subarray with one element.

min

• Compute step: Starting from i = 1, we want to insert a new element into the
sorted subarray of size i.

– Find the minimum value amin among the remaining elements at positions
i, ..., n − 1 of the array. By construction, amin is greater or equal than all
elements of the current sorted subarray.
– Swap amin with the element at position i of the array.

At the end of this step, the sorted subarray is of size i + 1.

min

• Repeat step: Repeat the compute step until the sorted subarray reaches a size
of n.

...
min min

105
Super Study Guide Sorting and Search

Remark: Insertion and selection sort are very similar in that they build the sorted
array from scratch and add elements one at a time.

❒ Cycle sort – Cycle sort is an unstable sorting algorithm that has a time com-
plexity of O(n2 ) and a space complexity of O(1).

Intuition Determine the index of the final position of each element in the sorted
array.

Algorithm

• Compute step: Starting from i = 0, we want to find the final position of


element ai along with those of the other elements impacted by this move.

To do so, we count the number of elements that are smaller than ai :

Ni = # {k, ak < ai }

Ni

The final position of ai is at index Ni , since we know that there are Ni smaller
elements than ai in the final sorted array.

– Case Ni = i: This is a self-cycle, meaning that the element is already at


its correct position. There is nothing to do.
– Case Ni ̸= i: This is the start of a cycle of moves. Place ai at position
Ni (or to the right of any duplicates, if applicable) and keep the replaced
value in a temporary variable.

106
Super Study Guide Sorting and Search

Ni

Keep moving elements using this logic until getting back to position i.
• Repeat step: Repeat the compute step until reaching the end of the array. We
can see cycles being formed from the way elements are moved.

Remark: This algorithm sorts the array with a minimum amount of rewrites since
it only moves elements to their final positions.
❒ Basic sorting algorithms summary – The table below summarizes the main
basic sorting algorithms:
Time
Type Space Stability
Best Average Worst
Bubble sort O(n) O(n2 ) O(n2 ) O(1) Yes

Insertion sort O(n) O(n2 ) O(n2 ) O(1) Yes


2 2 2
Selection sort O(n ) O(n ) O(n ) O(1) Yes

Cycle sort O(n2 ) O(n2 ) O(n2 ) O(1) No

4.1.3 Efficient sort


❒ Merge sort – Merge sort is a stable sorting algorithm that has a time complexity
of O(n log(n)) and a space complexity of O(n).

Intuition Build a sorted array by merging two already-sorted arrays.

107
Super Study Guide Sorting and Search

Algorithm
• Divide step: Divide the array into as many subarrays as there are elements.
Each resulting subarray has only one element and can be considered as sorted.

• Conquer step: For each pair of sorted subarrays, build a sorted array by
merging them using the two-pointer technique.

Repeat the process until all subarrays are merged into one final sorted array.

Remark: This algorithm is usually implemented recursively.


❒ Heap sort – Heap sort is an unstable sorting algorithm that has a time com-
plexity of O(n log(n)) and a space complexity of O(1).

Intuition Incrementally build a sorted subarray by successively retrieving the


maximum of remaining elements using a max-heap.

108
Super Study Guide Sorting and Search

Algorithm
• Initialization: Build a max-heap from the unsorted array in O(n) time. This
is done by recursively swapping each parent with their child of highest value.

7 4 7 7
6 6
1 3 4 6
4 4 ⟺ 4 4 ⟺
3 3
2 2
1 2 7 4 6 1 2 1 4 3

We can use the same array to represent the max-heap.


• Compute step: Starting from i = 0, incrementally build a sorted subarray of
size i + 1 that is placed at the end of the array.
– Pop an element from the max-heap and place it at position n − i − 1 of
the array.
7 7 7 3
6 6
4 6 4 6
4 4 ⟺ 4 4 ⟺
3 3
2 2
1 2 1 4 3 1 2 1 4 7

By construction, the popped element is greater or equal than the n−i−1


elements of the heap and smaller or equal than the i previously popped
elements.
– Heapify the resulting max-heap of size n − i − 1 in O(log(n)) time.
7 3 7 6
6 6
4 6 4 4
4 4 ⟺ 4 4 ⟺
3 3
2 2
1 2 1 4 7 1 2 1 3 7

• Repeat step: Repeat the compute step until the subarray reaches a size of n.

7 2 7
6 6
1 3
4 4 ⟺ 4 4
3 3
2 2
1 4 4 6 7 1

109
Super Study Guide Sorting and Search

❒ Quick sort – Quick sort is an unstable sorting algorithm that has a time com-
plexity of O(n2 ) and a space complexity of O(n).

Intuition Recursively choose an element of the array to be the pivot, and put:

• Smaller elements to the left of the pivot.

• Bigger elements to the right of the pivot.

Algorithm

• Compute step:

– Pivot isolation step: Choose an element of the array to be the pivot p


and swap it with the last element of the array.

– Partition step: Starting from indices l = 0 and r = 0, we make sure that


elements ar that are smaller or equal than the pivot p are sent to the left
of the array:
∗ Case ar ⩽ p: Swap element ar at index r with element al at index
l. Increment l by 1.
∗ Case ar > p: There is nothing to do.
This process is successively repeated for all indices r until the second-to-
last position of the array.

l r l r l r

110
Super Study Guide Sorting and Search

– Pivot inclusion step: The final position of the left pointer l represents
the position in the array where its left elements are all array elements
that are smaller than or equal to the pivot. We swap the pivot with the
element from position l.

At the end of this step, the pivot is at its correct and final position.
• Recursion step: The compute step is run recursively on the resulting subarrays
on the left and right of the pivot. They are then merged back together to form
the final sorted array.

...

We note that the runtime of the algorithm is sensitive to the choice of the pivot and
the patterns found in the input array. In general, a time complexity of O(n log(n))
and a space complexity of O(log(n)) can be obtained when the pivot is a good
approximation of the median of the array.

The two most common methods to choose the pivot are:

Random choice Median of three


Pivot is the median of the first, middle
Pivot is chosen at random.
and last elements of the array.

❒ Efficient sorting algorithms summary – The table below summarizes the


main efficient sorting algorithms:

111
Super Study Guide Sorting and Search

Time
Type Space Stability
Best Average Worst
Merge sort O(n log(n)) O(n log(n)) O(n log(n)) O(n) Yes
Heap sort O(n log(n)) O(n log(n)) O(n log(n)) O(1) No
2
Quick sort O(n log(n)) O(n log(n)) O(n ) O(n) No

4.1.4 Special sort


❒ Counting sort – Counting sort is a stable sorting algorithm that is efficient for
integer arrays with values within a small range [[0, k]]. It has a time complexity of
O(n + k) and a space complexity of O(n + k).

Intuition Determine the final position of each element by counting the number
of times its associated value appears in the array.

Algorithm

• Number of occurrences: This step takes O(n + k) time and O(k) space.

– Initialization: Initialize an array C of length k + 1.


– Count step: Scan array A and increment the counter cv of each encoun-
tered value v ∈ {0, ..., k} by 1.

7
6
4 4
3 +1
2 0 1 2 3 4 5 6 7
1
...

7
6
4 4
3 +1
2 0 1 2 3 4 5 6 7
1

112
Super Study Guide Sorting and Search

Each count cv represents the number of times the value v ∈ {0, ..., k}
appears in array A.
– Cumulative step: Compute the cumulative sum of array C = [c0 , .., ck ]
and move each resulting element to the right. This operation is done
in-place and takes O(k) time and O(1) space.

0 1 1 1 2 0 1 1 0 0 1 2 3 5 5 6
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7

For each value v present in A, the associated element cv in the resulting array
C indicates its starting index i in the final sorted array A′ .

• Construction of the sorted array: This step takes O(n) time and O(n) space.

– Initialization: Initialize an array A′ of length n that will contain the final


sorted array.
– Main step: For each value v of the unsorted array A:
∗ Write v to index cv of A′ .

7
6
4 4 4
3 0 0 1 2 3 5 5 6
2 0 1 2 3 4 5 6 7
1
3

∗ Increment cv by 1 so that any later duplicates can be handled.


+1
0 1 2 3 4 5 6 7

At the end of this process, we obtain the final sorted array A′ .

7 7
6 6
4 4 4 4
3 0 1 2 3 5 5 5 7 3
2 0 1 2 3 4 5 6 7 2
1 1
5

❒ Radix sort – Radix sort is a stable sorting algorithm that is well suited for
integer arrays where elements are written with a limited number of digits d, each
digit being in the range [[0, k]]. This algorithm has a time complexity of O(d(n + k))
and a space complexity of O(n + k).

113
Super Study Guide Sorting and Search

Intuition Successively sort elements based on their digits using counting sort.

X... X X X . .. X X X .. .X X
...

Algorithm

• Compute step: Perform counting sort based on the rightmost digit. This step
takes O(n + k) time and O(n + k) space.

72
67 72 67

40 40 40 40
31 27 31 27
15 15

• Repeat step: Repeat the compute step on the remaining digits until reaching
the leftmost digit.

72 67 72
67

40 40 40 40
31 27 27 31
15 15

At the end of this process, the array is sorted.

The trick of this algorithm lies in the stability property of counting sort: the relative
ordering based on a given (weak) digit helps in breaking ties of later (stronger) digits.

❒ Special sorting algorithms summary – The table below summarizes the main
special sorting algorithms:

Time
Type Space Stability
Best Average Worst
Counting sort O(n + k) O(n + k) O(n + k) O(n + k) Yes
Radix sort O(d(n + k)) O(d(n + k)) O(d(n + k)) O(n + k) Yes

114
Super Study Guide Sorting and Search

4.2 Search algorithms


In this part, we will focus on the most important search algorithms such as linear
search and binary search. We will also study the main string pattern matching
algorithms.

4.2.1 Basic search


❒ Linear search – Linear search is a basic search method often used when the rel-
ative ordering of elements is not known, e.g. in unsorted arrays. It has a complexity
of O(n) time and O(1) space:

• Scan step: Check whether the constraint of the problem is verified on the first
element of the array.

Repeat the process on the remaining elements in a sequential way.

• Final step: Stop the search when the desired condition is satisfied.

❒ Two-pointer technique – The two-pointer technique provides an efficient way


to scan arrays by leveraging the assumptions of the problem, e.g. the fact that an
array is sorted.

To illustrate this concept, suppose A = [a0 , ..., an−1 ] a sorted array. Given S, the
goal is to find two elements ai , aj such that ai + aj = S.

ai + aj = S

a0 a1 ... ai ... aj ... an−1


0 1 ... i ... j ... n−1

The two-pointer technique finds an answer in O(n) time:

• Initialization: Initialize pointers l and r that are set to be the first and last
element of the array respectively.

115
Super Study Guide Sorting and Search

l r

• Compute step: Compute Spointers ≜ al + ar .

– Case Spointers < S: The computed sum needs to increase, so we move


the l pointer to the right.

l l r

– Case Spointers > S: The computed sum needs to decrease, so we move


the r pointer to the left.

l r r

• Final step: Repeat the compute step until one of the following situations
happens:

– Case l = r: There is no solution to the problem.

lr

– Case Spointers = S: A solution is found with i = l and j = r.

l r

❒ Trapping rain water – A classic problem that uses the two-pointer technique
is the trapping rain water problem. Given a histogram H = [h0 , ..., hn−1 ] that
represents the height hi ⩾ 0 of each building i, the goal is to find the total volume
V of water that can be trapped between the buildings.

hi
i

116
Super Study Guide Sorting and Search

Tricks

• The maximum volume of water vi trapped at index i is determined by the


relative height between hi and the tallest buildings on the left hM
l and on the
right hM
r :
vi = min(hM M
l , hr ) − hi

• Use the two-pointer technique to efficiently update hM M


l and hr at each step.

vi

hlM hi hrM
i

The solution is found in O(n) time and O(1) space:

• Initialization:

– Pointers: Set the left and right pointers to l = 0 and r = n − 1.


– Maximum heights: Set the maximum height of the left buildings hM
l and
M
the right buildings hr to hl and hr .
– Total amount of water: Set the total amount of water V to 0.

hlM hrM
l r

• Compute step: Update the values of the maximum heights of left and right
buildings with the current values:

hM M
l = max(hl , hl ) and hM M
r = max(hr , hr )

– If hM M
l < hr , then the left side is the limiting side, so:

117
Super Study Guide Sorting and Search

∗ Add the available volume vl = hM


l −hl at index l to the total amount
of water V :
V ←− V + vl
∗ Move the left pointer to the right:

l ←− l + 1

vl
hlM hrM
l l r

– Conversely, if hM M
l ⩾ hr , then:

∗ Add the available volume vr = hM


r − hr at index r to the total
amount of water V :
V ←− V + vr
∗ Move the right pointer to the left:

r ←− r − 1

hlM hrM
l r r

• Final step: The algorithm finishes when the two pointers meet, in which case
the amount of water is given by V .

hlM hrM
l r

118
Super Study Guide Sorting and Search

4.2.2 Binary search


❒ Algorithm – Binary search aims at finding a given target value t in a sorted
array A = [a0 , ..., an−1 ].

This algorithm has a complexity of O(log(n)) time and O(1) space:

• Initialization: Choose the lower bound l and the upper bound r corresponding
to the desired search space. Here, l = 0 and r = n − 1.

l r

• Compute step: Compute the middle m of the two bounds:


 
r−l
m≜l+
2

The above formula is written in a way that avoids integer overflow in case l
and r are too large.

am
l r

We have the following cases:

– Case t < am : Shrink the search space to the left side by updating r to
r = m − 1.
am
l r r
– Case t > am : Shrink the search space to the right side by updating l to
l = m + 1.
am
l l r
– Case t = am : We found a solution.
am
l r

• Final step: Repeat the compute step until one of the following situations
happens:

119
Super Study Guide Sorting and Search

– Case t = am : A solution is found.


– Case r < l: There is no solution since the pointers intersect after fully
covering the search space.

❒ Median of sorted arrays – Suppose we want to compute the median of two


sorted arrays A, B of respective lengths n1 , n2 , with n1 ⩽ n2 .

a0 a1 ... an1−1 b0 b1 ... bn2−1

A naive approach would combine the two sorted arrays in O(n1 +n2 ) time, and then
select the median of the resulting array. However, a O(log(n1 )) approach exists that
uses binary search in a clever way.

Tricks

• If we categorize all elements of both arrays into:


n1 +n2
– A left partition that has the ≈ 2 smallest elements.
n1 +n2
– A right partition that has the ≈ 2 biggest elements.

Then we can deduce the median by looking at the maximum of the left par-
tition and the minimum of the right partition.

• If we know how many elements from A are in the left partition, then we can
deduce how many elements from B are in there too.

Aleft∪ Bleft Aright∪ Bright

n1 + n2 n1 + n2
≈ ≈
2 2

• By noting Aleft , Aright , Bleft , Bright the left and right partitions from A and B,
we know the partitioning is done correctly when the following conditions are
satisfied:

(C1) max(Aleft ) ⩽ min(Bright ) and (C2) max(Bleft ) ⩽ min(Aright )

C1
a0 ... ai−1 ai ... an1−1 b0 ... bj−1 bj ... bn2−1
C2

120
Super Study Guide Sorting and Search

The binary search solution is based on the position of the partitioning within the
array A. The solution is found in O(log(n1 )) time:

• Initialization: We note i ∈ [[0, n1 ]] and j ∈ [[0, n2 ]] the lengths of the left


partitions within array A and B respectively. We fix the total size of the left
partitions to be n1 +n2 2 +1 .

n1 + n2 + 1
⌊ ⌋
n2 − +i
n1 − i 2
a0 ... ai−1 ai ... an1−1 b0 ... bj−1 bj ... bn2−1
n1 + n2 + 1
⌊ ⌋
i
−i
2

For a given i, the following formula determines j:


 
n1 + n2 + 1
j= −i
2

• Binary search: We perform a binary search on i between 0 and n1 .

a0 ... al−1 al ... ai−1 ai ... ar−1 ar ... an1−1


l i r

For each candidate, we have the following cases:

– (C1) is not satisfied: We restrict the search space to the left side of i.
a0 ... al−1 al ... ai−1 ai ... ar−1 ar ... an1−1
l r r

– (C2) is not satisfied: We restrict the search space to the right side of i.
a0 ... al−1 al ... ai−1 ai ... ar−1 ar ... an1−1
l l r

– Both (C1) and (C2) are satisfied: The partitioning is done correctly.
a0 ... al−1 al ... ai−1 ai ... ar−1 ar ... an1−1
l i r

121
Super Study Guide Sorting and Search

• Final result: Once the correct partitioning is found, we deduce the value of
the median. We note:

maxleft ≜ max(Aleft , Bleft ) and minright ≜ min(Aright , Bright )

We have:

Case Result Illustration


maxleft + minright
n1 + n2 even median =
2

n1 + n2 odd median = maxleft

4.2.3 Substring search


❒ String pattern matching – Given a string s = s0 ...sn−1 of length n and a
pattern p = p0 ...pm−1 of length m, the string matching problem is about finding
whether pattern p appears in string s.

a p a p y p a p y p a p p s p a p y p a p p
0 i n−1 0 m−1

We note si:i+m−1 the substring of s of length m that starts from si and finishes at
si+m−1 .
❒ Brute-force approach – The brute-force approach consists of checking whether
pattern p matches any possible substring of s of size m in O(n × m) time and O(1)
space.

Starting from i = 0, compare the substring that starts from index i with pattern p.

a p a p y p a p y p a p p s

p a p y p a p p

We have the following possible situations:

• One of the characters is not matching: The pattern does not match the asso-
ciated substring.

a p a p y p a p y p a p p s

p a p y p a p p

122
Super Study Guide Sorting and Search

• All characters are matching: The pattern is found in the string.

a p a p y p a p y p a p p s

p a p y p a p p

❒ Knuth-Morris-Pratt algorithm – The Knuth-Morris-Pratt (KMP) algorithm


is a string pattern matching algorithm that has a complexity of O(n + m) time and
O(m) space.

Intuition Avoid starting the pattern search from scratch after a failed match.

a p a p y p a p y p a p p s

p a p y p a p p

a p a p y p a p y p a p p s

p a p p p a p y

We do so by checking whether the pattern matched so far has a prefix that is also
a suffix:

• Step 1 : Build the prefix-suffix array.

p a p y p a p p 0 0 1 0 1 2 3 1
0 1 2 3 4 5 6 7

• Step 2 : Find the pattern in the original string using the prefix-suffix array.

a p a p y p a p y p a p p s

p a p y p a p p
0 1 2 3 4 5 6 7

0 0 1 0 1 2 3 1

Prefix-suffix array construction We want to build an array Q = [q0 , ..., qm−1 ]


where each value qi has two interpretations:

• It is the length of the largest prefix that is also a suffix in the substring p0 ...pi .

123
Super Study Guide Sorting and Search

p a p y p a p p

0 0 1 0 1 2 3 1

• It is also the index in the pattern that is right after the prefix.

p a p y p a p p
0 1 2 3 4 5 6 7

0 0 1 0 1 2 3 1

This part has a complexity of O(m) time and O(m) space:


• Initialization:
– By convention, we set q−1 to 0.
– The element q0 is set to 0 since the substring p0 has a single character.
– The pointers i and j find the largest prefix that is also a suffix to deduce
the value of qi . They are initially set to i = 1 and j = 0.

p a p y p a p p
j i
0

• Compute step: Determine the length of the largest prefix that is also a suffix
for substring p0 ...pi :
– Case pi ̸= pj and j = 0: The substring does not have a prefix that is
also a suffix, so we set qi to 0:

qi ←− 0

We increment i by 1.
p a p y p a p p
j i
0 0

– Case pi = pj : The last letter pi of the substring matches with pj . As a


result, the length of the prefix is the length of p0 ...pj :

qi ←− j + 1

We increment both i and j by 1 respectively.

124
Super Study Guide Sorting and Search

p a p y p a p p
j i
0 0 1

– Case pi ̸= pj and j > 0: The substring may have a smaller prefix that is
also a suffix:
j ←− qj−1

We repeat this process until we fall in one of the two previous cases.
p a p y p a p p
j j i
0 0 1 0

Linear-time string pattern search We want to efficiently search pattern p in


string s using the newly-constructed prefix-suffix array Q.

This part has a complexity of O(n) time and O(1) space:

• Initialization: The pointers i and j check whether there is a match between


each character of the string and the pattern respectively. They are initially
set to 0.

a p a p y p a p y p a p p s
i
p a p y p a p p
j
0 0 1 0 1 2 3 1

• Compute step: We check whether characters si and pj match.


– Case si = pj : The characters match. We increment i and j by 1.
a p a p y p a p y p a p p s
i
p a p y p a p p
j
0 0 1 0 1 2 3 1

– Case si ̸= pj : The characters do not match. We determine where we


should start our search again by using the information contained in the
prefix-suffix array.

125
Super Study Guide Sorting and Search

∗ qj−1 = 0: We need to restart the pattern match from the beginning


since there is no prefix/suffix to leverage.
∗ qj−1 > 0: We can continue our search after the next largest matched
prefix of the pattern.
We set j to qj−1 .

a p a p y p a p y p a p p s
i
p a p y p a p p
3 j
0 0 1 0 1 2 3 1

• Final step: The algorithm stops when one of the following situations happens:

– Case j = m: The pattern has been matched.


– Case i = n and j < m: The whole string has been searched without
finding a match.

a p a p y p a p y p a p p s
i
p a p y p a p p
j
0 0 1 0 1 2 3 1

❒ Rabin-Karp algorithm – The Rabin-Karp algorithm is a string pattern match-


ing algorithm that uses a hashing trick. It has an expected time complexity of
O(n + m), but depending on the quality of the hash function, the complexity may
increase up to O(n × m). The space complexity is O(1).

Intuition Compute the hash value of the pattern along with those of the sub-
strings of the same length. If the hash values are equal, confirm whether the un-
derlying strings indeed match.

The trick is to choose a hash function h that can deduce h(si+1:i+m ) from the hash
value of the previous substring h(si:i+m−1 ) in O(1) time via a known function f :

h(si+1:i+m ) = f (h(si:i+m−1 ), h(si ), h(si+m ))

126
Super Study Guide Sorting and Search

a p a p y p a p y p a p p s

Algorithm

• Initialization: Given a hash function h, we compute the hash value h(p) of


the pattern p.

m
p a p y p a p p

h(p)

• Compute step: Starting from index i = 0, we use the hashing trick to compute
the hash value of substring si:i+m−1 that starts from i and that has the same
length as p in O(1) time.

– Case h(p) ̸= h(si:i+m−1 ): p and si:i+m−1 definitely do not match.


a p a p y p a p y p a p p s p a p y p a p p
i
h(si:i+m−1) ≠ h(p)

– Case h(p) = h(si:i+m−1 ): There may be a match between p and si:i+m−1 ,


so we compare p and si:i+m−1 .
∗ Sub-case p = si:i+m−1 : The pattern has been matched.
∗ Sub-case p ̸= si:i+m−1 : There is a collision between the hash values
of p and si:i+m−1 . Since there is no match, we continue the search.

a p a p y p a p y p a p p s p a p y p a p p
i
h(si:i+m−1) = h(p)

127
Super Study Guide Notations

Notations
This page summarizes the meaning behind the conventions taken in this book.

Coloring
• ❒ Blue: definitions, notations
• ❒ Red: theorems, properties, algorithms, techniques, summaries
• ❒ Green: operations, applications, classic problems
• Purple: remarks

Symbols
Notation Meaning Illustration
≜ by definition, equal to 3! ≜ 3 × 2 × 1
←− is assigned the value of i ←− 1
≈ approximatively equal to e ≈ 2.72
⊆ is a subset of {2} ⊆ {2, 3}
∅ empty set {2} ∩ {3} = ∅
#S or |S| number of elements in S #{4, 6, 7} = 3
=⇒ implies that x > 0 =⇒ x2 > 0
⇐⇒ equivalent to x = 0 ⇐⇒ x2 = 0
x1 +...+xn
a
b estimate of a µ
b= n

⌊a⌋ floor of a ⌊2.3⌋ = 2


⌈a⌉ ceiling of a ⌈2.3⌉ = 3
|a| absolute value of a |−1| = 1
[[a, b]] integers between a and b included [[1, 3]] = {1, 2, 3}
N set of nonnegative integers {0, 1, 2, ...}

N set of strictly positive integers {1, 2, ...}

Indexing
• Indices of arrays are chosen to start from 0 to align with the convention taken
by most programming languages.
• The numbering of elements in other data structures, such as stacks, queues,
linked lists, hash sets and hash tables, start from 1.

128
Super Study Guide References

References
Adelson-Velsky G., Landis E. (1962) An algorithm for the organization of informa-
tion, Proceedings of the USSR Academy of Sciences, Vol. 146, pages 263-266

Bayer R. (1972) Symmetric binary B-Trees: Data structure and maintenance algo-
rithms, Acta Informatica, Vol. 1, pages 290-306

Bitner J. R., Reingold E. M. (1975) Backtracking Programming Techniques, Com-


munications of the ACM, Vol. 18, No. 11, pages 651-656

Bloom B. H. (1970) Space/time trade-offs in hash coding with allowable errors,


Communications of the ACM, Vol. 13, No. 7

Booth A. D., Colin A. J. T. (1960) On the Efficiency of a New Method of Dictionary


Construction, Information and Control, Vol. 3, pages 327-334

Cayley A. (1889) A theorem on trees, Quarterly Journal of Pure and Applied Math-
ematics, Vol. 23, pages 376-378

Cormode G., Muthukrishnan S. (2003) An Improved Data Stream Summary: The


Count-Min Sketch and its Applications

Dijkstra E. W. (1959) A Note on Two Problems in Connexion with Graphs, Nu-


merische Mathematik, pages 269-271

Fenwick P. M. (1994) A new data structure for cumulative frequency tables, Software:
Practice and Experience, Vol. 24, No. 3, pages 327-336

Floyd R. W. (1962) Algorithm 97: Shortest path, Communications of the ACM, Vol.
5, No. 6, page 345

Floyd R.W. (1967) Nondeterministic Algorithms, Journal of the ACM, Vol. 14, No.
4, pages 636-644

Fredkin E. (1960) Trie Memory, Communications of the ACM, Vol. 3, No. 9, pages
490-499

Goldstine H. H. (1948) Planning and Coding of Problems for an Electronic Com-


puter Instrument, Part 2, Vol. 2

Held M., Karp R. M. (1962) A Dynamic Programming Approach to Sequencing


Problems, Journal of the Society for Industrial and Applied Mathematics, Vol. 10,
No.1, pages 196-210

Hibbard T. N. (1962) Some Combinatorial Properties of Certain Trees With Ap-

129
Super Study Guide References

plications to Searching and Sorting, Journal of the ACM, Vol. 9, No. 1, pages
13-28

Hoare C. A. R. (1961) Algorithm 64: Quicksort, Communications of the ACM, Vol.


4, No. 7, page 321
Karp R., Rabin M. (1977) Efficient Randomized Pattern-Matching Algorithms, IBM
Journal Research and Development, Vol. 31, No. 2, pages 249-260
Knuth D., Morris J. H., Pratt V. (1977) Fast pattern matching in strings, SIAM
Journal on Computing, Vol. 6, No. 2, pages 323-350

Knuth D. (1998) Sorting and searching, The Art of Computer Programming, Volume
3

Kruskal J. B. (1956) On the shortest spanning subtree of a graph and the traveling
salesman problem, Proceedings of the American Mathematical Society, Vol. 7, No.
1, pages 48-50
Merigoux D. (2013) Pense-bête : analyse
Prim R. C. (1957) Shortest Connection Networks And Some Generalizations, The
Bell System Technical Journal, pages 1389-1401

Sharir M. (1981) A strong-connectivity algorithm and its applications to data flow


analysis, Computers and Mathematics with Applications, Vol. 7, No. 1, pages
67-72
Tarjan R. E. (1972) Depth-first search and linear graph algorithms, SIAM Journal
on Computing, Vol. 1 No. 2, pages 146-160
Tarjan R. E. (1983) Data Structures and Network Algorithms
Williams J. W. J. (1964) Algorithm 232: Heapsort, Communications of the ACM,
Vol. 7, No. 6, pages 347-348

Windley P. F. (1960) Trees, Forests and Rearranging, The Computer Journal, Vol.
3, No. 2, pages 84-88

130
Index

A C
A⋆ algorithm . . . . . . . . . . . . . . . . . . . . . . . 65 call stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
addressing Cayley’s formula . . . . . . . . . . . . . . . . . . . . 69
closed . . . . . . . . . . . . . . . . . . . . . . . . . . 39 coin change problem . . . . . . . . . . . . . . . . 23
open . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 collision
adjacency chaining . . . . . . . . . . . . . . . . . . . . . . . 39
list . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 double hashing . . . . . . . . . . . . . . . . . 39
matrix . . . . . . . . . . . . . . . . . . . . . . . . . 54 linear probing . . . . . . . . . . . . . . . . . . 39
algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 quadratic probing . . . . . . . . . . . . . . 39
array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 combination . . . . . . . . . . . . . . . . . . . . . . . . 10
AVL tree . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 complexity . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
space . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
B time . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
backtracking . . . . . . . . . . . . . . . . . . . . . . . . . 3 complexity notation
Bellman-Ford algorithm . . . . . . . . . . . . . 65 Ω(f ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
binary digit . . . . . . . . . . . . . . . . . . . . . . . . . 14 O(f ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
binary indexed tree . . . . . . . . . . . . . . . . . 94 θ(f ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6
binary number . . . . . . . . . . . . . . . . . . . . . . 14 o(f ) . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6
binary search . . . . . . . . . . . . . . . . . . . . . . 119 connected component . . . . . . . . . . . . . . . 71
binary search tree . . . . . . . . . . . . . . . . . . . 84 count-min sketch . . . . . . . . . . . . . . . . . . . . 42
binary tree . . . . . . . . . . . . . . . . . . . . . . . . . . 78 counting sort . . . . . . . . . . . . . . . . . . . . . . 112
balanced . . . . . . . . . . . . . . . . . . . . . . . 79 cycle sort . . . . . . . . . . . . . . . . . . . . . . . . . . 106
complete . . . . . . . . . . . . . . . . . . . . . . . 78
diameter . . . . . . . . . . . . . . . . . . . . . . . 78 D
full . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 DAG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
perfect . . . . . . . . . . . . . . . . . . . . . . . . . 78 daily temperatures . . . . . . . . . . . . . . . . . . 33
binomial degree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
coefficient . . . . . . . . . . . . . . . . . . . . . . . 9 even . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
theorem . . . . . . . . . . . . . . . . . . . . . . . .10 in- . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
bit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 odd . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
highest set . . . . . . . . . . . . . . . . . . . . . 14 out- . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
least significant . . . . . . . . . . . . . . . . 14 depth-first search . . . . . . . . . . . . . . . . . . . 57
left shift . . . . . . . . . . . . . . . . . . . . . . . 15 Dijkstra’s algorithm . . . . . . . . . . . . . . . . . 63
lowest set . . . . . . . . . . . . . . . . . . . . . . 14 divide and conquer . . . . . . . . . . . . . . . . . . . 4
most significant . . . . . . . . . . . . . . . . 14 dynamic programming . . . . . . . . . . . . . . . 4
right shift . . . . . . . . . . . . . . . . . . . . . . 15 bottom-up . . . . . . . . . . . . . . . . . . . . . . 5
bloom filter . . . . . . . . . . . . . . . . . . . . . . . . . 40 top-down . . . . . . . . . . . . . . . . . . . . . . . .5
breadth-first search . . . . . . . . . . . . . . . . . 55
brute-force . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 E
bubble edge . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
down . . . . . . . . . . . . . . . . . . . . . . . . . . 81 weight . . . . . . . . . . . . . . . . . . . . . . . . . 69
up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Euclidean division . . . . . . . . . . . . . . . . . . 12
bubble sort . . . . . . . . . . . . . . . . . . . . . . . . 102 dividend . . . . . . . . . . . . . . . . . . . . . . . 13

131
Super Study Guide Index

divisor . . . . . . . . . . . . . . . . . . . . . . . . . 13 while . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
quotient . . . . . . . . . . . . . . . . . . . . . . . 13
remainder . . . . . . . . . . . . . . . . . . . . . . 13 K
k largest elements . . . . . . . . . . . . . . . . . . . 84
F k smallest elements . . . . . . . . . . . . . . . . . 83
factorial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9 Kadane’s algorithm . . . . . . . . . . . . . . . . . 27
Fenwick tree . . . . . . . . . . . . . . . . . . . . . . . . 94
Kahn’s algorithm . . . . . . . . . . . . . . . . . . . 62
Fibonacci sequence . . . . . . . . . . . . . . . . . .13
knapsack problem . . . . . . . . . . . . . . . . . . . 19
First In First Out . . . . . . . . . . . . . . . . . . . 35
Knuth-Morris-Pratt algorithm . . . . . 123
Floyd’s algorithm . . . . . . . . . . . . . . . . . . . 45
Kosaraju’s algorithm . . . . . . . . . . . . . . . . 72
Floyd-Warshall algorithm . . . . . . . . . . . 67
Kruskal’s algorithm . . . . . . . . . . . . . . . . . 70
G
graph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 L
acyclic . . . . . . . . . . . . . . . . . . . . . . . . . 55 Last In First Out . . . . . . . . . . . . . . . . . . . 32
bipartite . . . . . . . . . . . . . . . . . . . . . . . 55 lexicographic ordering . . . . . . . . . . . . . . . 11
complete . . . . . . . . . . . . . . . . . . . . . . . 55 linear search . . . . . . . . . . . . . . . . . . . . . . . 115
connected . . . . . . . . . . . . . . . . . . . . . . 55 linked list
cyclic . . . . . . . . . . . . . . . . . . . . . . . . . . 55 doubly . . . . . . . . . . . . . . . . . . . . . . . . . 49
directed . . . . . . . . . . . . . . . . . . . . . . . . 53 singly . . . . . . . . . . . . . . . . . . . . . . . . . . 45
undirected . . . . . . . . . . . . . . . . . . . . . 53 load factor . . . . . . . . . . . . . . . . . . . . . . . . . . 39
greedy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 lowest common ancestor . . . . . . . . . . . . 77
LRU cache . . . . . . . . . . . . . . . . . . . . . . . . . . 50
H
handshaking lemma . . . . . . . . . . . . . . . . . 54 M
hash master theorem . . . . . . . . . . . . . . . . . . . . . . 8
collision . . . . . . . . . . . . . . . . . . . . . . . . 38 maximum subarray sum . . . . . . . . . . . . .28
function . . . . . . . . . . . . . . . . . . . . . . . .37 mean . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
map . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 median . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
median of sorted arrays . . . . . . . . . . . . 120
table . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
memoization . . . . . . . . . . . . . . . . . . . . . . . . . 2
value . . . . . . . . . . . . . . . . . . . . . . . . . . .37
merge intervals problem . . . . . . . . . . . . . 28
heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
merge sort . . . . . . . . . . . . . . . . . . . . . . . . . 107
max- . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
minimum spanning tree . . . . . . . . . . . . . 69
min- . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
mode . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
heap sort . . . . . . . . . . . . . . . . . . . . . . . . . . 108
modulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
heapify
monotonic stack . . . . . . . . . . . . . . . . . . . . 34
down . . . . . . . . . . . . . . . . . . . . . . . . . . 81
up . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Held-Karp algorithm . . . . . . . . . . . . . . . . 17 N
N -ary tree . . . . . . . . . . . . . . . . . . . . . . . . . . 87
I N -Queens problem . . . . . . . . . . . . . . . . . . 22
insertion sort . . . . . . . . . . . . . . . . . . . . . . 103 node
integer overflow . . . . . . . . . . . . . . . . . . . . . 16 depth . . . . . . . . . . . . . . . . . . . . . . . . . . 76
integer representation . . . . . . . . . . . . . . . 13 distance . . . . . . . . . . . . . . . . . . . . . . . .77
iteration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 height . . . . . . . . . . . . . . . . . . . . . . . . . .77
for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 number of islands . . . . . . . . . . . . . . . . . . . 58

132
Super Study Guide Index

P self-balancing tree . . . . . . . . . . . . . . . . . . .90


Pascal serialization . . . . . . . . . . . . . . . . . . . . . . . . . 78
triangle . . . . . . . . . . . . . . . . . . . . . . . . 10 sliding window trick . . . . . . . . . . . . . . . . .30
permutation . . . . . . . . . . . . . . . . . . . . . . . . 11 sorting algorithm . . . . . . . . . . . . . . . . . . 101
pigeonhole principle . . . . . . . . . . . . . . . . . 10 stability . . . . . . . . . . . . . . . . . . . . . . 101
pivot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 spanning tree . . . . . . . . . . . . . . . . . . . . . . . 69
median of three . . . . . . . . . . . . . . . 111 stack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
random choice . . . . . . . . . . . . . . . . 111 pop . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
prefix sum array . . . . . . . . . . . . . . . . . . . . 93 push . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
prefix tree . . . . . . . . . . . . . . . . . . . . . . . . . . 88 stack overflow . . . . . . . . . . . . . . . . . . . . . . . . 2
prefix-suffix array . . . . . . . . . . . . . . . . . . 123 string . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Prim’s algorithm . . . . . . . . . . . . . . . . . . . . 69 string pattern matching . . . . . . . . . . . . 122
problem complexity strongly connected component . . . . . . 71
NP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8
NP-complete . . . . . . . . . . . . . . . . . . . . 9 T
NP-hard . . . . . . . . . . . . . . . . . . . . . . . . 9 topological sort . . . . . . . . . . . . . . . . . . . . . 62
P ............................... 8 trapping rain water . . . . . . . . . . . . . . . . 116
traveling salesman problem . . . . . . . . . 16
Q tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
queue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 child . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
dequeue . . . . . . . . . . . . . . . . . . . . . . . . 35 grandparent . . . . . . . . . . . . . . . . . . . . 76
enqueue . . . . . . . . . . . . . . . . . . . . . . . . 35 parent . . . . . . . . . . . . . . . . . . . . . . . . . 76
quick sort . . . . . . . . . . . . . . . . . . . . . . . . . . 110 sibling . . . . . . . . . . . . . . . . . . . . . . . . . 76
uncle . . . . . . . . . . . . . . . . . . . . . . . . . . 76
R tree rotation . . . . . . . . . . . . . . . . . . . . . . . . 90
Rabin-Karp algorithm . . . . . . . . . . . . . 126 left . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
radix sort . . . . . . . . . . . . . . . . . . . . . . . . . . 113 right . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
range query . . . . . . . . . . . . . . . . . . . . . . . . . 92 tree traversal . . . . . . . . . . . . . . . . . . . . . . . 79
recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 in-order . . . . . . . . . . . . . . . . . . . . . . . . 79
red-black tree . . . . . . . . . . . . . . . . . . . . . . . 91 post-order . . . . . . . . . . . . . . . . . . . . . 79
black-height . . . . . . . . . . . . . . . . . . . . 92 pre-order . . . . . . . . . . . . . . . . . . . . . . .79
robot room cleaner . . . . . . . . . . . . . . . . . . 60 trie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
two-pointer technique . . . . . . . . . . . . . . 115
S
segment tree . . . . . . . . . . . . . . . . . . . . . . . . 97 V
selection sort . . . . . . . . . . . . . . . . . . . . . . 104 vertex . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

133
Super Study Guide Authors

About the authors


Afshine and Shervine are twin brothers of Persian origin. They were born and
raised in Paris, France, came to the US for grad school and are currently working in
the tech industry in the San Francisco Bay Area. They have a passion for creating
easy-to-digest material on computer science-related topics.

You can find a more detailed individual bio for each of them below.

Afshine Amidi is currently working on solving natural lan-


guage processing problems at Google. He also teaches the Data
Science Tools class to graduate students at MIT as well as a deep
learning workshop at Stanford. Previously, he worked in applied
machine learning for recommender systems at Uber Eats where
he focused on building ranking models that improve the quality
of the overall search results by taking into account several objective functions. Also,
Afshine published a few papers at the intersection of deep learning and computa-
tional biology. He holds a Bachelor’s and a Master’s Degree from École Centrale
Paris and a Master’s Degree from MIT.

You can learn more about Afshine at www.mit.edu/~amidi

Shervine Amidi is currently working on problems at the in-


tersection of ranking and natural language processing at Google.
He also teaches a deep learning workshop at Stanford. Previ-
ously, he worked in applied machine learning for recommender
systems at Uber Eats where he focused on representation learn-
ing to better surface dish recommendations. Also, Shervine published a few papers
at the intersection of deep learning and computational biology. He holds a Bache-
lor’s and a Master’s Degree from École Centrale Paris and a Master’s Degree from
Stanford University.

You can learn more about Shervine at www.stanford.edu/~shervine

134

You might also like