algos1 Flashcards

1
Q

What is the complexity of quicksort?

A

O(n log n) in the average case, O(n^2) in the worst case

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Why is quicksort O(n^2) in the worst case?

A

If the input is already sorted and you pick the first item as the pivot every time when you are partitioning, the depth of the call stack will be the same as the number of elements in the list (as you are not really dividing them).. Every time you partition you are going through each element (O(n)), but in the worst case you are partitioning more often before hitting the base case. N.b. in the average case the depth of the stack is log n as you are halving the list each time.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is a hash table?

A

A data structure which maps keys to values. When given an input key, this is hashed to give the index of the value, which can then be accessed in O(1) time.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

What can go wrong with a hash table?

A

Collisions. If the hashing algo is not good then many different inputs can get the same index from the hash (collisions). Can use a linked list at that index for the multiple values, but then are forced to search through the linked list in O(n) time to find something

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What are some use cases for hash tables?

A

Preventing duplicates; caching; any time you have to map a key to a value.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

What is a hash table’s load factor?

A

The number of elements / total number of slots in hash table. A load factor of 1 means there are the same number of elements as slots and there is likelihood of collision. To avoid this should expand hash table at load factor of 0.7.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Describe binary search

A

Given a sorted list and an element e to search for. Get the midpoint of the list and check if e is there. If not, if element in mid is larger than e, reduce the search to the subarray below mid. If element in mid is smaller than e, reduce the search to subarray above mid. This means that every time you don’t find e you are halving the array. Which makes the complexity O(log n).

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Describe quicksort

A

Given a list to sort. Partition the list by picking an element and putting everything smaller to its left and everything larger to its right. Then take the subarray to the left and do the same again, and the subarray to the right and do the same again (recursively call quicksort on the subarrays). Eventually will reach base case where the start of the array is the same as the end of the array, so can return. Once all recursive calls return the array is sorted.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

What is breadth first search?

A

Searching a graph by searching connected nodes, then their connections, then the connections of their connections

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What is a graph?

A

An abstract data type made up of nodes and edges connecting those nodes. Represents a network.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

How can you implement a graph?

A
One way is to use a hash table / dictionary. e.g.
{
"bob": ["alice", "claire", "dave"],
"alice: ["dave", "edgar"]
"claire": ["alice"],
etc...
}
In the above, the names are nodes. The list mapped to bob contains bob's neighbours i.e. the nodes bob is connected to.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

What is the complexity of breadth first search?

A

You may be searching your entire network, following each edge. So it is at least O(number of edges). And you are also adding everybody to the queue, which is O(number of vertices). So total is O(V+E)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What are some ways of representing graphs?

A

Edge lists:
Array of [vertex1, vertex2] objects.
e.g. [ [1, 5], [1, 2], [3, 5] ]

