📓 Python Programming - Data Structures


1. Lists

Lists in Python: - Ordered, mutable (can be changed) collection of items.

  • Can store mixed data types.
# Creating a list
fruits = ["apple", "banana", "cherry"]
print(fruits)

➤ Indexing, Slicing & Negative Indexing:

# Indexing
print(fruits[0])  # First item
print(fruits[-1])  # Last item

# Slicing
print(fruits[0:2])  # First two items

➤ Looping Through Lists & Conditions:

for fruit in fruits:
    if fruit.startswith("b"):
        print(f"{fruit} starts with 'b'")

➤ Useful List Methods:

# Adding items
fruits.append("orange")
print(fruits)

fruits.insert(1, "mango")
print(fruits)

# Removing items
fruits.remove("banana")
print(fruits)

# Sorting
fruits.sort()
print(fruits)

➤ More List Methods & Operations:

# More list methods
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
print("Original list:", numbers)

# Count occurrences
print("Count of 1:", numbers.count(1))

# Find index
print("Index of 4:", numbers.index(4))

# Extend (vs append)
numbers.extend([7, 8])  # Adds multiple items
print("After extend:", numbers)

# Reverse
numbers.reverse()
print("After reverse:", numbers)

# Clear
temp_list = [1, 2, 3]
temp_list.clear()
print("After clear:", temp_list)

# Copy (shallow vs deep)
original = [1, [2, 3], 4]
shallow_copy = original.copy()
deep_copy = original[:]  # Simple way for lists
print("Original:", original)
print("Shallow copy:", shallow_copy)

# List concatenation and repetition
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2
print("Combined:", combined)

repeated = [0] * 5
print("Repeated:", repeated)

➤ List Comprehension with Conditions:

# Create list of squares for even numbers from 0 to 10
squares = [x**2 for x in range(11) if x % 2 == 0]
print(squares)

➤ Nested Lists (2D Lists):

# Creating a 2D list (matrix)
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
print("Matrix:", matrix)

# Accessing elements
print("Element at [1][2]:", matrix[1][2])

# Iterating through 2D list
print("All elements:")
for row in matrix:
    for element in row:
        print(element, end=" ")
    print()  # New line after each row

# Using enumerate for indices
print("With indices:")
for i, row in enumerate(matrix):
    for j, element in enumerate(row):
        print(f"[{i}][{j}] = {element}")

# List comprehension for 2D
flattened = [element for row in matrix for element in row]
print("Flattened:", flattened)

# Creating a 2D list with comprehension
multiplication_table = [[i * j for j in range(1, 4)] for i in range(1, 4)]
print("Multiplication table:", multiplication_table)

2. Tuples

Tuples: - Ordered, immutable collection.

  • Faster than lists.

  • Suitable for fixed data.

# Creating a tuple
dimensions = (10, 20, 30)
print(dimensions)

# Indexing and Slicing
print(dimensions[1])
print(dimensions[:2])

# Looping
for dim in dimensions:
    print(dim)

➤ More Tuple Operations:

# Tuple methods
numbers = (1, 2, 3, 2, 4, 2, 5)
print("Original tuple:", numbers)
print("Count of 2:", numbers.count(2))
print("Index of 3:", numbers.index(3))

# Tuple concatenation
tuple1 = (1, 2, 3)
tuple2 = (4, 5, 6)
combined = tuple1 + tuple2
print("Combined tuple:", combined)

# Tuple repetition
repeated = (0,) * 5  # Note the comma for single element tuple
print("Repeated tuple:", repeated)

# Single element tuple (important!)
single = (42,)  # Comma is required
not_tuple = (42)  # This is just an integer
print("Single element tuple:", single, type(single))
print("Not a tuple:", not_tuple, type(not_tuple))

# Nested tuples
nested = ((1, 2), (3, 4), (5, 6))
print("Nested tuple:", nested)
print("Access nested element:", nested[1][0])

# Named tuples (from collections module)
from collections import namedtuple

# Define a named tuple
Point = namedtuple('Point', ['x', 'y'])
Student = namedtuple('Student', ['name', 'age', 'grade'])

