Chapter 2: Data Structures & Built-ins - Interview Preparation Notes

Table of Contents

  1. Advanced List Operations
  2. Tuple Deep Dive
  3. Set Operations & Methods
  4. Dictionary Mastery
  5. String Methods & Manipulation
  6. Important Built-in Functions
  7. Data Structure Comparison
  8. Common Interview Questions
  9. Practice Problems

Advanced List Operations

List Methods Deep Dive

# Creating lists
numbers = [1, 2, 3, 4, 5]
fruits = ["apple", "banana", "cherry"]
mixed = [1, "hello", 3.14, [1, 2, 3]]
 
# Adding elements
fruits.append("orange")           # Add single element to end
fruits.extend(["grape", "kiwi"])  # Add multiple elements
fruits.insert(1, "mango")         # Insert at specific index
fruits += ["pear"]                # Concatenation
 
# Removing elements
fruits.remove("banana")           # Remove first occurrence
fruits.pop()                      # Remove and return last element
fruits.pop(0)                     # Remove and return element at index
del fruits[1]                     # Delete element at index
fruits.clear()                    # Remove all elements
 
# Finding elements
fruits = ["apple", "banana", "cherry", "apple"]
print(fruits.index("apple"))      # 0 (first occurrence)
print(fruits.count("apple"))      # 2 (count occurrences)
print("banana" in fruits)         # True (membership test)
 
# Sorting and reversing
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
numbers.sort()                    # In-place sorting
print(numbers)                    # [1, 1, 2, 3, 4, 5, 6, 9]
 
numbers.sort(reverse=True)        # Descending order
print(numbers)                    # [9, 6, 5, 4, 3, 2, 1, 1]
 
# Create new sorted list without modifying original
original = [3, 1, 4, 1, 5]
sorted_list = sorted(original)    # [1, 1, 3, 4, 5]
print(original)                   # [3, 1, 4, 1, 5] (unchanged)
 
# Reversing
numbers = [1, 2, 3, 4, 5]
numbers.reverse()                 # In-place reverse
print(numbers)                    # [5, 4, 3, 2, 1]
 
reversed_list = list(reversed(numbers))  # Create new reversed list

Advanced List Slicing

# Basic slicing: list[start:stop:step]
numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
# Basic slicing
print(numbers[2:5])     # [2, 3, 4]
print(numbers[:3])      # [0, 1, 2]
print(numbers[3:])      # [3, 4, 5, 6, 7, 8, 9]
print(numbers[:])       # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (copy)
 
# Step slicing
print(numbers[::2])     # [0, 2, 4, 6, 8] (every 2nd element)
print(numbers[1::2])    # [1, 3, 5, 7, 9] (every 2nd starting from index 1)
print(numbers[::3])     # [0, 3, 6, 9] (every 3rd element)
 
# Negative indexing
print(numbers[-1])      # 9 (last element)
print(numbers[-3:])     # [7, 8, 9] (last 3 elements)
print(numbers[:-2])     # [0, 1, 2, 3, 4, 5, 6, 7] (all except last 2)
 
# Reverse slicing
print(numbers[::-1])    # [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] (reverse)
print(numbers[5:2:-1])  # [5, 4, 3] (reverse from index 5 to 2)
 
# Modifying slices
numbers[2:5] = [10, 20, 30]  # Replace slice
print(numbers)               # [0, 1, 10, 20, 30, 5, 6, 7, 8, 9]
 
numbers[::2] = [100, 200, 300, 400, 500]  # Replace every 2nd element
print(numbers)  # [100, 1, 200, 20, 300, 5, 400, 7, 500, 9]

List Comprehensions (Preview - Detailed in Chapter 3)

# Basic list comprehension
squares = [x**2 for x in range(5)]
print(squares)  # [0, 1, 4, 9, 16]
 
# With condition
even_squares = [x**2 for x in range(10) if x % 2 == 0]
print(even_squares)  # [0, 4, 16, 36, 64]
 
