https://www.udemy.com/course/master-the-coding-interview-data-structures-algorithms/

Getting more Interviews

Resume

Use this online resume maker https://www.resumemaker.online/

Keypoint on having a nice resume - Should be One page, Relevant Skills (spesific job - spesific skills), Personalized ( dont use the same resume for every, customize the name  ) , Online Link (github, website, portfolio)

LinkedIn

Portfolio

Email

BIG O'S

Algoritmic efficiency

Cheatsheet Big O

O(1) Constant - no loops
O(log N) Logarithmic - usually searching algorithms have log n if they are sorted (Binary Search)
O(n) Linear - for loops, while loops through n items
O(n log(n)) Log Linear - usually sorting operations
O(n^2) Quadratic - every element in a collection needs to be compared to ever other element. Two nested loops
O(2^n) Exponential - recursive algorithms that solves a problem of size N
O(n!) Factorial - you are adding a loop for every element

Iterating through half a collection is still O(n)
Two separate collections: O(a * b)

WHAT CAN CAUSE TIME IN A FUNCTION?

  • Operations (+,-, \*, /)
  • Comparisons (<, >, ===)
  • Looping (for, while)
  • Outside Function call (function())

WHAT CAUSES SPACE COMPLEXITY?

  • Variables
  • Data Structures
  • Function Call
  • Allocations

O(n) - linear time

Linear BigO
  • O(n) -> linear time
    depends on number of input -> n
// Finding how long it takes to run in milliseconds

const {performance} = require('perf_hooks');
const nemo = ['nemo'];
const everyone = ['dory', 'bruce', 'marlin', 'nemo', 'gill', 'bloat', 'nigel', 'squirt', 'darla','hank'];
const large = new Array(1000).fill('nemo'); // will create 1000 nemo in array

function findNemo(array) {
  let t0 = performance.now();
  for (let i = 0; i < array.length; i++) {
   if (array[i] === 'nemo')  {
     console.log('Found NEMO!');
   }
  }
  let t1 = performance.now();
  console.log('Call to find Nemo took ' + (t1-t0) + ' milliseconds');
}

findNemo(large); // 0(n) --> Linear Time

O(1) - constant time

  • no matter how many time the boxes increase, we're always just grabbing the first item in array
const boxes = [0,1,2,3,4,5];
function logFirstTwoBoxes(boxes) {
  console.log(boxes[0]); // O(1)
  console.log(boxes[1]); // O(1)
}

logFirstTwoBoxes(boxes); // O(2)

Exercise 1

// What is the Big O of the below function? (Hint, you may want to go line by line)
function funChallenge(input) {
  let a = 10; // O(1)
  a = 50 + 3; // O(1)

  for (let i = 0; i < input.length; i++) { // O(n)
    anotherFunction(); // O(n)
    let stranger = true; // O(n)
    a++; O(n)
  }
  return a; // O(1)
}

BIG O(3 + 4n) simplify to O(n)

Exercise 2

// What is the Big O of the below function? (Hint, you may want to go line by line)
function anotherFunChallenge(input) {
  let a = 5; O(1)
  let b = 10; O(1)
  let c = 50; O(1)
  for (let i = 0; i < input; i++) { O(n)
    let x = i + 1; O(n)
    let y = i + 2; O(n)
    let z = i + 3; O(n)
  }
  for (let j = 0; j < input; j++) { O(n)
    let p = j * 2; O(n)
    let q = j * 2; O(n)
  }
  let whoAmI = "I don't know"; O(1)
}

O(4 + 7n)

RULE BOOK

Rule 1: Always worst Case

The worst case is that the input, instead of being the fourth item is at the very end. So even if we have this brake statement, we're still going to run this 10 times because we're still going to have to go through 10th item.

Rule 2: Remove Constants

function printFirstItemThenFirstHalfThenSayHi100Times(items) {
    console.log(items[0]); // O(1)

    var middleIndex = Math.floor(items.length / 2);
    var index = 0;

    while (index < middleIndex) {
        console.log(items[index]);
        index++;
    }

    for (var i = 0; i < 100; i++) {
        console.log('hi');
    }
}

// O(1 + n/2 + 100)
// removing 1 and /2
// O(n + 1)
// O(n)


Rule 3:

function compressBoxesTwice(boxes, boxes2) {
  boxes.forEach(function(boxes)) {
    console.log(boxes);
  }

  boxes2.forEach(function(boxes2)) {
    console.log(boxes2);
  }
}

//O(a + b)
  • Different inputs should have different variables: O(a + b).
  • A and B arrays nested would be: O(a * b)

+ for steps in order

* for nested steps

const boxes = ['a','b','c','d','e'];
function logAllPairsOfArray(array) {
  for(let i = 0; i< array.length; i++) {
    for(let j = 0; j < array.length; j++) {
      console.log(array[i], array[j])
    }
  }
}

logAllPairsOfArray(boxes);

//O(n^2)

Rule 4: Drop Non-dominant terms

function printAllNumbersThenAllPairSums(numbers) {

  console.log('these are the numbers:');
  numbers.forEach(function(number) {
    console.log(number);
  });

  console.log('and these are their sums:');
  numbers.forEach(function(firstNumber) {
    numbers.forEach(function(secondNumber) {
      console.log(firstNumber + secondNumber);
    });
  });
}

printAllNumbersThenAllPairSums([1,2,3,4,5])

// O( n + n^2) // remove n because n^2 is more important
// O( n^2)
How would you simplify this ?
O( x^2 + 3x + 100 + x/2 )
example if x = 5
x^2 = 25 3x = 15 100 x/2 = 2.5 -> 100 is relevant
but when x = 500 is not relevant anymore
to simplify it 
O(x^2)

What does it all means

Data structures are simply ways to store data, Algorithm are functions of ways to use data structures to make a program.

Data Structure + Algorithm = Program

https://www.bigocheatsheet.com/

O(n!)

Most expensive, some say Oh no...
We're adding a nested loop for every input that we have.

3 Pillars of programming

What is good code?

  1. Readable
  2. Scalable - Speed ( how much time does it take a function to run, etc) - Memory ( ram )