# Create instances
p = Point(10, 20)
student = Student('Alice', 20, 'A')

print("Point:", p)
print("Access by name:", p.x, p.y)
print("Student:", student)
print("Student name:", student.name)

➤ List vs Tuple:

  • List: Mutable → You can modify contents.

  • Tuple: Immutable → Contents cannot be changed.

  • Lists have more built-in methods compared to tuples.

  • Use lists for homogeneous items and tuples for heterogeneous items.

➤ Switching Between Lists and Tuples:

# List to Tuple
my_list = [1, 2, 3]
my_tuple = tuple(my_list)
print(my_tuple)

# Tuple to List
new_list = list(my_tuple)
print(new_list)

➤ Tuple Unpacking:

person = ("Alice", 25, "Engineer")
name, age, profession = person
print(name, age, profession)

3. Sets

Sets: - Unordered collection of unique items.

  • Used for mathematical operations like union, intersection, etc.

➤ Basic Set Operations:

# Creating sets
colors = {"red", "green", "blue"}
print(colors)

# Adding, Removing, Discarding
colors.add("yellow")
print(colors)

colors.remove("green")  # Raises error if not present
print(colors)

colors.discard("purple")  # No error if not present
print(colors)

➤ More Set Operations & Methods:

# Set from list (removes duplicates)
numbers = [1, 2, 2, 3, 3, 3, 4, 5]
unique_numbers = set(numbers)
print("Original list:", numbers)
print("Unique set:", unique_numbers)

# Set methods
set1 = {1, 2, 3, 4, 5}
set2 = {4, 5, 6, 7, 8}

print("Set1:", set1)
print("Set2:", set2)

# Union (all elements)
print("Union (|):", set1 | set2)
print("Union method:", set1.union(set2))

# Intersection (common elements)
print("Intersection (&):", set1 & set2)
print("Intersection method:", set1.intersection(set2))

# Difference (elements in set1 but not in set2)
print("Difference (-):", set1 - set2)
print("Difference method:", set1.difference(set2))

# Symmetric difference (elements in either set, but not both)
print("Symmetric difference (^):", set1 ^ set2)
print("Symmetric difference method:", set1.symmetric_difference(set2))

# Subset and superset checks
small_set = {1, 2}
print("Is {1, 2} subset of set1?", small_set.issubset(set1))
print("Is set1 superset of {1, 2}?", set1.issuperset(small_set))

# Disjoint sets (no common elements)
set3 = {10, 11, 12}
print("Are set1 and set3 disjoint?", set1.isdisjoint(set3))

# Set comprehension
even_squares = {x**2 for x in range(10) if x % 2 == 0}
print("Even squares:", even_squares)

➤ Set Operations:

a = {1, 2, 3, 4}
b = {3, 4, 5, 6}

print("Union:", a | b)
print("Intersection:", a & b)
print("Difference:", a - b)

➤ Frozenset vs Set:

  • set → Mutable

  • frozenset → Immutable

fs = frozenset([1, 2, 3])
print(fs)

4. Dictionaries

Dictionaries: - Collection of key-value pairs.

  • Keys must be unique.

➤ Creating & Accessing:

student = {"name": "Alice", "age": 22, "grade": "A"}
print(student["name"])

➤ Useful Dictionary Methods:

# Get with default
print(student.get("age"))
print(student.get("major", "Not specified"))

# Update
student.update({"grade": "A+", "major": "CS"})
print(student)

# Keys, Values
print(student.keys())
print(student.values())

# Pop
student.pop("age")
print(student)

➤ More Dictionary Methods & Operations:

# Dictionary from lists
keys = ['name', 'age', 'city']
values = ['Bob', 25, 'London']
person = dict(zip(keys, values))
print("Dictionary from lists:", person)

# Dictionary methods
inventory = {'apples': 50, 'bananas': 30, 'cherries': 20}
print("Original inventory:", inventory)

# Items, keys, values iteration
print("Items:")
for item in inventory.items():
    print(item)

print("Key-value pairs:")
for key, value in inventory.items():
    print(f"{key}: {value}")