# Nested list comprehension
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
flattened = [num for row in matrix for num in row]
print(flattened)  # [1, 2, 3, 4, 5, 6, 7, 8, 9]

Tuple Deep Dive

Tuple Immutability

# Tuples are immutable
coordinates = (10, 20)
# coordinates[0] = 15  # TypeError: 'tuple' object does not support item assignment
 
# But if tuple contains mutable objects, those can be modified
nested = ([1, 2, 3], "hello")
nested[0].append(4)  # This works!
print(nested)  # ([1, 2, 3, 4], 'hello')

Tuple Packing and Unpacking

# Tuple packing
person = "John", 25, "Engineer"  # Parentheses optional
print(person)  # ('John', 25, 'Engineer')
 
# Tuple unpacking
name, age, job = person
print(f"Name: {name}, Age: {age}, Job: {job}")
 
# Multiple assignment (tuple unpacking)
a, b = 10, 20
print(f"a = {a}, b = {b}")  # a = 10, b = 20
 
# Swapping variables (common interview question)
a, b = b, a
print(f"a = {a}, b = {b}")  # a = 20, b = 10
 
# Unpacking with * (Python 3.5+)
numbers = (1, 2, 3, 4, 5)
first, *middle, last = numbers
print(f"First: {first}, Middle: {middle}, Last: {last}")
# First: 1, Middle: [2, 3, 4], Last: 5
 
# Unpacking in function calls
def add_three(a, b, c):
    return a + b + c
 
values = (1, 2, 3)
result = add_three(*values)  # Unpack tuple as arguments
print(result)  # 6

Tuple Methods

# Tuple methods (limited compared to lists)
numbers = (1, 2, 3, 2, 4, 2)
 
print(numbers.count(2))    # 3 (count occurrences)
print(numbers.index(3))    # 2 (index of first occurrence)
 
# Tuple concatenation
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined = tuple1 + tuple2
print(combined)  # (1, 2, 3, 4, 5, 6)
 
# Tuple repetition
repeated = (1, 2) * 3
print(repeated)  # (1, 2, 1, 2, 1, 2)

When to Use Tuples vs Lists

# Use tuples when:
# 1. Data shouldn't change (coordinates, database records)
coordinates = (10, 20)
person_info = ("John", 25, "Engineer")
 
# 2. As dictionary keys (tuples are hashable, lists are not)
locations = {
    (0, 0): "origin",
    (1, 1): "diagonal",
    (2, 2): "another diagonal"
}
 
# 3. Function return values
def get_name_and_age():
    return "John", 25
 
name, age = get_name_and_age()
 
# Use lists when:
# 1. Data needs to be modified
shopping_list = ["milk", "bread", "eggs"]
shopping_list.append("butter")
 
# 2. Order matters and might change
scores = [85, 92, 78, 96]
scores.sort()

Set Operations & Methods

Set Creation and Basic Operations

# Creating sets
fruits = {"apple", "banana", "cherry"}
numbers = set([1, 2, 3, 4, 5])
empty_set = set()  # Not {} (that's a dict)
 
# Adding elements
fruits.add("orange")
fruits.update(["grape", "kiwi"])  # Add multiple elements
fruits.update({"mango", "pear"})  # Add from another set
 
# Removing elements
fruits.remove("banana")    # Raises KeyError if not found
fruits.discard("apple")    # No error if not found
fruits.pop()               # Remove and return arbitrary element
fruits.clear()             # Remove all elements
 
print(fruits)  # {'grape', 'kiwi', 'mango', 'pear'}

Set Mathematical Operations

# Set operations
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}
 
# Union
union1 = set1.union(set2)
union2 = set1 | set2
print(union1)  # {1, 2, 3, 4, 5, 6, 7, 8}
 
# Intersection
intersection1 = set1.intersection(set2)
intersection2 = set1 & set2
print(intersection1)  # {4, 5}
 
