0% found this document useful (0 votes)
2 views11 pages

Python Interview Questions - MD

The document provides a collection of coding interview questions and solutions covering various topics such as data structures, algorithms, and Python-specific idioms. It includes functions for reversing strings, checking for palindromes, finding duplicates, and implementing algorithms like FizzBuzz and binary search. Additionally, it discusses concepts like decorators, generators, and class design, emphasizing time and space complexities for each solution.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views11 pages

Python Interview Questions - MD

The document provides a collection of coding interview questions and solutions covering various topics such as data structures, algorithms, and Python-specific idioms. It includes functions for reversing strings, checking for palindromes, finding duplicates, and implementing algorithms like FizzBuzz and binary search. Additionally, it discusses concepts like decorators, generators, and class design, emphasizing time and space complexities for each solution.
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

A mix of easy, medium, and harder questions covering common interview themes: data

structures, strings, recursion, OOP, and Python-specific idioms.

Write a function to reverse a string without using [::-1] .

def reverse_string(s: str) -> str:


result = []
for char in s:
[Link](0, char)
return ''.join(result)

# More efficient version using two pointers


def reverse_string_v2(s: str) -> str:
chars = list(s)
left, right = 0, len(chars) - 1
while left < right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
return ''.join(chars)

O(n) time, O(n) space.

Determine if a string is a palindrome, ignoring case and non-alphanumeric


characters.

def is_palindrome(s: str) -> bool:


cleaned = [[Link]() for c in s if [Link]()]
return cleaned == cleaned[::-1]

print(is_palindrome("A man, a plan, a canal: Panama")) # True

Do it with O(1) extra space using two pointers instead of building a new list.
Given a list, return all elements that appear more than once.

def find_duplicates(nums: list) -> list:


seen = set()
duplicates = set()
for n in nums:
if n in seen:
[Link](n)
else:
[Link](n)
return list(duplicates)

print(find_duplicates([1, 2, 3, 2, 4, 5, 1])) # [1, 2]

O(n) time, O(n) space.

Given a list of integers and a target, return indices of the two numbers that add
up to the target.

def two_sum(nums: list, target: int) -> list:


seen = {} # value -> index
for i, num in enumerate(nums):
complement = target - num
if complement in seen:
return [seen[complement], i]
seen[num] = i
return []

print(two_sum([2, 7, 11, 15], 9)) # [0, 1]

O(n) time, O(n) space (vs O(n²) brute force).


def fizzbuzz(n: int) -> list:
result = []
for i in range(1, n + 1):
if i % 15 == 0:
[Link]("FizzBuzz")
elif i % 3 == 0:
[Link]("Fizz")
elif i % 5 == 0:
[Link]("Buzz")
else:
[Link](str(i))
return result

Given a string of text, return the frequency of each word (case-insensitive).

from collections import Counter


import re

def word_frequency(text: str) -> dict:


words = [Link](r'\w+', [Link]())
return dict(Counter(words))

print(word_frequency("The cat sat on the mat. The cat ran."))


# {'the': 3, 'cat': 2, 'sat': 1, 'on': 1, 'mat': 1, 'ran': 1}
def merge_sorted(a: list, b: list) -> list:
merged = []
i, j = 0, 0
while i < len(a) and j < len(b):
if a[i] <= b[j]:
[Link](a[i])
i += 1
else:
[Link](b[j])
j += 1
[Link](a[i:])
[Link](b[j:])
return merged

print(merge_sorted([1, 3, 5], [2, 4, 6])) # [1, 2, 3, 4, 5, 6]

O(n + m) time.

Given a string containing ()[]{} , determine if the brackets are balanced.

def is_valid(s: str) -> bool:


stack = []
pairs = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in [Link]():
[Link](char)
elif char in pairs:
if not stack or [Link]() != pairs[char]:
return False
return not stack

print(is_valid("{[()]}")) # True
print(is_valid("{[(])}")) # False

O(n) time and space. Stack-based — a very common pattern to recognize.


Write an e�cient function to compute the nth Fibonacci number.

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n: int) -> int:
if n <= 1:
return n
return fib(n - 1) + fib(n - 2)

print(fib(30)) # 832040

Explain why plain recursion is O(2^n) and memoization brings it to O(n). Also
be ready to write the iterative O(n) time / O(1) space version.

def fib_iterative(n: int) -> int:


a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a

def flatten(nested: list) -> list:


result = []
for item in nested:
if isinstance(item, list):
[Link](flatten(item))
else:
[Link](item)
return result

print(flatten([1, [2, 3, [4, 5]], 6])) # [1, 2, 3, 4, 5, 6]


def binary_search(arr: list, target: int) -> int:
low, high = 0, len(arr) - 1
while low <= high:
mid = (low + high) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
low = mid + 1
else:
high = mid - 1
return -1

print(binary_search([1, 3, 5, 7, 9, 11], 7)) # 3

O(log n). Requires sorted input — mention this constraint.


from collections import OrderedDict

class LRUCache:
def __init__(self, capacity: int):
[Link] = capacity
[Link] = OrderedDict()

def get(self, key: int) -> int:


if key not in [Link]:
return -1
[Link].move_to_end(key)
return [Link][key]

def put(self, key: int, value: int) -> None:


if key in [Link]:
[Link].move_to_end(key)
[Link][key] = value
if len([Link]) > [Link]:
[Link](last=False)

cache = LRUCache(2)
[Link](1, 1)
[Link](2, 2)
print([Link](1)) # 1
[Link](3, 3) # evicts key 2
print([Link](2)) # -1

Why OrderedDict gives O(1) get/put, and how you'd do it manually with a
hashmap + doubly linked list.
from collections import defaultdict

def group_anagrams(words: list) -> list:


groups = defaultdict(list)
for word in words:
key = ''.join(sorted(word))
groups[key].append(word)
return list([Link]())

print(group_anagrams(["eat", "tea", "tan", "ate", "nat", "bat"]))


# [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

class ListNode:
def __init__(self, val=0, next=None):
[Link] = val
[Link] = next

def has_cycle(head: ListNode) -> bool:


slow = fast = head
while fast and [Link]:
slow = [Link]
fast = [Link]
if slow == fast:
return True
return False

Floyd's Tortoise and Hare — O(n) time, O(1) space.

Write a decorator that prints how long a function takes to run.


import time
from functools import wraps

def timer(func):
@wraps(func)
def wrapper(*args, **kwargs):
start = time.perf_counter()
result = func(*args, **kwargs)
elapsed = time.perf_counter() - start
print(f"{func.__name__} took {elapsed:.4f}s")
return result
return wrapper

@timer
def slow_function():
[Link](0.5)
return "done"

slow_function()

Why @wraps matters (preserves __name__ / __doc__ ), di�erence between


decorators with and without arguments.

Show you understand generators vs. returning a list.

def fib_generator():
a, b = 0, 1
while True:
yield a
a, b = b, a + b

gen = fib_generator()
print([next(gen) for _ in range(10)])
# [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Lazy evaluation, memory e�ciency vs. lists, yield vs return .


Design a BankAccount class with deposit/withdraw and proper encapsulation.

class BankAccount:
def __init__(self, owner: str, balance: float = 0):
[Link] = owner
self._balance = balance # convention: "protected"

def deposit(self, amount: float) -> None:


if amount <= 0:
raise ValueError("Deposit amount must be positive")
self._balance += amount

def withdraw(self, amount: float) -> None:


if amount > self._balance:
raise ValueError("Insufficient funds")
self._balance -= amount

@property
def balance(self) -> float:
return self._balance

def __repr__(self) -> str:


return f"BankAccount(owner={[Link]!r}, balance={self._balance})"

acc = BankAccount("Alice", 100)


[Link](50)
[Link](30)
print(acc) # BankAccount(owner='Alice', balance=120)

_ vs __ naming conventions, @property , why raise exceptions instead of


returning error codes.
• lists/dicts/sets are mutable; strings/tuples/ints are
immutable. Why does this matter for default function arguments ( def f(x=[])
pitfall)?
• is == identity vs equality.
• [x for x in y] builds a list in
memory; (x for x in y) is lazy.
• *args **kwargs how they work and when to use them.
• what it is, and why it a�ects multi-threading for CPU-
bound tasks (use multiprocessing instead).
• [Link]() vs [Link]() .
• @staticmethod @classmethod

�. Try each problem yourself first (with a timer, ~15-20 min) before looking at the
answer.
�. For each, be ready to state time/space complexity out loud.
�. Practice explaining your approach before writing code — many interviewers weigh
this as much as the final solution.
�. Re-implement a few (like Two Sum, Valid Parentheses, LRU Cache) from memory a
day later to check retention.

You might also like