Data structures and algorithms

Basic data structures

Sorting algorithms

Tree search types

Big O notation

LeetCode course

Time complexity examples

O(1): uses the same resources no matter the input size

print(input)

for i in range(5000):
    print(input) # O(5000) = O(1)

O(n): complexity scales linearly with the input size

for i in input:
    print(input)
# or
for i in input:
    for j in range(5000):
        print(input)
        # even though there are nested loops, the inner loop runs the same
        # number of times for each item in input, and O(5000n) === O(n)

O(n2):

for i in input:
    for j in input:
        print(i * j)
        # the inner loop runs n times each time the outer loop runs,
        # so O(n * n) = O(n^2)

O(n+m):

for i in input1:
    print(i)

for i in input1:
    print i

for i in input2:
    print i

# O(2n + m) = O(n + m)

O(n2): (partial series explanation)

for (let i = 0; i < input.length; i++) {
    for (let j = i; j < input.length; j++) {
        console.log(input[i] + input[j])
    }
}

// the inner loop depends on what iteration the outer loop is on
// on the first outer loop iteration, the inner loop runs n times
// on the second outer loop iteration, the inner loop runs n - 1 times, etc.
// therefore, the total number of iterations is 1 + 2 + 3 + ... + n,
// which equals n(n + 1)/2 = (n^2 + n)/2 = O(n^2)
// the addition in the numerator and constant in the denominator are ignored

Python equivalent:

for i in range(len(input)):
    for j in range(i, len(input)):
        print(i, j, input[i], input[j], input[i] + input[j])

Logarithmic complexity

Two Pointers

def check_palindrome(string):
    i, j = 0, len(string) - 1
    while i < j:
        if string[i] != string[j]:
            return False
        i += 1
        j -= 1
    return True

print(check_palindrome("racecar"))  # True
print(check_palindrome("banana"))  # False
print(check_palindrome("amanaplanacanalpanama"))  # True
def can_reach_target(nums, target):
    i, j = 0, len(nums) - 1
    while i < j:
        sum = nums[i] + nums[j]
        if sum == target:
            print(nums[i], nums[j])
            return True
        elif sum < target:
            # the sum needs to be bigger, so move the left pointer up
            i += 1
        elif sum > target:
            # the sum needs to be smaller, move the right pointer down
            j -= 1
    return False

print(can_reach_target([1, 2, 4, 6, 8, 9, 14, 15], 13))  # 9, 4
def combine(nums1: list[int], nums2: list[int]):
    i = j = 0
    result = []
    while i < len(nums1) and j < len(nums2):
        # on each iteration, take the smaller of the two current numbers
        if nums1[i] < nums2[j]:
            result.append(nums1[i])
            i += 1
        else:
            result.append(nums2[j])
            j += 1

    # append the rest of the nums
    while i < len(nums1):
        result.append(nums1[i])
        i += 1
    while j < len(nums2):
        result.append(nums2[j])
        j += 1

    assert len(result) == len(nums1) + len(nums2)
    return result

print(combine([5, 9, 20, 48, 60, 201], [19, 38, 95]))
print(combine([19, 38, 95], [5, 9, 20, 48, 60, 201]))
def is_subsequence(sub: str, full: str):
    i = j = 0
    while i < len(sub) and j < len(full):
        # step through the full string, and increment i each time we find a
        # character of the subsequence string
        if sub[i] == full[j]:
            i += 1
        j += 1
    # we know it's a subsequence if we found all the characters
    return i == len(sub)

print(is_subsequence("ace", "abcde"))  # true
print(is_subsequence("adb", "abcde"))  # false

Sliding Window

# pseudocode
left = 0
for right in range(len(arr)):
    # add the element at arr[right] to the window
    while WINDOW_IS_INVALID:
        # remove the element at arr[left] from the window
        left += 1
    # update the answer if necessary - remember the current state of the
    # window may not "beat" the current answer
return answer
def longest_sub(nums: list[int], k: int):
    left = 0
    sum = 0
    # we use a separate answer variable because the state of `left` and
    # `right` at the end of the for loop may not be the correct solution
    answer = 0
    for right in range(len(nums)):
        sum += nums[right]
        while sum > k:
            sum -= nums[left]
            left += 1
        # only overwrite answer if the current subarray is larger than the
        # previous largest
        answer = max(answer, right - left + 1)
    return answer

print(longest_sub([3, 2, 1, 3, 1, 1], 5))  # 3
print(longest_sub([1, 2, 3, 4, 5, 50000], 9999))  # 5
print(longest_sub([10, 20, 30, 40, 50], 1)) # 0
def longest_ones(str: str):
	left = 0
	zero_count = 0
	answer = 0
	for right in range(len(str)):
		if str[right] == '0': zero_count += 1
		while zero_count > 1:
			if str[left] == '0': zero_count -= 1
			left += 1
		answer = max(answer, right - left + 1)
	return answer

print(longest_ones('1101100111')) # 5
def count_valid_sublists(nums: list[int], limit: int):
    if limit <= 1:
        return 0

    left = 0
    product = 1
    count = 0
    for right in range(len(nums)):
        product *= nums[right]
        while product >= limit:
            product /= nums[left]
            left += 1
        count += right - left + 1
    return count

print(count_valid_sublists([10, 5, 2, 6], 100))  # 8
def largest_sum(nums: list[int], k: int):
    sum = 0
    for i in range(k):
        # get the sum of the numbers in the initial window
        sum += nums[i]
    answer = sum

    for i in range(k, len(nums)):
        # add the upcoming number and subtract the one leaving the window
        sum += nums[i] - nums[i - k]
        answer = max(answer, sum)
    return answer

print(largest_sum([3, -1, 4, 12, -8, 5, 6], 4))  # 8

Dynamic programming

LeetCode course

You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security systems connected and it will automatically contact the police if two adjacent houses were broken into on the same night.

Given an integer array nums representing the amount of money of each house, return the maximum amount of money you can rob tonight without alerting the police.