# Difference
diff1 = set1.difference(set2)
diff2 = set1 - set2
print(diff1)  # {1, 2, 3}
 
# Symmetric difference (elements in either set, but not both)
sym_diff1 = set1.symmetric_difference(set2)
sym_diff2 = set1 ^ set2
print(sym_diff1)  # {1, 2, 3, 6, 7, 8}
 
# Subset and superset
set3 = {1, 2, 3}
print(set3.issubset(set1))     # True
print(set1.issuperset(set3))   # True
print(set1.isdisjoint(set2))   # False (they share elements)

Set Methods

# Set methods
fruits = {"apple", "banana", "cherry"}
 
# Check membership
print("apple" in fruits)        # True
print("orange" not in fruits)   # True
 
# Length
print(len(fruits))              # 3
 
# Copy
fruits_copy = fruits.copy()
print(fruits_copy)              # {'apple', 'banana', 'cherry'}
 
# Frozen sets (immutable sets)
frozen = frozenset([1, 2, 3, 4])
# frozen.add(5)  # AttributeError: 'frozenset' object has no attribute 'add'

Dictionary Mastery

Dictionary Creation and Access

# Creating dictionaries
person = {
    "name": "John",
    "age": 25,
    "city": "New York"
}
 
# Alternative creation methods
person2 = dict(name="Jane", age=30, city="Boston")
person3 = dict([("name", "Bob"), ("age", 35), ("city", "Chicago")])
 
# Accessing values
print(person["name"])           # John
print(person.get("age"))        # 25
print(person.get("phone", "N/A"))  # N/A (default value)
 
# Check if key exists
print("name" in person)         # True
print("phone" in person)        # False

Dictionary Methods

# Dictionary methods
person = {"name": "John", "age": 25, "city": "New York"}
 
# Keys, values, items
print(person.keys())            # dict_keys(['name', 'age', 'city'])
print(person.values())          # dict_values(['John', 25, 'New York'])
print(person.items())           # dict_items([('name', 'John'), ('age', 25), ('city', 'New York')])
 
# Update dictionary
person.update({"phone": "123-456-7890", "age": 26})
print(person)  # {'name': 'John', 'age': 26, 'city': 'New York', 'phone': '123-456-7890'}
 
# Remove items
del person["city"]              # Remove key-value pair
phone = person.pop("phone")     # Remove and return value
print(phone)  # 123-456-7890
 
# Pop with default
email = person.pop("email", "No email")  # Return default if key doesn't exist
print(email)  # No email
 
# Clear dictionary
person.clear()
print(person)  # {}

Dictionary Iteration Patterns

# Different ways to iterate over dictionaries
scores = {"Alice": 85, "Bob": 92, "Charlie": 78, "Diana": 96}
 
# Iterate over keys
for name in scores:
    print(f"{name}: {scores[name]}")
 
# Iterate over keys explicitly
for name in scores.keys():
    print(f"{name}: {scores[name]}")
 
# Iterate over values
for score in scores.values():
    print(f"Score: {score}")
 
# Iterate over key-value pairs
for name, score in scores.items():
    print(f"{name}: {score}")
 
# Dictionary comprehension
squared_scores = {name: score**2 for name, score in scores.items()}
print(squared_scores)  # {'Alice': 7225, 'Bob': 8464, 'Charlie': 6084, 'Diana': 9216}

Advanced Dictionary Operations

# Nested dictionaries
students = {
    "Alice": {"age": 20, "grade": "A", "subjects": ["Math", "Physics"]},
    "Bob": {"age": 19, "grade": "B", "subjects": ["Chemistry", "Biology"]}
}
 
# Accessing nested values
print(students["Alice"]["age"])           # 20
print(students["Alice"]["subjects"][0])   # Math
 
# Adding to nested dictionary
students["Alice"]["subjects"].append("Computer Science")
 
# Dictionary from two lists
names = ["Alice", "Bob", "Charlie"]
ages = [20, 19, 21]
name_age_dict = dict(zip(names, ages))
print(name_age_dict)  # {'Alice': 20, 'Bob': 19, 'Charlie': 21}
 