# Popitem (removes and returns last item)
last_item = inventory.popitem()
print("Popped item:", last_item)
print("After popitem:", inventory)

# Setdefault (get or set default value)
inventory.setdefault('oranges', 0)
print("After setdefault:", inventory)

# Dictionary merging (Python 3.9+)
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
# merged = dict1 | dict2  # Python 3.9+
merged = {**dict1, **dict2}  # Works in older versions
print("Merged dictionary:", merged)

# Dictionary with default values
from collections import defaultdict
dd = defaultdict(int)  # Default value is 0
dd['a'] += 1
dd['b'] += 2
print("Default dict:", dict(dd))

# Counter (specialized dictionary)
from collections import Counter
text = "hello world"
char_count = Counter(text)
print("Character count:", char_count)
print("Most common:", char_count.most_common(3))

➤ Dictionary Comprehension:

# Squares of numbers
squares = {x: x**2 for x in range(5)}
print(squares)

# Filtering even squares
even_squares = {k: v for k, v in squares.items() if v % 2 == 0}
print(even_squares)

➤ Nested Dictionaries:

# Student with nested details
student = {
    "name": "Alice",
    "subjects": {"Math": 90, "Science": 95}
}

print(student["subjects"]["Math"])

🎯 Quick Recap

✅ Lists → Ordered, mutable

✅ Tuples → Ordered, immutable

✅ Sets → Unordered, unique items

✅ Dictionaries → Key-value pairs

✅ Use list/dictionary comprehension for concise code

5. Additional Data Structures

Python provides several specialized data structures in the collections module:

# Deque (double-ended queue)
from collections import deque

# Create a deque
dq = deque([1, 2, 3, 4, 5])
print("Original deque:", dq)

# Add to both ends
dq.appendleft(0)
dq.append(6)
print("After adding to both ends:", dq)

# Remove from both ends
left_item = dq.popleft()
right_item = dq.pop()
print(f"Removed {left_item} from left, {right_item} from right")
print("After removal:", dq)

# Rotate
dq.rotate(2)  # Rotate right by 2
print("After rotating right by 2:", dq)

# OrderedDict (remembers insertion order)
from collections import OrderedDict
od = OrderedDict()
od['first'] = 1
od['second'] = 2
od['third'] = 3
print("OrderedDict:", od)

# Move to end
od.move_to_end('first')
print("After moving 'first' to end:", od)

# ChainMap (combines multiple dictionaries)
from collections import ChainMap
dict1 = {'a': 1, 'b': 2}
dict2 = {'c': 3, 'd': 4}
dict3 = {'e': 5, 'f': 6}

chain = ChainMap(dict1, dict2, dict3)
print("ChainMap:", chain)
print("Value of 'a':", chain['a'])
print("All keys:", list(chain.keys()))

6. Performance & When to Use Each Structure

Time Complexity Comparison:

Operation List Tuple Set Dictionary
Access by index O(1) O(1) N/A N/A
Access by key N/A N/A N/A O(1)
Search O(n) O(n) O(1) O(1)
Insert O(n) N/A O(1) O(1)
Delete O(n) N/A O(1) O(1)

When to Use Each: - List: When you need ordered, mutable data with duplicates - Tuple: When you need ordered, immutable data (coordinates, database records) - Set: When you need unique elements and fast membership testing - Dictionary: When you need key-value mapping with fast lookups

# Practical examples of when to use each structure

# Example 1: Shopping cart (List - ordered, allows duplicates)
shopping_cart = ['apple', 'banana', 'apple', 'orange']
print("Shopping cart:", shopping_cart)

# Example 2: GPS coordinates (Tuple - immutable, fixed structure)
locations = [
    ('New York', 40.7128, -74.0060),
    ('London', 51.5074, -0.1278),
    ('Tokyo', 35.6762, 139.6503)
]
print("Locations:", locations)

# Example 3: Unique visitors (Set - no duplicates)
unique_visitors = {'user1', 'user2', 'user1', 'user3'}
print("Unique visitors:", unique_visitors)

