Order the common complexity classes according to their growth rate
Identify the complexity classes of common sort methods
| Sort Name | Time Complexity | Space Complexity |
|---|---|---|
| bubble | O(n^2) | O(1) |
| selection | O(n^2) | O(1) |
| insertion | O(n^2) | O(1) |
| merge | O(n log n) | O(n) |
| quick | O(n log n)/O(n^2) | O(n)/O(log n) |
Important takeaway here is being able to connect code patterns with time complexities
Doing an exact number of calculations (independent of input) -> constant O(1) time
function loglinear(n) {
if (n <= 1) return;
for (let i = 1; i <= n; i++) { // n calculations in each stack frame
console.log(n);
}
loglinear(n / 2); // log n number of stack frames
loglinear(n / 2);
}// O(n^2)
function quadratic(n) {
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= n; j++) {
console.log(`${i}, ${j}`);
}
}
}
// O(n^3)
function cubic(n) {
for (let i = 1; i <= n; i++) {
for (let j = 1; j <= n; j++) {
for (let k = 1; k <= n; k++) {
console.log(`${i}, ${j}, ${k}`);
}
}
}
}// O(2^n)
function exponential_2n(n) {
console.log(n);
if (n === 1) return;
exponential_2n(n - 1);
exponential_2n(n - 1);
}
// O(3^n)
function exponential_3n(n) {
console.log(n);
if (n === 1) return;
exponential_3n(n - 1);
exponential_3n(n - 1);
exponential_3n(n - 1);
}Apply memoization to recursive problems to make them less than polynomial time.
// Standard Implementation
// Time Complexity: O(2^n)
// - For each call to fib, we have to make two branches, with n levels to this tree
function fib(n) {
if (n === 1 || n === 2) return 1;
return fib(n - 1) + fib(n - 2);
}
// Using memoization
// Time Complexity: O(n)
// - We only ever have to calculate fib(x) one time, every other time that we use it in a larger problem, the result is immediately accessible in our memo
// - The memo removes the repeated trees of calculations from our original code
function fib(n, memo = {}) {
if (n in memo) return memo[n]; // If we already calculated this value, return it
if (n === 1 || n === 2) return 1;
// Store the result in the memo first before returning
// Make sure to pass the memo in to your calls to fib!
memo[n] = fib(n - 1, memo) + fib(n - 2, memo);
return memo[n];
}Apply tabulation to iterative problems to make them less than polynomial time.
Example of a tabulated fibonacci:
// Using tabulation
// Time Complexity: O(n)
// - We are iterating through an array directly related to the size of the input and performing a constant number of calculations at each step (adding our two previous values together and storing the result in the array).
function fib(n) {
// We create a table to track our values as we build them up
// We're making it n+1 here so that table[n] lines up with the nth fib number
// This is because arrays are zero-indexed.
// We could have used an array of length n, but we would have to remember that
// the nth fib number would then be stored at table[n-1]. Completely doable,
// but I think making them line up is more intuitive.
let table = new Array(n + 1);
// Seed our table with our starting values.
// Again, if we had a table of length n, we could have seeded table[0] = 1
// and table[1] = 1 and had the same final result with our indices shifted.
table[0] = 0;
table[1] = 1;
// Iterate through our input and fill out our table as we go.
for (let i = 2; i < table.length; i++) {
table[i] = table[i - 1] + table[i - 2];
}
// At the end, the final entry in our table is the result we are looking for.
// The table holds all of the sub-answers that we used to get to this result.
return table[n];
}