Introduction

I love Python. It’s simple and gets the job done. Writing Python code is generally faster and easier than the other languages because it handles most of the work in the background for you instead of making you implement it. Because of this trait, I think it suits well for coding interview questions. As a third-year computer engineering student, I must always be ready to be tested. This requires me to have a language that I can write well all the time for interview questions. I generally don’t write Python or don’t solve interview questions daily. Because of that, I spend more time thinking about the syntax and programming language-related things than the actual implementation of the question. However, this must be changed. I’m writing this as a cheat sheet for myself to have a look at how things are done in Python, especially for interviews. My main source for writing this was Python for Coding Interviews - Everything you Need to Know by NeetCode video. If you want to see something more detailed, I suggest you go and check it out yourself. Too much talk, let’s dive in

Cheatsheet

Variables

Multiple assignments can be done in this way

a, b, c = 31, "abc", False

Unfortunately, we can’t use i++

n = n + 1
n += 1
# n++ # syntax error

We don’t have NULL, but have None as equivalent

n = None

If Statements

Everything is straightforward in if statements except there is no else if but there is a separate keyword as elif. Also, attention to indentation.

n = 1
if n < 2:
	n -= 1
elif n > 69:
	n *= 2
else:
	n += 3

Parentheses are needed for multi-line conditions. For conditions, we write and instead of && and or instead of ||.

n, m = 1, 2
if ((n > 2 and
	n != m) or n == m):
	n += 1

Inline if statements are useful for some cases

result = "Positive" if number > 0 else "Non-positive"

Loops

while True:
	print("This will run forever")
 
# Looping from 0 to 4
for i in range(5):
	print(i)
	
# Looping from 2 to 5
for i in range(2, 6):
	print(i)
	
# Looping from 5 to 2 (decrementing by 1)
for i in range(5, 1, -1):
	print(i)
	
# Looping from 31 to 69 with incrementing by 2 each time
for i in range(31, 70, 2):
	print(i)

Math

The division is decimal by default

print (5 / 2) # 2.5

Double slash rounds down, this also includes negative numbers