# Dictionary with default values
from collections import defaultdict
 
# Default dictionary
dd = defaultdict(list)
dd["fruits"].append("apple")
dd["fruits"].append("banana")
print(dd["fruits"])  # ['apple', 'banana']
print(dd["vegetables"])  # [] (empty list, not KeyError)
 
# Counter (from collections)
from collections import Counter
text = "hello world"
letter_count = Counter(text)
print(letter_count)  # Counter({'l': 3, 'o': 2, 'h': 1, 'e': 1, ' ': 1, 'w': 1, 'r': 1, 'd': 1})

String Methods & Manipulation

Essential String Methods

# String creation and basic info
text = "  Hello World  "
print(len(text))                # 15
print(text[0])                  # ' ' (space)
 
# Case methods
print(text.upper())             # "  HELLO WORLD  "
print(text.lower())             # "  hello world  "
print(text.title())             # "  Hello World  "
print(text.capitalize())        # "  hello world  "
print(text.swapcase())          # "  hELLO wORLD  "
 
# Whitespace methods
print(text.strip())             # "Hello World"
print(text.lstrip())            # "Hello World  "
print(text.rstrip())            # "  Hello World"
 
# Search methods
text = "Hello World"
print(text.startswith("Hello")) # True
print(text.endswith("World"))   # True
print(text.find("World"))       # 6 (index of first occurrence)
print(text.find("Python"))      # -1 (not found)
print(text.index("World"))      # 6 (raises ValueError if not found)
print(text.count("l"))          # 3 (count occurrences)

String Splitting and Joining

# Splitting strings
sentence = "apple,banana,cherry,date"
fruits = sentence.split(",")
print(fruits)  # ['apple', 'banana', 'cherry', 'date']
 
# Split with max splits
text = "one two three four five"
words = text.split(" ", 2)  # Split into max 3 parts
print(words)  # ['one', 'two', 'three four five']
 
# Split lines
multiline = "Line 1\nLine 2\nLine 3"
lines = multiline.splitlines()
print(lines)  # ['Line 1', 'Line 2', 'Line 3']
 
# Joining strings
fruits = ["apple", "banana", "cherry"]
sentence = ", ".join(fruits)
print(sentence)  # apple, banana, cherry
 
# Join with different separators
words = ["Hello", "World"]
print(" ".join(words))      # Hello World
print("-".join(words))      # Hello-World
print("".join(words))       # HelloWorld

String Replacement and Formatting

# String replacement
text = "Hello World"
new_text = text.replace("World", "Python")
print(new_text)  # Hello Python
 
# Replace with count limit
text = "apple apple apple"
new_text = text.replace("apple", "orange", 2)
print(new_text)  # orange orange apple
 
# String formatting (multiple methods)
name = "John"
age = 25
 
# f-strings (Python 3.6+, preferred)
message = f"Hello, {name}! You are {age} years old."
print(message)
 
# .format() method
message = "Hello, {}! You are {} years old.".format(name, age)
print(message)
 
message = "Hello, {name}! You are {age} years old.".format(name=name, age=age)
print(message)
 
# % formatting (older style)
message = "Hello, %s! You are %d years old." % (name, age)
print(message)
 
# String alignment and padding
text = "Hello"
print(text.ljust(10))        # "Hello     " (left align, pad to 10)
print(text.rjust(10))        # "     Hello" (right align, pad to 10)
print(text.center(10))       # "  Hello   " (center align, pad to 10)
print(text.center(10, "*"))  # "**Hello***" (center with custom padding)

String Validation Methods

# String validation methods
text1 = "Hello123"
text2 = "123"
text3 = "Hello"
text4 = "hello"
text5 = "HELLO"
text6 = "Hello World"
text7 = "   "
 