# Example 4: User profiles (Dictionary - key-value mapping)
user_profiles = {
    'user1': {'name': 'Alice', 'age': 25},
    'user2': {'name': 'Bob', 'age': 30},
    'user3': {'name': 'Charlie', 'age': 35}
}
print("User profiles:", user_profiles)

# Memory usage comparison
import sys
list_data = [1, 2, 3, 4, 5]
tuple_data = (1, 2, 3, 4, 5)
set_data = {1, 2, 3, 4, 5}
dict_data = {1: 'a', 2: 'b', 3: 'c', 4: 'd', 5: 'e'}

print(f"List memory: {sys.getsizeof(list_data)} bytes")
print(f"Tuple memory: {sys.getsizeof(tuple_data)} bytes")
print(f"Set memory: {sys.getsizeof(set_data)} bytes")
print(f"Dict memory: {sys.getsizeof(dict_data)} bytes")

7. Converting Between Data Structures

Converting between different data structures is often necessary:

# Converting between data structures
original_list = [1, 2, 3, 2, 4, 5]
print("Original list:", original_list)

# List to other structures
list_to_tuple = tuple(original_list)
list_to_set = set(original_list)  # Removes duplicates
print("List to tuple:", list_to_tuple)
print("List to set:", list_to_set)

# Tuple to other structures
tuple_to_list = list(list_to_tuple)
tuple_to_set = set(list_to_tuple)
print("Tuple to list:", tuple_to_list)
print("Tuple to set:", tuple_to_set)

# Set to other structures
set_to_list = list(list_to_set)
set_to_tuple = tuple(list_to_set)
print("Set to list:", set_to_list)
print("Set to tuple:", set_to_tuple)

# Dictionary conversions
sample_dict = {'a': 1, 'b': 2, 'c': 3}
print("Original dict:", sample_dict)

# Dictionary to lists/tuples/sets
dict_keys = list(sample_dict.keys())
dict_values = list(sample_dict.values())
dict_items = list(sample_dict.items())
print("Dict keys:", dict_keys)
print("Dict values:", dict_values)
print("Dict items:", dict_items)

# Lists to dictionary
keys = ['name', 'age', 'city']
values = ['Alice', 25, 'Boston']
list_to_dict = dict(zip(keys, values))
print("Lists to dict:", list_to_dict)

# List of tuples to dictionary
tuple_list = [('x', 1), ('y', 2), ('z', 3)]
tuples_to_dict = dict(tuple_list)
print("Tuples to dict:", tuples_to_dict)

# String to other structures
text = "hello"
string_to_list = list(text)
string_to_tuple = tuple(text)
string_to_set = set(text)  # Removes duplicate characters
print("String to list:", string_to_list)
print("String to tuple:", string_to_tuple)
print("String to set:", string_to_set)

📝 Practice Exercises

Basic Exercises:

  1. Create a list of your favorite 5 movies and sort them.

  2. Convert a list of numbers to a tuple and unpack the first three numbers.

  3. Find common elements between two sets.

  4. Create a dictionary for a book with title, author, and year. Add a genre to it.

  5. Use dictionary comprehension to map numbers from 1 to 5 to their cubes.

Intermediate Exercises:

  1. Create a 2D list representing a 3x3 matrix and print it row by row.

  2. Remove duplicates from a list while preserving order (without using set).

  3. Create a word frequency counter using a dictionary.

  4. Use set operations to find students taking both Math and Science courses.

  5. Implement a simple phone book using nested dictionaries.

Advanced Exercises:

  1. Create a function that flattens a nested list of any depth.

  2. Build a simple inventory system using dictionaries and lists.

  3. Use collections.Counter to analyze text and find the most common words.

  4. Create a student grading system with multiple subjects and calculate averages.

  5. Implement a simple cache system using dictionaries.

Challenge Exercises:

  1. Create a data structure to represent a family tree using nested dictionaries.

  2. Build a simple database system that can store and query student records.

  3. Implement a text-based menu system for a restaurant using data structures.

  4. Create a library management system with books, authors, and borrowers.

  5. Build a simple social network representation using dictionaries and sets.