print (5 // 2) # 2
print (-3 // 2) # -2

For round negative numbers towards zero you can use decimal division and then convert to int.

print(int(-3 / 2)) # -1

Negative modding might not work as you might expected.

print(-10 % 3) # 2

To get -1 from modding negative numbers you can use

import math
print(math.fmod(-10, 3)) # -1.0

More math helpers

import math
 
print(math.floor(3 / 2))
print(math.ceil(3 / 2))
print(math.sqrt(2))
print(math.pow(2, 3))

Python numbers are infinite, so they never overflow. If you need to use max/min values you can use

float("inf")
float("-inf")

A REALLY HUGE number will always be smaller than float("inf").

Arrays

Arrays are called Lists in Python. Arrays in Python are dynamic.

arr = [1, 2, 3]

Arrays can be used as stacks. Appending and popping a value is an O(1) operation.

arr.append(4)
arr.pop()

Also, you can insert a value to a certain index. This is an O(n) operation.

arr.insert(1, 7) # O(n) operation

You can set the value of a given index in constant time.

arr[0] = 0 # O(1)

A list with a default value can be generated like this

n = 5
arr = [1] * n

The index of an array starts from 0. Indexing -1 will give you the last value, -2 will give you the second last value, etc.

arr = [1, 2, 3, 4]
print(arr[0]) # 1
print(arr[-1]) # 4
print(arr[-2]) # 3

Sublisting (aka slicing) is a handy tool. Similarly to for-loop ranges, the last index is non-inclusive

arr = [1, 2, 3, 4, 5]
print(arr[1:3]) # [2, 3]
print(arr[0:4]) # [1, 2, 3, 4]

Unpacking is also possible but you must be sure that num of variables matches to the array length

a, b, c = [1, 2, 3]

As the range() function gives us a list, looping another list mechanism is pretty straightforward.

nums = [1, 2, 3]
 
# with index
for i in range(len(nums)):
	print(nums[i])
 
# without index
for n in nums:
	print(n)
 
# with index and value
for i, n in enumerate(nums):
	print(i, n)

Looping multiple arrays simultaneously with using zip() and unpacking is also possible.

nums1 = [1, 3, 5]
nums2 = [2, 4, 6]
 
for n1, n2 in zip(nums1, nums2):
	print(n1, n2)

Reversing an array is pretty simple

nums.reverse()

Sorting

Sorting an array is pretty straightforward

arr.sort()

To sort in descending order you can set reverse argument True

arr.sort(reverse=True)

Strings will be sorted in alphabetical order

arr = ["bob", "alice", "jane", "doe"]
arr.sort() # ["alice", "bob", "doe", "jane"]

If you need custom sorting (sorting by length for example) you can set key argument to a lambda function.

For this case our lambda function means: for every value ‘x’, map it with len(x).

arr.sort(key=lambda x:len(x))

List Comprehension

Creating lists with initial values can easily be done by using list comprehensions.

arr = [i for i in range(5)] # [0, 1, 2, 3, 4]
arr = [i+i for i in range(5)] # [0, 2, 4, 6, 8]

2D Arrays

You can easily create a 2D array with list comprehension

arr = [[0] * 4 for i in range(4)]

If you think this can be simplified by multiplying a list, unfortunately, it won’t work.

# This won't work
# arr = [[0] * 4] * 4

Strings

Strings can be generated by using ' and ".

s = "abc"
ss = 'abc'

You can get a substring with slicing like arrays

print(s[0:2]) #ab

Strings are immutable, so you can’t change a char in a certain index. Modifying a string will create a new string.

# This won't work
# s[0] = "A"
 
# This creates a new string
s += "def" # O(n)

If you need the ASCII value of a char

print(ord("a")) # 97

You can combine a list of strings with .join()

strings = ["ab", "cd", "ef"]
print("".join(strings)) # abcdef
print("-".join(strings)) # ab-cd-ef

Queues

In Python, queues are double-ended by default

from collections import deque
 
queue = deque()
queue.append(1)
queue.append(2)
print(queue) # deque([1, 2])
 
queue.popleft()
print(queue)  # deque([2]) # O(1)
 
queue.appendleft(1)
print(queue) # deque([1, 2])
 
queue.pop()
print(queue) # deque([1])

Hash Sets

We can search and insert in O(1) in Hash Sets.

mySet = set()
 
mySet.add(1)
mySet.add(2)
print(mySet) # {1, 2}
print(len(mySet)) # 2
print(1 in mySet) # True
print(2 in mySet) # True
print(3 in mySet) # False
 
mySet.remove(2)
print(2 in mySet) # False

Sets can be generated from lists. And also Set comprehensions.

# Generate from list
print(set([1, 2, 3])) # {1, 2, 3}
 
# Generate with set comprehension
mySet = { i for i in range(5) }

Hash Maps

Hash Maps (a.k.a dictionaries) is one of the most used data collections for coding interviews. Creating a hash map is easy. You can assign or update a key to a value. For example, string alice is the key here and 88 is the value of the key.

myMap = {}
myMap["alice"] = 88
myMap["bob"] = 77
 
#or
myMap = {"alice": 88, "bob": 77}
 
# We can change a value of key
myMap["alice"] = 80

We can look for if a key exists or not

print("alice" in myMap) # True
myMap.pop("alice")
print("alice" in myMap) # False

Like list comprehension, also using dict comprehensions is possible.

myMap = { i: 2*i for i in range(3)}

Looping through maps is also pretty useful. The code is pretty self-exploratory

for key in myMap:
	print(key, myMap[key])
 
for val in myMap.values():
	print(val)
 
for key, val in myMap.items():
	print(key, val)

For some cases, creating the dict with defaultdict prevents a lot of edge case crashes

from collections import defaultdict
 
# Initialize a defaultdict with int, which defaults missing keys to 0
counts = defaultdict(int)
 
# Count occurrences in a list
for item in ["apple", "banana", "apple"]:
    counts[item] += 1

Tuples

Tuples are like arrays but immutable

tup = (1, 2, 3)
print(tup)
print(tup[-1])

Tuples also can be used as a key for hash map/set But lists can’t be keys

myMap = { (1,2): 3 }
print(myMap[(1,2)])
 
mySet = set()
mySet.add((1, 2))
print((1, 2) in mySet) # True

Unpacking is also possible for tuples

fruits = ("apple""banana""cherry")  
  
(green, yellow, red) = fruits  
  
print(green) # apple
print(yellow) # banana
print(red) # cheery

Heaps

Heaps might be useful for some kind of questions. Python heaps are min heaps by default. Under the hood, heaps are arrays

import heapq
 
minHeap = []
heapq.heappush(minHeap, 3)
heapq.heappush(minHeap, 2)
heapq.heappush(minHeap, 4)
 
# Min is always at index 0
print(minHeap[0]) # 2
 
while len(minHeap):
	print(heapq.heappop(minHeap))
# 2 
# 3
# 4

There is no max heaps by default. To work around this, you can use min heap but multiply the values by -1 when doing push & pop operations

maxHeap = []
heapq.heappush(maxHeap, -3)
heapq.heappush(maxHeap, -2)
heapq.heappush(maxHeap, -4)
 
# Max is always at index 0
print(-1 * maxHeap[0])
 
while len(minHeap):
	print(-1 * heapq.heappop(minHeap))
# 4
# 3
# 2

Heaps can also be built with initial values.

arr = [2, 1, 8, 4, 5]
heapq.heapify(arr) # O(n)

Functions

def myFunc(n, m):
	return n * m
 
print(myFunc(3, 4))

Nested Functions

Nested functions have access to outer variables

def outer(a, b):
	c = "c"
 
	def inner():
		return a + b + c
	return inner()
 
print(outer("a", "b")) # abc

They also can modify objects but not reassign them unless nonlocal keyword is used

def double(arr, val):
	def helper():
		# Modifying array works
		for i, n in enumerate(arr):
			arr[i] *= 2
 
		# will only modify val in the helper scope
		# val *= 2
 
		# this will modify val outside helper scope
		nonlocal val
		val *= 2
	helper()
	print(arr, val)
 
nums = [1, 2]
val = 3
double(nums, val) # [2, 4] 6

Classes

Ofc, there is a lot more about Python classes. However, knowing __init__ and usage of self should be enough for interviews.

class MyClass:
	# Constructor
	def __init__(self, nums):
		self.nums = nums
		self.size = len(nums)
 
	# self keyword required as param
	def getLength(self):
		return self.size
 
	def getDoubleLength(self):
		return 2 * self.getLength()