print(text1.isalnum())       # True (alphanumeric)
print(text2.isdigit())       # True (digits only)
print(text3.isalpha())       # True (letters only)
print(text4.islower())       # True (lowercase)
print(text5.isupper())       # True (uppercase)
print(text6.istitle())       # True (title case)
print(text7.isspace())       # True (whitespace only)

Important Built-in Functions

Map, Filter, and Reduce

# Map function
numbers = [1, 2, 3, 4, 5]
squared = list(map(lambda x: x**2, numbers))
print(squared)  # [1, 4, 9, 16, 25]
 
# Map with multiple iterables
list1 = [1, 2, 3]
list2 = [4, 5, 6]
sums = list(map(lambda x, y: x + y, list1, list2))
print(sums)  # [5, 7, 9]
 
# Filter function
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens)  # [2, 4, 6, 8, 10]
 
# Reduce function (from functools)
from functools import reduce
numbers = [1, 2, 3, 4, 5]
product = reduce(lambda x, y: x * y, numbers)
print(product)  # 120
 
# Sum of numbers
total = reduce(lambda x, y: x + y, numbers)
print(total)  # 15

Enumerate and Zip

# Enumerate function
fruits = ["apple", "banana", "cherry"]
for index, fruit in enumerate(fruits):
    print(f"{index}: {fruit}")
# 0: apple
# 1: banana
# 2: cherry
 
# Enumerate with start value
for index, fruit in enumerate(fruits, 1):
    print(f"{index}: {fruit}")
# 1: apple
# 2: banana
# 3: cherry
 
# Zip function
names = ["Alice", "Bob", "Charlie"]
ages = [25, 30, 35]
cities = ["NYC", "LA", "Chicago"]
 
# Zip two lists
for name, age in zip(names, ages):
    print(f"{name} is {age} years old")
 
# Zip multiple lists
for name, age, city in zip(names, ages, cities):
    print(f"{name}, {age}, lives in {city}")
 
# Unzip (reverse of zip)
pairs = [("Alice", 25), ("Bob", 30), ("Charlie", 35)]
names, ages = zip(*pairs)
print(names)  # ('Alice', 'Bob', 'Charlie')
print(ages)   # (25, 30, 35)

Other Important Built-in Functions

# Range function
print(list(range(5)))        # [0, 1, 2, 3, 4]
print(list(range(1, 6)))     # [1, 2, 3, 4, 5]
print(list(range(0, 10, 2))) # [0, 2, 4, 6, 8]
 
# Len function
print(len("Hello"))          # 5
print(len([1, 2, 3, 4]))     # 4
print(len({"a": 1, "b": 2})) # 2
 
# Min and Max
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(min(numbers))          # 1
print(max(numbers))          # 9
print(min("hello"))          # 'e' (lexicographically smallest)
 
# Sum function
print(sum([1, 2, 3, 4, 5]))  # 15
print(sum([1, 2, 3, 4, 5], 10))  # 25 (start with 10)
 
# Sorted function
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print(sorted(numbers))       # [1, 1, 2, 3, 4, 5, 6, 9]
print(sorted(numbers, reverse=True))  # [9, 6, 5, 4, 3, 2, 1, 1]
 
# Any and All
numbers = [1, 2, 3, 4, 5]
print(any(x > 3 for x in numbers))    # True (at least one > 3)
print(all(x > 3 for x in numbers))    # False (not all > 3)
 
# Reversed function
numbers = [1, 2, 3, 4, 5]
reversed_numbers = list(reversed(numbers))
print(reversed_numbers)      # [5, 4, 3, 2, 1]

Data Structure Comparison

When to Use Each Data Structure

Data StructureMutableOrderedDuplicatesUse Cases
ListDynamic collections, stacks, queues
TupleFixed data, coordinates, function returns
SetUnique elements, membership testing
Dictionary✅ (Python 3.7+)❌ (keys)Key-value pairs, lookup tables

Performance Comparison

import time
 
# List vs Set for membership testing
large_list = list(range(1000000))
large_set = set(range(1000000))
 
