algorithms Flashcards

1
Q

Implement Insertion sort

A

for(int i = 1; i < arr.length; i++){
int key = arr[i];
int j = i - 1;

while(j >=0 && arr[j]>key){
arr[j+1] = arr[j];
j = j-1;
}
arr[j+1] = key;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

When is best to use Insertion sort?

A

When array is almost sorted and/or when there are not many elements

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

Implement merge in merge sort.

A

public void merge(int[] numbers, int start, int cut, int end){
int leftSize = (cut-start)+1;
int rightSize = end-cut;

int[] left = new int[leftSize]; 
int[] right = new int[rightSize];

for(int i = 0; i < leftSize; i++){
	left[i] = numbers[start+i];
}

for(int i = 0; i < rightSize; i++){
	right[i] = numbers[(cut+1)+i];
}

int leftIndex = 0;
int rightIndex = 0;

int k = start;
	while(leftIndex < leftSize && rightIndex < rightSize){
		if(left[leftIndex] <= right[rightIndex]){
			numbers[k] = left[leftIndex];
			leftIndex++;
		}else{
			numbers[k] = right[rightIndex];
			rightIndex++;
		}
		k++;
	}
	while(leftIndex < leftSize){
		numbers[k] = left[leftIndex];
		leftIndex++;
		k++;
	}
	while(rightIndex < rightSize){
		numbers[k] = right[rightIndex];
		rightIndex++;
		k++;
	}

}

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

Implement the sort function of merge sort.

A
void sort(int arr[], int l, int r)
    {
        if (l < r) {
            // Find the middle point
            int m =l+ (r-l)/2;
        // Sort first and second halves
        sort(arr, l, m);
        sort(arr, m + 1, r);
            // Merge the sorted halves
            merge(arr, l, m, r);
        }
    }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

How does the Rabin-Karp Algorithm work? (describe it)

A

The Rabin-Karp Algorithm is one of the string pattern searching algorithms.
It works similarly to a brute force algorithm (checking the text for the pattern in each position) but uses a rolling hash instead, so the runtime is much better. The rehashing calculation of course has to be really efficient, it should take constant time.

You could implement it by using these steps:

  • calculate hash for pattern
  • calculate hash from position 0 until the length of the pattern
  • check if hash value is the same, if so, check if characters are the same, if so return
  • if there wasn’t a match so far calculate the next hash, be subtracting the hash value of the first character and add the value of the now new last character
  • check if the two hash values are the same, if so check if characters are the same, if so return
  • and so on, until the end of the text

When calculating hash:

  • we need to keep the collisions low
  • we need a solution to not use too big numbers
  • make sure that each pattern’s hash value depends on not just the characters but the characters position too. So ‘asd’ won’t be the same as ‘das’.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Give an example how to calculating hash for the next position in a Rabin-Karp algorithm.

A

int prime = any prime number
int d = 10^pattern.length-1
int remove = oldHash - ((OldFirstCharValued) mod prime)
if remove is negative –> remove += prime;
newHash = ((remove
10) mod prime+newCharValue) mod prime

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

Describe the runner technique and some useful applications of it.

A

You iterate over the linked list with two pointers, one is moving faster than the other.

For example if you want to find the middle point of a linked list, you can have a slower pointer which incremented by one each time, and a faster which incremented by two. When the faster reaches the end you have to be in the middle point with the slower one. (if the list has odd elements it is the middle, but if it’s even you are at the two middle points.)

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

Implement in-order binary tree traversal

A
void inOrderTraversal(TreeNode node){
if (node != null){
inOrderTraversal(node.left);
visit(node);
inOrderTraversal(node.right);
}
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Implement a pre-order binary tree traversal.

A
void preOrderTraversal(TreeNode node){
if (node != null){
visit(node);
preOrderTraversal(node.left);
preOrderTraversal(node.right);
}
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Implement a post-order binary tree traversal

A
void postOrderTraversal(TreeNode node){
if (node != null){
postOrderTraversal(node.left);
postOrderTraversal(node.right);
visit(node);
}
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Describe how a new node is inserted in a binary search tree?

A
  • check if tree is empty, if it is add new node as root
  • otherwise start from root and compare it with the new node
  • if new node is smaller, go left, otherwise go right
  • when reached end insert node left if smaller or right if greater
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Implement adding a new node to a binary search tree

A
void insert(Node n){
	insert(root,n);
}
void insert(Node root, Node newNode){
	if(root == null){
		root = newNode;
	}
	if(newNode < root){
		insert(root.left,newNode);
	}else{
		insert(root.right,newNode);
	}

}

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

How the deletion of a node works in a binary search tree?

A

if it’s a leaf node just delete it

if it has one child just swap it with child and delete it

if it has two children swap it with it’s in-order successor and delete it

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

Name the two most common ways to search a graph and explain them.

A

Depth-first search (DFS): We explore each branch completely before moving on to the next branch. Going deep before going wide.

Breadth-first search (BFS): Explore each neighbor before going on to any children. We go wide before we go deep.

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

What is bubble sort?

A

In bubble sort, we start at the beginning of the array and swap the first two elements if the first is greater than the second. Then, we go to the next pair, and so on, continuously making sweeps of the array until it is sorted. In doing so, the smaller items slowly “bubble” up to the beginning of the list.

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

Implement bubble sort.

A
void bubbleSort(int arr[])
    {
        int n = arr.length;
        for (int i = 0; i < n-1; i++)
            for (int j = 0; j < n-i-1; j++)
                if (arr[j] > arr[j+1])
                {
                    // swap arr[j+1] and arr[j]
                    int temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
    }
17
Q

What is selection sort?

A

Find the smallest element using a linear scan and move it to the front (swapping it with the front element). Then, find the second smallest and move it, again doing a linear scan. Continue doing this until all the elements are in place

18
Q

Implement selection sort.

A
void sort(int arr[])
    {
        int n = arr.length;
        // One by one move boundary of unsorted subarray
        for (int i = 0; i < n-1; i++)
        {
            // Find the minimum element in unsorted array
            int min_idx = i;
            for (int j = i+1; j < n; j++)
                if (arr[j] < arr[min_idx])
                    min_idx = j;
            // Swap the found minimum element with the first
            // element
            int temp = arr[min_idx];
            arr[min_idx] = arr[i];
            arr[i] = temp;
        }
    }
19
Q

What is quick sort?

A

QuickSort is a Divide and Conquer algorithm. It picks an element as pivot and partitions the given array around the picked pivot.

There are many different versions of quickSort that pick pivot in different ways.

  • Always pick first element as pivot.
  • Always pick last element as pivot
  • Pick a random element as pivot.
  • Pick median as pivot.

The key process in quickSort is partition(). Target of partitions is, given an array and an element x of array as pivot, put x at its correct position in sorted array and put all smaller elements (smaller than x) before x, and put all greater elements (greater than x) after x.

20
Q

Implement quickSort’s partition method. Pivot should be the last element.

A

static int partition(int[] arr, int low, int high)
{

int pivot = arr[high];

int i = (low - 1);

for(int j = low; j <= high - 1; j++)
{

    if (arr[j] < pivot)
    {
            i++;
            swap(arr, i, j);
        }
    }
    swap(arr, i + 1, high);
    return (i + 1);
}
static void swap(int[] arr, int i, int j)
{
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
21
Q

What is Heap sort?

A

Heap sort is a comparison-based sorting technique based on Binary Heap data structure. It is similar to selection sort where we first find the minimum element and place the minimum element at the beginning. We repeat the same process for the remaining elements.

22
Q

How counting sort works? Explain with example input input: 1,4,1,2,7,5,2.

A
  1. ) count in a separate array how many times each object is present in the input.
  2. ) change the count, by adding the count of the previous object to the current object
  3. ) go through input and check the corresponding index in the counting array.
  4. ) remove one from that count on the index and write input value to the position of the remaining count
  5. ) do this for the whole input

Example:
input: 1,4,1,2,7,5,2
counting array initialized: 0,0,0,0,0,0,0,0
counting array with counts: 0,2,2,0,1,1,0,1
changing counting (adding previous): 0,2,4,4,5,6,6,7

forming output:
input: 1 –> 1st element of counting array: 2 –> remove one (2-1=1) –> put 1 at index 1
changed counting array: 0,1,4,4,5,6,6,7
output: 0,1,0,0,0,0,0

input: 4 –> 4th element of counting array: 5 –> remove one (5-1=4) –> put 4 at index 4
changed counting array: 0,1,4,4,4,6,6,7
output: 0,1,0,0,4,0,0

input: 1 –> 1st element of counting array: 1 –> remove one (1-1=0) –> put 1 at index 0
changed counting array: 0,0,4,4,4,6,6,7
output: 1,1,0,0,4,0,0

input: 2 –> 2nd element of counting array: 4 –> remove one (4-1=3) –> put 2 at index 3
changed counting array: 0,0,3,4,4,6,6,7
output: 1,1,0,2,4,0,0

input: 7 –> 7th element of counting array: 7 –> remove one (7-1=6) –> put 7 at index 6
changed counting array: 0,0,3,4,4,6,6,6
output: 1,1,0,2,4,0,7

input: 5 –> 5th element of counting array: 6 –> remove one (6-1=5) –> put 5 at index 5
changed counting array: 0,0,3,4,4,6,5,6
output: 1,1,0,2,4,5,7

input: 2 –> 2th element of counting array: 3 –> remove one (3-1=2) –> put 2 at index 2
changed counting array: 0,0,2,4,4,6,5,6
output: 1,1,2,2,4,5,7

23
Q

When is counting sort efficient?

A

Counting sort is efficient if the range of input data is not significantly greater than the number of objects to be sorted. Consider the situation where the input sequence is between range 1 to 10K and the data is 10, 5, 10K, 5K.

24
Q

Explain how radix sort works.

A

we iterate through each digit of the number, grouping numbers by each digit. t. For example, if we have an array of integers, we might first sort by the first digit, so that the 0s are grouped together. Then, we sort each of these groupings by the next digit. We
repeat this process sorting by each subsequent digit, until finally the whole array is sorted.

25
Q

Explain Binary search.

A

Take a sorted array, divide it in the middle. Check if middle is smaller or greater than the searched item. If lower search the left half if greater search the right half, and so on.

26
Q

Implement binary search recursively.

A
int binarySearch(int arr[], int start, int end, int searchedKey)
    {
        if (end >= start) {
            int mid = start + (end - start) / 2;
        if (arr[mid] == searchedKey){
            return mid;
        }
            if (arr[mid] > searchedKey){
                return binarySearch(arr, start, mid - 1, searchedKey);
            }
            return binarySearch(arr, mid + 1, end, searchedKey);
        }
    return -1;
}
27
Q

Why you would or wouldn’t write an algorithm recursively instead iteratively?

A

Recursive algorithms can be very space inefficient. Each recursive call adds a new layer to the stack, which means that if your algorithm recurses to a depth of n, it uses at least O ( n) memory. For this reason, it’s often better to implement a recursive algorithm iteratively. All recursive algorithms can be implemented iteratively, although sometimes the code to do so is much more complex.