Adjacency matrices
[ [0, 1, 0, 0, 0, 0, 1, 0, 1, 0],
  [1, 0, 0, 0, 1, 0, 1, 0, 0, 1],
  [0, 0, 0, 0, 1, 0, 1, 0, 0, 0],
  [0, 0, 0, 0, 1, 1, 0, 0, 1, 0],
  [0, 1, 1, 1, 0, 1, 0, 0, 0, 1],
  [0, 0, 0, 1, 1, 0, 0, 0, 0, 0],
  [1, 1, 1, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
  [1, 0, 0, 1, 0, 0, 0, 1, 0, 0],
  [0, 1, 0, 0, 1, 0, 0, 1, 0, 0] ]
Adjacency lists
[ [1, 6, 8],
  [0, 4, 6, 9],
  [4, 6],
  [4, 5, 8],
  [1, 2, 3, 5, 9],
  [3, 4],
  [0, 1, 2],
  [8, 9],
  [0, 3, 7],
  [1, 4, 7] ]
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

What is time for searching for a given edge in different representations of graphs?

A

edge list: O(E)
adjacency matrix: O(1)
adjacency list: O(degree)

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Pseudo code breadth first search

A

Create a graph (either adjacency matrix, adjacency list or edge list, or as a hashtable)
Create a queue.

Add source node to queue.
while queue is not empty:
  dequeue next node
  get node's neighbours from graph.
  for each neighbour that has not been visited:
    mark as visited
    check if goal / set predecessor and distance
    add to queue

So will visit source, then nodes adjacent to source, then nodes adjacent to those nodes etc.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Pseudo code Dijkstra’s algorithm

A

The goal of DA is to find the shortest path between two nodes in a WEIGHTED, directed (?) graph.

Create a graph (e.g. hashtable mapping nodes to neighbours and the weight between them)
Create a hashtable mappping nodes to cost - i.e the cost to get to that node from the start node.
Create a hashtable mapping nodes to predecessor - their pred on the path from start to finish.

Initialise Costs and Preds for the neighbours of the start node, to contain cost = weight from start to that node and pred = the start node. The rest have cost = infinity and pred = null

Main algo:
Get lowest cost node (LCN)
While lowest cost node != null
For neighbours of lowest cost node:
cost = current cost of neighbour (Costs[n])
new_cost = weight between LCN and neighbour + cost
of LCN
if new_cost < cost, we have found a shorter path to that
node, so make cost of neighbour be new_cost
set Preds[neighbour] to LCN
Add LCN to processed
Get lowest cost node

return Costs, Preds

For finding lowest cost node:
loop through Costs to get node with current lowest cost

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
17
Q

Pseudo code depth first search

A
DFS works by picking a source node's child, visiting its child, then the child of its child etc. Can be implemented recursively like this:
dfs(source, graph):
 for each neighbour of source in graph:
   if neighbour is not visited:
     mark neighbour as visited
     dfs(neighbour, graph)

With the recursion we are using the call stack to go deep, then backtrack back and go deep on another neighbour.

Can also be implemented iteratively like this:
dfs_iterative(source, graph):
  mark source as visited
  add source to stack
  while stack is not empty:
    pop node off stack
    for each neighbour of node in graph:
      mark node as visited
      push neighbour on to stack
18
Q

When do you use breadth first search and when do you do depth first search?

A

BFS is useful for finding shortest path. It looks at nodes 1 level deep, then 2 levels deep, etc. So it will find the shortest path to a target node.
DFS is useful for finding if a path exists.
It will sometimes depend on the structure of the graph whether one is more useful than another.
https://stackoverflow.com/questions/3332947/when-is-it-practical-to-use-depth-first-search-dfs-vs-breadth-first-search-bf

19
Q

Example of a greedy algorithm

A
Scheduling classroom classes when class times overlap.
Pick the class that finishes earliest. Then pick the class that starts after that class finishes and finishes earliest. And repeat.
20
Q

Pseudo code partitioning for quicksort

A

Input is array, start index, end index.
Pick pivot - e.g. last element in array.
Set 2 array pointers (follower, leader) to same as start.
While leader <= end:
Check element at array[follower]
If elem is larger than pivot, do nothing.
If it is smaller, swap element at array[follower] with array[leader]. Increment follower.
Increment leader.
End up with follower at index of the last thing that was larger than pivot, so swap elem at pivot with elem in follower.
e.g.
[2, 6, 3, 5, 4]

21
Q

What is an NP complete problem?

A

Problems with no known fast solutions. In this context, NP stands for “nondeterministic polynomial time”.
Can use approximation algorithm (e.g. greedy) to find approximate solution.

22
Q

What is a greedy algorithm?

A
An algorithm which takes the locally optimal solution each time, with the aim to get the ultimate optimal solution. E.g. for knapsack problem, take the most expensive item that fits in bag, then take second most expensive item which fits in bag, etc.
Or for scheduling - take the class which finishes earliest, then take the class that starts after that which finishes earliest etc.
23
Q

What is big O complexity?

A

todo
include this https://medium.com/@.RT/total-n00bs-guide-to-big-o-big-%CF%89-big-%CE%B8-aa259ae8a1c2

Asymptoptic/complexity analysis in general: a way of describing the performance of an algorithm, more specifically a measure of how quickly the algorithm will scale with its input. The quicker it scales/grows, the slower it gets with bigger input.

Big O: Big O, in strict academic terms, is the upper bound of a given algorithm. You can say e.g. that a function 4n is Big O of n^2. But also that 4n is Big O of n^3.
The general definition is that a function f(n) is Big O of g(n) if there is a constant M such that after a given value of n the following is always true:
f(n) <= M * g(n)
Essentially that g(n), from some n onwards, will always be greater than f(n).

Big Omega: Is the opposite of Big O. It is the lower bound of a function.
f(n) >= M * g(n)

Big Theta: A function f(n) is Big Theta of g(n) if there is a given constant M for which f(n) is both Big O AND Big Omega of g(n). This means that our algo is tightly bound by g(n).

N.B. when people talk about Big O, they generally mean Big Theta.

24
Q

What is dynamic programming? What is top-down and what is bottom-down approach?

A

A means of solving problems in such a way that breaks them down into subproblems and memoizes the results of the subproblems so they can be combined to solve the actual problem.

Top-down - Formulate recursive solution, store solutions to subproblems. Whenever attempting to solve a subproblem, check the table to see if already solved.
Bottom-up - Solve the sub-problems first and build on them as you go.

25
Q

Pseudo code DP fibonacci function

A

Similar to non DP recursive fibonacci but store solutions to sub-problems.
fib(n):
Have a map M tracking results for each n.
if n is smaller or equal to 1, return n.
Otherwise check if n is in M. If it is not, add it to M as the result of fib(n-1) + fib(n-2).
Return M[n].

Because we are storing results for each n in map, we are only calculating them once, and thereafter using the value from M.

26
Q

What are the 2 key attributes a problem must have to be solvable by DP?

A
  1. Optimal substructure - you must be able to obtain the optimal solution to problem by combining optimal solutions to sub-problems.
  2. Overlapping sub-problems (same sub-problems get solved multiple times - think non-DP recursive fibonacci). This property is missing from e.g. merge sort or quick sort which is why they are not DP.
27
Q

What is an array?

A

Data structure which stores items in memory contiguously.

28
Q

What is time complexity of inserting and deleting from array and why?

A

O(n) (linear time), because if you want to insert at a certain index you have to shift the other elements. However just inserting at the end is O(1).

29
Q

What is time complexity of reading from array?

A

O(1) (constant time). Elements are stored contiguously. Accessing an array e.g. arr[3] you can go straight to that location. So it is constant time.

30
Q

What is a linked list?

A

A linked list is a linear collection of data elements whose order is not given by their physical placement in memory (as opposed to an array). Instead, each element has a reference to the location of the next element in the list.

31
Q

What is time complexity of reading specific item in linked list? (e.g. 10th element)

A

O(n) - have to traverse the list, jumping from element to element until you get to your desired element

32
Q

What is time complexity of inserting into linked list (somewhere not at the start)

A

Actual insertion is O(1) - just repointing references. But to get to the place where you want to insert you have to traverse the list, which is O(n).

33
Q

Arrays vs Linked Lists

A

Arrays are fast access and can be accessed randomly. Insertion into LLs can be faster and LLs are dynamic and can add as many elements as you want without having to resize.

34
Q

Pseudo code selection sort

A

Selection sort by repeatedly finding the smallest element in the subarray left to sort and putting it in the leftmost position

Given a list input
Starting at index 0, find the smallest element in list, put in index 0.
Increase index by 1 and repeat.

35
Q

Pseudo code insertion sort

A

Insertion sort works by taking the element to the right of the sorted subarray and placing it in the correct place within the subarray.

Given an array input
At the start, sorted subarray will be arr[0]
Take elem at arr[1] and move to the left of everything it is smaller than
Increment index and repeat

36
Q

Pseudo code bubble sort

A

Bubble sort works by comparing each pair in array and putting them in order. Bigger elements will bubble up to the right of the array.

Given array input
For i from 0 to len(arr)
For j from 0 to len(arr)-i
Compare elements at j and j+1, swap if j is bigger

37
Q

Pseudo code merge sort

A

Given a list of items, a left and right index:
if left >= right return
If not, find the middle index by subtracting left from right and dividing by 2
Call mergesort(items, left, mid)
Call mergesort(items, mid+1, right)
Call merge(items, left, mid, right)

Merge takes list and a left, midpoint and right indices.
Create 2 new lists, left and right.
Put everything up to middle in L, everything after middle in R.
Keep 3 pointers - i for the main list, j for left list, k for right list.
While j <= L.length and k <= R.length:
compare L[j] and R[k], put smallest in main[i].
increment i and increment whichever of j and k was smallest.
Once out of the while loop, loop through remainder of items in L and R and add to main[i], i++ etc. Merge complete.

38
Q

Pseudo code insert into binary search tree

A

To insert element e
Current = root;
If e < current:
if root.left is null, insert at root.left, otherwise current=root.left
if e > current, do the same but to the right

RECURSIVE:
to insert element e
insert(root, e)
if root is null, root = e
else if e < root, if root.left is null, insert at root.left. Otherwise call insert(root.left, e).
If e > root, do same for right.
39
Q

Pseudo code delete from binary search tree

A

What you need to do depends on whether node has children or not.
No kids: simply remove node
1 kid: Set value of node to kid’s value, delete kid
2 kids: Set value of node to value of leftmost child in right subtree, remove that child

40
Q

What is a binary search tree?

A

An ADT made up of nodes, each having up to 2 children, and ordered such that a child that is smaller is to the left of its parent and a child that is greater is to the right of its. Therefore for a given root node we know that all nodes smaller than it are in the left subtree and all the nodes greater than it are in the right subtree.