# Test membership in list (O(n))
start = time.time()
999999 in large_list
list_time = time.time() - start
 
# Test membership in set (O(1))
start = time.time()
999999 in large_set
set_time = time.time() - start
 
print(f"List lookup time: {list_time:.6f}s")
print(f"Set lookup time: {set_time:.6f}s")
# Set is significantly faster for membership testing

Common Interview Questions

1. What’s the difference between a list and a tuple?

Answer:

  • Lists are mutable, tuples are immutable
  • Lists use [], tuples use ()
  • Lists have more methods (append, remove, etc.)
  • Tuples are faster and use less memory
  • Tuples can be used as dictionary keys, lists cannot

2. How do you remove duplicates from a list?

Answer:

# Method 1: Using set (loses order)
original = [1, 2, 2, 3, 4, 4, 5]
unique = list(set(original))
print(unique)  # [1, 2, 3, 4, 5] (order may vary)
 
# Method 2: Preserving order
unique_ordered = []
for item in original:
    if item not in unique_ordered:
        unique_ordered.append(item)
print(unique_ordered)  # [1, 2, 3, 4, 5] (preserves order)
 
# Method 3: Using dict.fromkeys() (Python 3.7+)
unique_ordered = list(dict.fromkeys(original))
print(unique_ordered)  # [1, 2, 3, 4, 5] (preserves order)

3. How do you merge two dictionaries?

Answer:

# Method 1: Using update() (modifies first dict)
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
dict1.update(dict2)
print(dict1)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
 
# Method 2: Using ** unpacking (Python 3.5+)
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = {**dict1, **dict2}
print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}
 
# Method 3: Using | operator (Python 3.9+)
dict1 = {"a": 1, "b": 2}
dict2 = {"c": 3, "d": 4}
merged = dict1 | dict2
print(merged)  # {'a': 1, 'b': 2, 'c': 3, 'd': 4}

4. What’s the difference between append() and extend()?

Answer:

  • append() adds a single element to the end
  • extend() adds all elements from an iterable to the end
list1 = [1, 2, 3]
list2 = [1, 2, 3]
 
list1.append([4, 5])    # [1, 2, 3, [4, 5]]
list2.extend([4, 5])    # [1, 2, 3, 4, 5]

5. How do you sort a dictionary by value?

Answer:

scores = {"Alice": 85, "Bob": 92, "Charlie": 78, "Diana": 96}
 
# Method 1: Using sorted() with key parameter
sorted_scores = dict(sorted(scores.items(), key=lambda x: x[1]))
print(sorted_scores)  # {'Charlie': 78, 'Alice': 85, 'Bob': 92, 'Diana': 96}
 
# Method 2: Using sorted() with operator.itemgetter
from operator import itemgetter
sorted_scores = dict(sorted(scores.items(), key=itemgetter(1)))
print(sorted_scores)  # {'Charlie': 78, 'Alice': 85, 'Bob': 92, 'Diana': 96}
 
# Method 3: Sort in descending order
sorted_scores = dict(sorted(scores.items(), key=lambda x: x[1], reverse=True))
print(sorted_scores)  # {'Diana': 96, 'Bob': 92, 'Alice': 85, 'Charlie': 78}

Practice Problems

Easy Level

  1. Count Frequency of Characters
def count_characters(text):
    char_count = {}
    for char in text:
        char_count[char] = char_count.get(char, 0) + 1
    return char_count
 
# Test
text = "hello world"
result = count_characters(text)
print(result)  # {'h': 1, 'e': 1, 'l': 3, 'o': 2, ' ': 1, 'w': 1, 'r': 1, 'd': 1}
  1. Find Common Elements in Two Lists
def find_common(list1, list2):
    set1 = set(list1)
    set2 = set(list2)
    return list(set1.intersection(set2))
 
# Test
list1 = [1, 2, 3, 4, 5]
list2 = [4, 5, 6, 7, 8]
common = find_common(list1, list2)
print(common)  # [4, 5]
  1. Group Anagrams