Which code is best?

  1. readable
  2. Memory - space complexity (what's the memory usage of code)
  3. Speed - time complexity

In programming usually there's a trade off, if you want speed you might have to sacrifice memory and vice versa.

Space Complexity

what causes space complexity?

  • variables
  • data structure
  • function call
  • allocations
function booooo(n) {
  for (let i = 0; i < n.length; i++) {
    console.log('boooooo');
  }
}
booooo([1,2,3,4,5]);

// time O(n)
// space O(1)

Another way of loops

const findNemo2 = array => {
  array.forEach(fish => {
    if (fish === 'nemo') {
       console.log('Found Nemo');
    }
  });
}
findNemo2(everyone);

const findNemo3 = array => {
  for (let fish of array) {
    if (fish === 'nemo') {
      console.log('Found Nemo');
    }
  }
}

findNemo3(everyone);

The important thing that we learn here is that Big O is about how you can scale.

Premature optimization can be a root of evil.

How to solve coding problem

  • Knowing all data structures, algorithm etc, it doesn't guarantee that you succeed in a technical interview
  • It's not the smartest interviewer that gets hired most of the time, it's the interviewer that is able o answer this fundamental question. "Will you solve the company problem?"
  • It's not necessarily about the solution to a problem in a coding interview. It's about the thought process and knowing the tradeoffs between data structures and algorithm, space and time complexity.

What are companies looking for?

  1. Analytic Skills
    How can you think through a problem and analize things, and during interview they want to hear your thought process and how you go from not knowing the answer to solve problem
  2. Coding Skills
    Is your code clean, well organize, readable
  3. Technical Skills
    Fundamentals, do you understand pro's and cons of different solutions?
  4. Communication Skills
    Can you communicate with others?

Companies are looking for people who know how to look for answers, know your data structures and algorithms. When you should use a certain data structure / algorithm over the other.

What We Need For Coding Interviews

Example of coding interview at google

Step By Step through a problem:

1. When the interviewer says the question, write down the key points at the top (i.e. sorted array). Make sure you have all the details. Show how organized you are.

2. Make sure you double check: What are the inputs? What are the outputs?

3. What is the most important value of the problem? Do you have time, and space and memory, etc.. What is the main goal?

4. Don't be annoying and ask too many questions.

5. Start with the naive/brute force approach. First thing that comes into mind. It shows that you’re able to think well and critically (you don't need to write this code, just speak about it).

6. Tell them why this approach is not the best (i.e. O(n^2) or higher, not readable, etc...)

7. Walk through your approach, comment things and see where you may be able to break things. Any repetition, bottlenecks like O(N^2), or unnecessary work? Did you use all the information the interviewer gave you? Bottleneck is the part of the code with the biggest Big O. Focus on that. Sometimes this occurs with repeated work as well.

8. Before you start coding, walk through your code and write down the steps you are going to follow.

9. Modularize your code from the very beginning. Break up your code into beautiful small pieces and add just comments if you need to.

10. Start actually writing your code now. Keep in mind that the more you prepare and understand what you need to code, the better the whiteboard will go. So never start a whiteboard interview not being sure of how things are going to work out. That is a recipe for disaster. Keep in mind: A lot of interviews ask questions that you won’t be able to fully answer on time. So think: What can I show in order to show that I can do this and I am better than other coders. Break things up in Functions (if you can’t remember a method, just make up a function and you will at least have it there. Write something, and start with the easy part.

11. Think about error checks and how you can break this code. Never make assumptions about the input. Assume people are trying to break your code and that Darth Vader is using your function. How will you safeguard it? Always check for false inputs that you don’t want. Here is a trick: Comment in the code, the checks that you want to do… write the function, then tell the interviewer that you would write tests now to make your function fail (but you won't need to actually write the tests).

12. Don’t use bad/confusing names like i and j. Write code that reads well.

13. Test your code: Check for no params, 0, undefined, null, massive arrays, async code, etc… Ask the interviewer if we can make assumption about the code. Can you make the answer return an error? Poke holes into your solution. Are you repeating yourself?

14. Finally talk to the interviewer where you would improve the code. Does it work? Are there different approaches? Is it readable? What would you google to improve? How can performance be improved? Possibly: Ask the interviewer what was the most interesting solution you have seen to this problem

15. If your interviewer is happy with the solution, the interview usually ends here. It is also common that the interviewer asks you extension questions, such as how you would handle the problem if the whole input is too large to fit into memory, or if the input arrives as a stream. This is a common follow-up question at Google, where they care a lot about scale. The answer is usually a divide-and-conquer approach — perform distributed processing of the data and only read certain chunks of the input from disk into memory, write the output back to disk and combine them later.

Roleplay 1

Interviewer gives you these question

Given 2 arrays, create a functiont that let's a user know ( true/false) whether these two arrays contain any common items

For example:
const array1 = ['a','b', 'c','x'];
const array2 = ['z','y', 'i'];
should return false.
---------------------
const array1 = ['a','b', 'c','x'];
const array2 = ['z','y', 'x'];
should return true.

What you should do is : check step by step,  So here :

1. When the interviewer says the question, write down the key points at the top (i.e. sorted array). Make sure you have all the details. Show how organized you are.

// write this down
2 parameters
return true / false

2. Make sure you double check: What are the inputs? What are the outputs? you can confirm the interviewer as well for the output can it be a number?

there are 2 arrays for input
there is 1 result for output

Update

// write this down
2 parameters - arrays
return true / false

3. What is the most important value of the problem? Do you have time, and space and memory, etc.. What is the main goal? How large is the array is going to get? if no more than 5 item we don't have to worry about BigO, time / space complexity as much.
you can ask interviewer, is our goal here to be as efficient possible with our function? whats more important? time / space complexity.
Interviewer just want the most efficient function, assuming that the array can be very large

Update

// write this down - arrays - no size limit
2 parameters - arrays
return true / false

4. Don't be annoying and ask too many questions. There usually time limit so ask only important questions.

5. Start with the naive/brute force approach. First thing that comes into mind. this look like a nested loop az ay ai etc, we know it's a big O(n^2), tell the interviewer that we know there's a easy/rough/naive solution.

6. Tell them why this approach is not the best. it maybe space or time, or maybe because it's big O (n^2)

7. Walk through your approach, comment things and see where you may be able to break things.

const array1 = ['a','b', 'c','x'];
const array2 = ['z','y', 'x'];

function containsCommonItem(arr1, arr2) {
  for (let i = 0 ; i < arr1.length; i++) {
    for (let j = 0; j < arr2.length; j++) {
      if (arr1[i] === arr2[j]) {
        return true;
      }
    }
  }
  return false;
}

containsCommonItem(array1, array2);
// O(n^2)

8. Never start a whiteboard interview not being sure of how things are going to work out. That is a recipe for disaster. Keep in mind: A lot of interviews ask questions that you won’t be able to fully answer on time. So think: What can I show in order to show that I can do this and I am better than other coders.

9. Modularize your code from the very beginning. Break up your code into beautiful small pieces and add just comments if you need to.

10. Start actually writing your code now. Keep in mind that the more you prepare and understand what you need to code, the better the whiteboard will go. So never start a whiteboard interview not being sure of how things are going to work out. That is a recipe for disaster.

const array1 = ['a','b', 'c','x'];
const array2 = ['z','y', 'a'];

function containsCommonItem2(arr1, arr2) {
  //loop through first array and create object where properties === items in the array
  let map = {};
  for (let i=0; i < arr1.length ; i++) {
    if (!map[arr1[i]]) {
      const item = arr1[i];
      map[item] = true;
    }
  }
  //loop through second array and check if item in second array exists on created object.
  for (let j=0;j < arr2.length; j++) {
    if (map[array2[j]]) {
      //console.log(true);
      return true;
    }
  }
  //console.log(false);
  return false;
  
}

containsCommonItem2(array1, array2);

// O(a + b) Time Complexity

11. Think about error checks and how you can break this code. Never make assumptions about the input. we want to start thinking about how error may arise.

12. Don’t use bad/confusing names like i and j. Write code that reads well. maybe change the input to names we understand, more meaningful variable

const array1 = ['a','b', 'c','x'];
const array2 = ['z','y', 'a'];

function containsCommonItem2(users, items) {
  //loop through first array and create object where properties === items in the array
  let map = {};
  for (let i=0; i < users.length ; i++) {
    if (!map[arr1[i]]) {
      const item = arr1[i];
      map[item] = true;
    }
  }
  //loop through second array and check if item in second array exists on created object.
  for (let j=0;j < items.length; j++) {
    if (map[array2[j]]) {
      //console.log(true);
      return true;
    }
  }
  //console.log(false);
  return false;
  
}

containsCommonItem2(array1, array2);

13. Test your code: Check for no params, 0, undefined, null, massive arrays, async code, etc

14. Finally talk to the interviewer where you would improve the code. maybe use some methods

function containsCommonItem3(arr1, arr2) {
  console.log(arr1.some(item => arr2.includes(item)));
}

containsCommonItem3(array1, array2);

15. If your interviewer is happy with the solution, the interview usually ends here. It is also common that the interviewer asks you extension questions, such as how you would handle the problem if the whole input is too large to fit into memory, or if the input arrives as a stream.  explain space complexity, maybe explain that the code can be modularized

function containsCommonItem(arr1, arr2) {
  for (let i = 0 ; i < arr1.length; i++) {
    for (let j = 0; j < arr2.length; j++) {
      if (arr1[i] === arr2[j]) {
        return true;
      }
    }
  }
  return false;
}
// 0(1) - Space Complexity because we are not creating new variables just the inputs
const array1 = ['a','b', 'c','x'];
const array2 = ['z','y', 'a'];

function containsCommonItem2(users, items) {
  //loop through first array and create object where properties === items in the array
  let map = {};
  for (let i=0; i < users.length ; i++) {
    if (!map[arr1[i]]) {
      const item = arr1[i];
      map[item] = true;
    }
  }
  //loop through second array and check if item in second array exists on created object.
  for (let j=0;j < items.length; j++) {
    if (map[array2[j]]) {
      //console.log(true);
      return true;
    }
  }
  //console.log(false);
  return false;
  
}

containsCommonItem2(array1, array2);

// O (a+b)
//O(a) Space Complexity, we are creating variables

Data Structures : Introduction

What is a Data Structure?

is a collection of values, values can have relationships and they can have function apply to them. Each data structure is good and is specialized for its own thing.

Block chain at the end of the day is simply a data structure, a way to hold information.

  1. How to build one
  2. How to use it

Data Structures in different language

Each language has their own data types or built in data types, example - javascript (boolean, strings, true, undefined), has data structures to organize these data types ( [] {} )

Operations on Data Structures

Traversal means access each data item exactly once so that it can be processed.