def group_anagrams(words):
    groups = {}
    for word in words:
        # Sort characters to create key
        key = ''.join(sorted(word))
        if key not in groups:
            groups[key] = []
        groups[key].append(word)
    return list(groups.values())
 
# Test
words = ["eat", "tea", "tan", "ate", "nat", "bat"]
result = group_anagrams(words)
print(result)  # [['eat', 'tea', 'ate'], ['tan', 'nat'], ['bat']]

Medium Level

  1. Two Sum Problem
def two_sum(nums, target):
    num_map = {}
    for i, num in enumerate(nums):
        complement = target - num
        if complement in num_map:
            return [num_map[complement], i]
        num_map[num] = i
    return []
 
# Test
nums = [2, 7, 11, 15]
target = 9
result = two_sum(nums, target)
print(result)  # [0, 1] (indices of 2 and 7)
  1. Valid Parentheses
def is_valid_parentheses(s):
    stack = []
    mapping = {')': '(', '}': '{', ']': '['}
    
    for char in s:
        if char in mapping:
            if not stack or stack.pop() != mapping[char]:
                return False
        else:
            stack.append(char)
    
    return not stack
 
# Test
print(is_valid_parentheses("()[]{}"))  # True
print(is_valid_parentheses("([)]"))    # False
  1. Merge Two Sorted Lists
def merge_sorted_lists(list1, list2):
    merged = []
    i = j = 0
    
    while i < len(list1) and j < len(list2):
        if list1[i] <= list2[j]:
            merged.append(list1[i])
            i += 1
        else:
            merged.append(list2[j])
            j += 1
    
    # Add remaining elements
    merged.extend(list1[i:])
    merged.extend(list2[j:])
    
    return merged
 
# Test
list1 = [1, 3, 5, 7]
list2 = [2, 4, 6, 8]
result = merge_sorted_lists(list1, list2)
print(result)  # [1, 2, 3, 4, 5, 6, 7, 8]

Advanced Level

  1. Implement a Stack using Lists
class Stack:
    def __init__(self):
        self.items = []
    
    def push(self, item):
        self.items.append(item)
    
    def pop(self):
        if self.is_empty():
            raise IndexError("Stack is empty")
        return self.items.pop()
    
    def peek(self):
        if self.is_empty():
            raise IndexError("Stack is empty")
        return self.items[-1]
    
    def is_empty(self):
        return len(self.items) == 0
    
    def size(self):
        return len(self.items)
 
# Test
stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())  # 3
print(stack.peek()) # 2
print(stack.size()) # 2
  1. Find First Non-Repeating Character
def first_non_repeating_char(s):
    char_count = {}
    
    # Count frequency
    for char in s:
        char_count[char] = char_count.get(char, 0) + 1
    
    # Find first non-repeating
    for char in s:
        if char_count[char] == 1:
            return char
    
    return None
 
# Test
print(first_non_repeating_char("leetcode"))  # 'l'
print(first_non_repeating_char("loveleetcode"))  # 'v'

Key Takeaways for Interviews

  1. Know when to use each data structure - Lists for ordered, mutable data; tuples for fixed data; sets for unique elements; dicts for key-value pairs

  2. Understand time complexity - Set/dict lookups are O(1), list lookups are O(n)

  3. Master slicing and indexing - Very common in interviews

  4. Practice with built-in functions - map, filter, reduce, enumerate, zip are frequently used

  5. Know string methods - String manipulation is common in interviews

  6. Understand immutability - Know what can and cannot be modified


Next Steps

After mastering these data structures and built-ins, move on to:

  • Chapter 3: Intermediate Python (List comprehensions, Lambda functions, File handling, Exception handling)
  • Chapter 4: Object-Oriented Programming
  • Chapter 5: Important Libraries
  • Chapter 6: Problem-Solving (DSA)

Remember: Practice implementing these data structures from scratch and solving problems using them!