Interview Prep

Entry-Level Interview Answers

Comprehensive answers to 200+ questions for 5-10 LPA companies. Master the fundamentals that get you hired.

OOP Basics Answers (1-20)

Fundamental object-oriented programming concepts with examples.

1. What is a class and an object? Give a simple example.

Answer: A class is a blueprint or template for creating objects. An object is an instance of a class.

class Car {
  String model;
  int year;
}

Car myCar = new Car(); // myCar is an object

2. What is the difference between public, private, and protected?

Answer:

  • Public: Accessible from anywhere
  • Private: Accessible only within the same class
  • Protected: Accessible within the same class and subclasses

3. What is a constructor? What happens if you don't write one?

Answer: A constructor is a special method called when an object is created. It initializes the object. If you don't write one, Java provides a default no-argument constructor.

4. Can a constructor return a value?

Answer: No, constructors don't have a return type, not even void. They return the instance of the class implicitly.

5. What is 'this' keyword? Give an example of when you use it.

Answer: 'this' refers to the current object instance. Used to distinguish between instance variables and parameters.

class Person {
  String name;
  Person(String name) {
    this.name = name; // 'this.name' is instance variable
  }
}

6. What is inheritance? Write a simple example.

Answer: Inheritance allows a class to inherit properties and methods from another class.

class Animal {
  void eat() { System.out.println("Eating"); }
}

class Dog extends Animal {
  void bark() { System.out.println("Barking"); }
}

7. What is the difference between method overloading and overriding?

Answer:

  • Overloading: Same method name, different parameters in the same class
  • Overriding: Same method name and parameters in subclass

8. Can you have two methods with the same name in a class?

Answer: Yes, through method overloading (different parameters) or overriding (in subclasses).

9. What is a destructor? Do you need to write one in Java?

Answer: A destructor cleans up resources when an object is destroyed. Java has automatic garbage collection, so you don't need to write destructors. Use finalize() method if needed.

10. What does 'static' mean? Why is the main() method static?

Answer: Static means it belongs to the class, not instances. main() is static so JVM can call it without creating an object.

11. Can you access a non-static variable from a static method?

What You'll Learn

Understanding static vs instance members.

Real-World Context

"Scope and accessibility of class members."

Breaking It Down

  • Static methods belong to the class, not instances
  • They can only access static variables and methods
  • Instance variables require an object instance
Python 3
class Example:
    static_var = "static"
    def __init__(self):
        self.instance_var = "instance"
    
    @staticmethod
    def static_method():
        return Example.static_var  # ✅ Works
        # return self.instance_var  # ❌ Error
    
    def instance_method(self):
        return self.instance_var  # ✅ Works

Line-by-Line Walkthrough

Static methods are called on the class itself and don't have access to 'this' or instance variables. This is why main() is static - it needs to run before any objects exist.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Simple access check.

Common Mistakes

  • Trying to use 'this' in static method
    Static methods don't have 'this' pointer

Try It Yourself

Create a static counter that tracks how many objects of a class have been created.

12. What is the final keyword used for?

What You'll Learn

Immutability and inheritance control.

Real-World Context

"Making classes, methods, or variables unchangeable."

Breaking It Down

  • For variables: makes them constants
  • For methods: prevents overriding
  • For classes: prevents inheritance
Python 3
# Python equivalent using naming convention
class Example:
    CONSTANT = "final"  # Convention: uppercase means final
    
    def normal_method(self):
        return "can be overridden"
    
    # No direct equivalent to final methods in Python

Line-by-Line Walkthrough

Final prevents modification or extension. Variables become constants, methods can't be overridden, and classes can't be inherited. This is similar to Python's naming conventions and frozen classes.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Compile-time restrictions.

Common Mistakes

  • Trying to override final method
    Remove final keyword if overriding is needed

Try It Yourself

Design a class hierarchy where some methods are final and some are not.

13. What is an abstract class? Can you create an object of it?

What You'll Learn

Blueprint classes for inheritance.

Real-World Context

"Classes that define structure but not complete implementation."

Breaking It Down

  • Abstract classes contain abstract methods (no implementation)
  • Concrete subclasses must implement all abstract methods
  • Cannot instantiate abstract classes directly
Python 3
from abc import ABC, abstractmethod

class Animal(ABC):  # Abstract class
    @abstractmethod
    def make_sound(self):
        pass  # No implementation
    
    def breathe(self):
        return "breathing"  # Concrete method

class Dog(Animal):
    def make_sound(self):
        return "woof"  # Must implement

# animal = Animal()  # ❌ Error: can't instantiate
dog = Dog()  # ✅ Works

Line-by-Line Walkthrough

Abstract classes define 'what' should be done, concrete classes define 'how'. They're essential for creating frameworks and ensuring subclasses implement required functionality.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Design-time concept.

Common Mistakes

  • Trying to instantiate abstract class
    Create concrete subclass and instantiate that

Try It Yourself

Create an abstract Shape class with area() method, and concrete Circle and Rectangle classes.

14. What is an interface? How is it different from an Abstract Class?

What You'll Learn

Contracts for class behavior.

Real-World Context

"Pure behavior specification without implementation."

Breaking It Down

  • Interface defines method signatures only
  • Classes implement interfaces
  • Can implement multiple interfaces
  • No instance variables (only constants)
Python 3
from abc import ABC, abstractmethod

class Flyable(ABC):  # Interface equivalent
    @abstractmethod
    def fly(self):
        pass

class Swimmable(ABC):
    @abstractmethod
    def swim(self):
        pass

class Duck(Flyable, Swimmable):  # Multiple inheritance
    def fly(self):
        return "flying"
    
    def swim(self):
        return "swimming"

Line-by-Line Walkthrough

Interfaces are like contracts - they specify what a class must do, but not how. Unlike abstract classes, interfaces support multiple inheritance and contain no implementation.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Pure design concept.

Common Mistakes

  • Adding implementation to interface methods
    Interfaces should only have method signatures

Try It Yourself

Design interfaces for Vehicle (start, stop) and MusicPlayer (play, pause) and implement in a CarWithStereo class.

15. Can an interface have variables? Can it have methods with code?

What You'll Learn

Interface capabilities and limitations.

Real-World Context

"What can and cannot be in an interface."

Breaking It Down

  • Variables: Only public static final constants
  • Methods: Can have default methods with implementation (Java 8+)
  • Abstract methods: Must be implemented by implementing classes
Python 3
# Python protocols/interfaces don't have variables
from typing import Protocol

class Drawable(Protocol):
    def draw(self) -> None: ...
    
    # No variables in protocol itself
    # Implementing classes define their own variables

Line-by-Line Walkthrough

Modern interfaces can have default implementations and constants, but the core purpose remains behavior specification. This allows interface evolution without breaking existing implementations.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Language feature.

Common Mistakes

  • Trying to add non-final variables to interface
    Use constants or put variables in implementing classes

Try It Yourself

Create an interface with a default method and see how implementing classes can override or inherit it.

Practical OOP Answers (21-50)

Hands-on OOP concepts with code examples.

21. Write a class for a Bank Account with deposit and withdraw methods.

What You'll Learn

Basic class design with encapsulation.

Real-World Context

"Creating a simple banking system class."

Breaking It Down

  • Create a class with private balance field
  • Add deposit method to increase balance
  • Add withdraw method to decrease balance with validation
  • Add getter for balance
Python 3
class BankAccount:
    def __init__(self, initial_balance=0):
        self.__balance = initial_balance
    
    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
    
    def withdraw(self, amount):
        if 0 < amount <= self.__balance:
            self.__balance -= amount
            return True
        return False
    
    def get_balance(self):
        return self.__balance

Line-by-Line Walkthrough

This demonstrates encapsulation by keeping balance private and providing controlled access through methods.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Simple operations on a single field.

Common Mistakes

  • Making balance public
    Use private field with getter methods

Try It Yourself

Add interest calculation method.

22. How do you prevent a class from being inherited?

What You'll Learn

Inheritance control mechanisms.

Real-World Context

"Making classes final or sealed."

Breaking It Down

  • Use 'final' keyword in Java
  • Use 'sealed' keyword in C# (newer versions)
  • Make constructor private (Singleton pattern)
Python 3
# Python doesn't have direct equivalent
# Convention: use _ prefix for "private" but still inheritable

class FinalClass:
    def __init__(self):
        self._private = "can't inherit easily"
    
    def __init_subclass__(cls, **kwargs):
        raise TypeError("Cannot inherit from FinalClass")

# class Child(FinalClass):  # ❌ Will raise TypeError
#     pass

Line-by-Line Walkthrough

Final classes prevent inheritance for security or design reasons. Private constructors prevent direct instantiation but allow controlled creation through factory methods.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Language-level restriction.

Common Mistakes

  • Thinking final methods prevent inheritance
    Final classes prevent inheritance, final methods prevent overriding

Try It Yourself

Implement a Singleton class that cannot be inherited.

23. What is the difference between pass by value and pass by reference?

What You'll Learn

Parameter passing mechanisms.

Real-World Context

"How arguments are passed to functions/methods."

Breaking It Down

  • Pass by value: Copy of the value is passed
  • Pass by reference: Reference/address is passed
  • Changes to reference affect original, changes to value don't
Python 3
def pass_by_value(x):
    x = x + 1  # Changes local copy
    print(f"Inside function: {x}")

def pass_by_reference(lst):
    lst.append(4)  # Changes original list
    print(f"Inside function: {lst}")

num = 5
pass_by_value(num)
print(f"Outside function: {num}")  # Still 5

my_list = [1, 2, 3]
pass_by_reference(my_list)
print(f"Outside function: {my_list}")  # [1, 2, 3, 4]

Line-by-Line Walkthrough

Understanding parameter passing is crucial for debugging. Python passes references to objects but copies immutable types. C++ gives explicit control with & for references.

Complexity Analysis

  • Time: O(1)
  • Space: O(1) for primitives, O(n) for objects
  • Depends on what is being passed.

Common Mistakes

  • Thinking Python always passes by reference
    Python passes references to objects, but objects might be immutable

Try It Yourself

Write a swap function that works correctly in both languages.

24. What is a memory leak? Give an example.

What You'll Learn

Memory management issues.

Real-World Context

"When memory is not properly freed."

Breaking It Down

  • Memory leak: Allocated memory not deallocated
  • Causes: Forgetting to free/delete, circular references
  • Symptoms: Program uses more memory over time
Python 3
# Python has garbage collection, but circular references can cause leaks
class Node:
    def __init__(self, value):
        self.value = value
        self.next = None

def create_circular_reference():
    node1 = Node(1)
    node2 = Node(2)
    node1.next = node2
    node2.next = node1  # Circular reference
    
    # Without breaking the cycle, these objects won't be garbage collected
    # even after function ends
    
    return node1

# Better approach: use weak references for circular structures
import weakref

class BetterNode:
    def __init__(self, value):
        self.value = value
        self.next = None  # Use weakref.ref() if needed

Line-by-Line Walkthrough

Memory leaks cause programs to consume increasing amounts of memory. In languages with manual memory management like C++, forgetting to delete/free is common. Python's garbage collector helps but circular references can still cause issues.

Complexity Analysis

  • Time: Varies
  • Space: Accumulates over time
  • Memory not reclaimed.

Common Mistakes

  • Relying on garbage collection in C++
    C++ requires manual memory management

Try It Yourself

Write a program that demonstrates a memory leak and then fix it.

25. What is the difference between '==' and 'equals()' method?

What You'll Learn

Reference vs content comparison.

Real-World Context

"How to properly compare objects in Java."

Breaking It Down

  • '==' compares memory addresses (references)
  • '.equals()' compares content/values
  • For primitives, '==' compares values
  • Override equals() for custom comparison logic
Python 3
# Python uses '==' for content comparison (like equals())
# 'is' is like '=='

class Person:
    def __init__(self, name):
        self.name = name
    
    def __eq__(self, other):  # Override == behavior
        if isinstance(other, Person):
            return self.name == other.name
        return False

person1 = Person("John")
person2 = Person("John")
person3 = person1

print(person1 == person2)  # True (content comparison)
print(person1 is person2)  # False (different objects)
print(person1 is person3)  # True (same object)

Line-by-Line Walkthrough

'==' checks if two references point to the same memory location. '.equals()' (or overridden operators) checks if the contents are the same. Always override equals() for custom classes when you want content-based comparison.

Complexity Analysis

  • Time: O(1) for references, varies for content
  • Space: O(1)
  • Reference comparison is instant, content depends on data structure.

Common Mistakes

  • Using == for String comparison in Java
    Use .equals() for String content comparison

Try It Yourself

Create a custom class and override equals() to compare based on multiple fields.

36-50. Memory & Advanced Basics

36. What is the difference between stack and heap memory? Stack for local variables, heap for objects.

37. What is garbage collection? Automatic memory management in Java.

38. What is exception handling? try-catch-finally blocks.

39. What is the difference between throw and throws? throw throws exception, throws declares it.

40. What is an ArrayList? Dynamic array implementation.

41. What is a LinkedList? Doubly linked list, good for insertions/deletions.

42. What is a HashSet? Set implementation using hash table, no duplicates.

43. What is the difference between Iterator and ListIterator? ListIterator can traverse backwards.

44. What is a Vector in Java? Thread-safe ArrayList.

45. What is the difference between Comparable and Comparator? Comparable for natural ordering, Comparator for custom.

46. What is serialization in Java? Converting object to byte stream.

47. What is the transient keyword? Excludes field from serialization.

48. What is the volatile keyword? Ensures visibility of changes across threads.

49. What is the synchronized keyword? Makes method/block thread-safe.

50. What is a deadlock? Two threads waiting for each other's resources.

SQL Answers (51-100)

Database query fundamentals and practical examples.

51. What is SQL? What does RDBMS stand for?

Answer: SQL (Structured Query Language) is used to manage relational databases. RDBMS stands for Relational Database Management System.

52. What is a Primary Key? Can it be NULL?

Answer: Primary Key uniquely identifies each record. No, it cannot be NULL.

57. Write a query to create a simple Employee table with id, name, salary.

What You'll Learn

Basic table creation.

Real-World Context

"Creating a fundamental employee table."

Breaking It Down

  • Use CREATE TABLE statement
  • Define columns with data types
  • Add constraints like PRIMARY KEY
Python 3
-- SQL Query
CREATE TABLE Employee (
    id INT PRIMARY KEY,
    name VARCHAR(100),
    salary DECIMAL(10,2)
);

Line-by-Line Walkthrough

This creates a basic table structure. Always specify data types and constraints appropriately.

Complexity Analysis

  • Time: O(1)
  • Space: Minimal
  • DDL operation, no data processing.

Common Mistakes

  • Not specifying data types
    Always define appropriate data types for columns

Try It Yourself

Add department_id as foreign key.

58. Write a query to insert 3 records into the Employee table.

What You'll Learn

Basic INSERT operations.

Real-World Context

"Adding multiple records to a database table."

Breaking It Down

  • Use INSERT INTO statement
  • Specify table name
  • List column names (optional but recommended)
  • Provide VALUES for each record
Python 3
-- SQL Query
INSERT INTO Employee (id, name, salary) VALUES
(1, 'John Doe', 50000),
(2, 'Jane Smith', 55000),
(3, 'Bob Johnson', 48000);

Line-by-Line Walkthrough

INSERT statements add new records to tables. Always specify column names for clarity and to avoid issues if table structure changes. Multiple records can be inserted in one statement for efficiency.

Complexity Analysis

  • Time: O(1) per record
  • Space: O(1)
  • Simple data insertion operation.

Common Mistakes

  • Not specifying column names
    Always list columns explicitly for maintainability

Try It Yourself

Insert records with some NULL values and see how it affects queries.

59. Write a query to update the salary of an employee with id = 5.

What You'll Learn

Basic UPDATE operations.

Real-World Context

"Modifying existing records in a database."

Breaking It Down

  • Use UPDATE statement
  • Specify table name
  • Use WHERE clause to identify specific record(s)
  • Set new values with SET clause
Python 3
-- SQL Query
UPDATE Employee
SET salary = 60000
WHERE id = 5;

Line-by-Line Walkthrough

UPDATE modifies existing data. Always use WHERE clause to avoid updating all records accidentally. Without WHERE, all rows in the table would be updated.

Complexity Analysis

  • Time: O(1) to O(n)
  • Space: O(1)
  • Depends on how many records match the WHERE condition.

Common Mistakes

  • Forgetting WHERE clause
    Always include WHERE or you'll update all records

Try It Yourself

Update salaries for all employees in a specific department with a percentage increase.

60. Write a query to delete all employees with salary less than 20000.

What You'll Learn

Basic DELETE operations.

Real-World Context

"Removing records from a database table."

Breaking It Down

  • Use DELETE FROM statement
  • Specify table name
  • Use WHERE clause to identify records to delete
  • Be careful - this permanently removes data
Python 3
-- SQL Query
DELETE FROM Employee
WHERE salary < 20000;

Line-by-Line Walkthrough

DELETE permanently removes records. Always use WHERE clause to specify which records to delete. Without WHERE, all records in the table would be deleted. Consider using SELECT first to verify which records will be affected.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • May need to scan many records to find matches.

Common Mistakes

  • Forgetting WHERE clause
    Test with SELECT first, then use same WHERE in DELETE

Try It Yourself

Delete employees who haven't logged in for 2 years (assuming you have a last_login column).

72. What is INNER JOIN? Write a simple example.

What You'll Learn

Combining data from multiple tables.

Real-World Context

"Retrieving related data from two tables."

Breaking It Down

  • Identify related columns (foreign keys)
  • Use INNER JOIN to combine tables
  • Specify join condition with ON clause
  • Select desired columns from both tables
Python 3
-- SQL Query
SELECT e.name, d.department_name
FROM Employee e
INNER JOIN Department d ON e.department_id = d.id;

Line-by-Line Walkthrough

INNER JOIN returns only records that have matching values in both tables. If an employee has no department or a department has no employees, those records won't appear in the result.

Complexity Analysis

  • Time: O(n*m) worst case
  • Space: O(n*m)
  • Cartesian product of matching records.

Common Mistakes

  • Using wrong join condition
    Verify foreign key relationships before joining

Try It Yourself

Join three tables: Employee, Department, and Project to show employees working on projects.

76. Write a query to find all employees and their department names (using JOIN).

What You'll Learn

Practical JOIN usage.

Real-World Context

"Getting employee information with department details."

Breaking It Down

  • Identify the tables: Employee and Department
  • Find the relationship: department_id foreign key
  • Use appropriate JOIN type
  • Select relevant columns
Python 3
-- SQL Query using INNER JOIN
SELECT e.id, e.name, e.salary, d.department_name
FROM Employee e
INNER JOIN Department d ON e.department_id = d.id;

Line-by-Line Walkthrough

This query combines employee personal information with organizational structure. INNER JOIN ensures only employees with valid departments are shown. LEFT JOIN would include employees without departments.

Complexity Analysis

  • Time: O(n log n) typically
  • Space: O(n)
  • Database uses indexes for efficient joins.

Common Mistakes

  • Selecting wrong columns
    Choose columns that provide meaningful information

Try It Yourself

Add a WHERE clause to show only employees from 'Engineering' department.

77. Write a query to count the number of employees in each department.

What You'll Learn

GROUP BY and aggregate functions.

Real-World Context

"Summarizing data by categories."

Breaking It Down

  • Use GROUP BY to group records by department
  • Use COUNT(*) to count employees per group
  • Include department name in SELECT
  • Optionally filter with HAVING
Python 3
-- SQL Query
SELECT d.department_name, COUNT(e.id) as employee_count
FROM Department d
LEFT JOIN Employee e ON d.id = e.department_id
GROUP BY d.id, d.department_name;

Line-by-Line Walkthrough

GROUP BY creates groups of records with the same value. Aggregate functions like COUNT work on each group. LEFT JOIN ensures departments with 0 employees are included.

Complexity Analysis

  • Time: O(n log n)
  • Space: O(k) where k is number of groups
  • Sorting/grouping operation required.

Common Mistakes

  • Using WHERE instead of HAVING
    WHERE filters before grouping, HAVING filters after

Try It Yourself

Find departments with average salary above 60000.

84. Write a query to find the second highest salary. (Without using LIMIT or TOP)

What You'll Learn

Subqueries and MAX functions.

Real-World Context

"Finding nth highest value without LIMIT."

Breaking It Down

  • Find the maximum salary
  • Find the maximum salary that is less than the overall maximum
  • Use subquery or self-join approach
Python 3
-- Method 1: Using subquery
SELECT MAX(salary) as second_highest
FROM Employee
WHERE salary < (SELECT MAX(salary) FROM Employee);

Line-by-Line Walkthrough

This finds the second highest by excluding the absolute highest value. The subquery approach is more efficient. For nth highest, you can modify the condition accordingly.

Complexity Analysis

  • Time: O(n log n)
  • Space: O(1)
  • Requires sorting to find max values.

Common Mistakes

  • Not handling duplicates
    Use DISTINCT if salaries can be the same

Try It Yourself

Find the third highest salary using the same approach.

System Concepts & Web Answers (101-130)

Web technologies and general programming concepts.

101. What is HTTP? What is HTTPS?

What You'll Learn

Web communication protocols.

Real-World Context

"How browsers and servers communicate."

Breaking It Down

  • HTTP: HyperText Transfer Protocol for web communication
  • HTTPS: Secure HTTP with SSL/TLS encryption
  • HTTP is plaintext, HTTPS encrypts data
  • HTTPS uses port 443, HTTP uses port 80
Python 3
# Python HTTP request
import requests

# HTTP (insecure)
response = requests.get('http://example.com')
print(f"Status: {response.status_code}")

# HTTPS (secure)
response = requests.get('https://example.com')
print(f"Secure connection: {response.url.startswith('https://')}")

Line-by-Line Walkthrough

HTTP is the foundation of web communication. HTTPS adds encryption to protect sensitive data like passwords and credit card information. Modern websites should always use HTTPS.

Complexity Analysis

  • Time: O(1) for connection, varies for data transfer
  • Space: O(data size)
  • Network operations depend on data amount.

Common Mistakes

  • Using HTTP for sensitive data
    Always use HTTPS for login forms and financial data

Try It Yourself

Check if a website supports HTTP/2 using browser developer tools.

102. What are HTTP methods? Explain GET and POST.

What You'll Learn

HTTP request types.

Real-World Context

"Different ways to interact with web servers."

Breaking It Down

  • GET: Retrieve data from server
  • POST: Send data to server to create/update resources
  • PUT: Update existing resource
  • DELETE: Remove resource
  • PATCH: Partial update
Python 3
import requests

# GET request - retrieve data
response = requests.get('https://api.example.com/users')
print(f"GET status: {response.status_code}")
print(f"Data: {response.json()}")

# POST request - send data
user_data = {'name': 'John', 'email': 'john@example.com'}
response = requests.post('https://api.example.com/users', 
                        json=user_data)
print(f"POST status: {response.status_code}")
print(f"Created user: {response.json()}")

Line-by-Line Walkthrough

GET requests are safe and can be cached/bookmarked. POST requests modify server state and should not be repeated. Understanding these is crucial for REST API design.

Complexity Analysis

  • Time: O(network latency)
  • Space: O(request/response size)
  • Network-bound operations.

Common Mistakes

  • Using GET for sensitive data
    Sensitive data goes in POST body, not URL parameters

Try It Yourself

Implement a simple REST API client that supports CRUD operations.

116. What is an algorithm? What is time complexity?

What You'll Learn

Algorithm analysis fundamentals.

Real-World Context

"Measuring algorithm efficiency."

Breaking It Down

  • Algorithm: Step-by-step procedure to solve a problem
  • Time complexity: How execution time grows with input size
  • Big O notation: Upper bound of growth rate
  • Common complexities: O(1), O(log n), O(n), O(n log n), O(n²)
Python 3
# O(1) - Constant time
def get_first_element(arr):
    return arr[0] if arr else None

# O(n) - Linear time
def find_max(arr):
    max_val = arr[0]
    for num in arr:  # Visits each element once
        if num > max_val:
            max_val = num
    return max_val

# O(n²) - Quadratic time
def bubble_sort(arr):
    for i in range(len(arr)):
        for j in range(len(arr) - i - 1):  # Nested loops
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr

Line-by-Line Walkthrough

Time complexity helps predict how an algorithm will perform with large inputs. O(1) is ideal, O(n²) algorithms become unusable for large datasets. Understanding this guides algorithm selection.

Complexity Analysis

  • Time: Varies by algorithm
  • Space: Varies by algorithm
  • Algorithm analysis is about asymptotic behavior.

Common Mistakes

  • Confusing worst-case with average-case
    Always consider worst-case scenario for reliability

Try It Yourself

Analyze the time complexity of finding duplicates in an array using different approaches.

117. What is O(n)? What is O(1)?

What You'll Learn

Specific complexity classes.

Real-World Context

"Understanding common time complexities."

Breaking It Down

  • O(1): Constant time - execution time doesn't depend on input size
  • O(n): Linear time - execution time grows linearly with input size
  • O(1) examples: array access by index, hash table lookup
  • O(n) examples: linear search, iterating through array
Python 3
# O(1) examples
def get_element_by_index(arr, index):
    return arr[index]  # Same time regardless of array size

def check_if_empty(arr):
    return len(arr) == 0  # Constant time operation

# O(n) examples
def find_element_linear(arr, target):
    for item in arr:  # Time grows with array size
        if item == target:
            return True
    return False

def sum_array(arr):
    total = 0
    for num in arr:  # Must visit each element
        total += num
    return total

Line-by-Line Walkthrough

O(1) operations are the fastest - their performance doesn't degrade with size. O(n) operations are acceptable for most applications but can be slow for very large datasets.

Complexity Analysis

  • Time: O(1) or O(n)
  • Space: O(1)
  • These are the most fundamental complexity classes.

Common Mistakes

  • Thinking array access is O(n)
    Random access arrays provide O(1) access by index

Try It Yourself

Identify O(1) vs O(n) operations in common programming tasks.

Coding Problems Answers (131-200)

Practical programming problems with solutions.

131. Write a program to check if a number is even or odd.

What You'll Learn

Basic conditional logic.

Real-World Context

"Simple number classification."

Breaking It Down

  • Take input number
  • Check if divisible by 2
  • Print appropriate message
Python 3
def check_even_odd(n):
    if n % 2 == 0:
        print(f"{n} is even")
    else:
        print(f"{n} is odd")

# Example usage
check_even_odd(5)  # Output: 5 is odd

Line-by-Line Walkthrough

Uses modulo operator to check divisibility by 2. This is a fundamental conditional statement.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Single operation regardless of input size.

Common Mistakes

  • Using n/2 == 0
    Use n % 2 == 0 for proper even check

Try It Yourself

Handle negative numbers.

132. Write a program to find the largest of 3 numbers.

What You'll Learn

Multiple condition comparison.

Real-World Context

"Finding maximum among three values."

Breaking It Down

  • Take three numbers as input
  • Compare them using nested if-else
  • Print the largest number
Python 3
def find_largest(a, b, c):
    if a >= b and a >= c:
        return a
    elif b >= a and b >= c:
        return b
    else:
        return c

# Example usage
print(find_largest(10, 25, 15))  # Output: 25

Line-by-Line Walkthrough

Uses logical AND operators to compare all combinations. This approach scales to more numbers.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Fixed number of comparisons.

Common Mistakes

  • Using multiple if statements
    Use if-else chain to avoid unnecessary checks

Try It Yourself

Find largest of n numbers using array.

133. Write a program to reverse a string.

What You'll Learn

String manipulation basics.

Real-World Context

"Reversing character order in a string."

Breaking It Down

  • Take string input
  • Use slicing or loop to reverse
  • Print reversed string
Python 3
def reverse_string(s):
    return s[::-1]

# Example usage
print(reverse_string("hello"))  # Output: olleh

Line-by-Line Walkthrough

Python uses slicing, C++ uses std::reverse algorithm. Both are efficient for string reversal.

Complexity Analysis

  • Time: O(n)
  • Space: O(n)
  • Creates new string of same length.

Common Mistakes

  • Trying to reverse in-place
    Strings are immutable in Python, use slicing

Try It Yourself

Reverse words in a sentence.

134-145. More Easy Problems

134. Write a program to check if a string is a palindrome.

What You'll Learn

String reversal and comparison.

Real-World Context

"Checking if a string reads the same forwards and backwards."

Breaking It Down

  • Take input string
  • Compare string with its reverse
  • Return true if they match
  • Handle case sensitivity and spaces as needed
Python 3
def is_palindrome(s):
    # Remove spaces and convert to lowercase for case-insensitive check
    cleaned = ''.join(c.lower() for c in s if c.isalnum())
    return cleaned == cleaned[::-1]

# Example usage
print(is_palindrome("A man, a plan, a canal: Panama"))  # True
print(is_palindrome("race a car"))  # False

Line-by-Line Walkthrough

A palindrome reads the same forwards and backwards. The key is cleaning the string (removing punctuation, spaces, and handling case) before comparison. Python's slicing makes this elegant.

Complexity Analysis

  • Time: O(n)
  • Space: O(n)
  • Creating cleaned string and reverse copy.

Common Mistakes

  • Not handling case sensitivity
    Convert to lowercase before comparison

Try It Yourself

Check if a number is a palindrome (without converting to string).

135. Write a program to find the factorial of a number.

What You'll Learn

Recursive and iterative solutions.

Real-World Context

"Calculating n! (n factorial)."

Breaking It Down

  • Handle base case: 0! = 1
  • For iterative: multiply numbers from 1 to n
  • For recursive: n! = n * (n-1)!
  • Handle negative numbers (factorial undefined)
Python 3
def factorial_iterative(n):
    if n < 0:
        raise ValueError("Factorial not defined for negative numbers")
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

def factorial_recursive(n):
    if n < 0:
        raise ValueError("Factorial not defined for negative numbers")
    if n == 0 or n == 1:
        return 1
    return n * factorial_recursive(n - 1)

# Example usage
print(factorial_iterative(5))  # 120
print(factorial_recursive(5))  # 120

Line-by-Line Walkthrough

Factorial grows very quickly, so use appropriate data types (long long in C++). Iterative solution is better to avoid stack overflow. Both solutions handle the base cases properly.

Complexity Analysis

  • Time: O(n)
  • Space: O(1) iterative, O(n) recursive
  • Iterative uses constant space, recursive uses stack space.

Common Mistakes

  • Not handling n = 0
    0! is defined as 1

Try It Yourself

Calculate factorial using big integer libraries for large numbers.

136. Write a program to print Fibonacci series up to n terms.

What You'll Learn

Sequence generation with multiple approaches.

Real-World Context

"Generating the Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8..."

Breaking It Down

  • Handle first two terms: 0 and 1
  • Each subsequent term is sum of previous two
  • Use iterative approach to avoid recursion depth issues
  • Print or return the sequence
Python 3
def fibonacci_iterative(n):
    if n <= 0:
        return []
    elif n == 1:
        return [0]
    elif n == 2:
        return [0, 1]
    
    fib_sequence = [0, 1]
    for i in range(2, n):
        next_fib = fib_sequence[i-1] + fib_sequence[i-2]
        fib_sequence.append(next_fib)
    
    return fib_sequence

def fibonacci_recursive(n):
    if n <= 0:
        return []
    elif n == 1:
        return [0]
    elif n == 2:
        return [0, 1]
    else:
        fib = fibonacci_recursive(n-1)
        fib.append(fib[-1] + fib[-2])
        return fib

# Example usage
print(fibonacci_iterative(10))  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
print(fibonacci_recursive(10))  # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Line-by-Line Walkthrough

Fibonacci sequence is fundamental in algorithm analysis. The iterative approach is more efficient and avoids recursion limits. Use long long for larger terms to prevent overflow.

Complexity Analysis

  • Time: O(n)
  • Space: O(n)
  • Store all n terms in a list/vector.

Common Mistakes

  • Starting sequence with 1, 1, 2...
    Standard Fibonacci starts with 0, 1, 1, 2...

Try It Yourself

Find the nth Fibonacci number using matrix exponentiation for O(log n) time.

137. Write a program to count vowels in a string.

What You'll Learn

String iteration and character checking.

Real-World Context

"Counting vowel characters (a, e, i, o, u) in text."

Breaking It Down

  • Define vowels (consider case sensitivity)
  • Iterate through each character in string
  • Check if character is a vowel
  • Increment counter for each vowel found
Python 3
def count_vowels(s):
    vowels = 'aeiouAEIOU'
    count = 0
    for char in s:
        if char in vowels:
            count += 1
    return count

# Example usage
print(count_vowels("Hello World"))  # 3 (e, o, o)
print(count_vowels("Programming"))  # 3 (o, a, i)

Line-by-Line Walkthrough

This demonstrates basic string processing. The approach is straightforward: check each character against a set of vowels. Consider whether to count both uppercase and lowercase.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • Single pass through string, constant extra space.

Common Mistakes

  • Only checking lowercase vowels
    Include both uppercase and lowercase vowels

Try It Yourself

Count consonants instead of vowels, or create a frequency map of all characters.

138. Write a program to check if a number is prime.

What You'll Learn

Prime number detection algorithm.

Real-World Context

"Determining if a number has no positive divisors other than 1 and itself."

Breaking It Down

  • Handle edge cases: numbers < 2 are not prime
  • Check divisibility from 2 to √n
  • If any divisor found, number is not prime
  • If no divisors found, number is prime
Python 3
import math

def is_prime(n):
    if n <= 1:
        return False
    if n <= 3:
        return True
    if n % 2 == 0 or n % 3 == 0:
        return False
    
    # Check divisibility up to √n
    for i in range(5, int(math.sqrt(n)) + 1, 6):
        if n % i == 0 or n % (i + 2) == 0:
            return False
    return True

# Example usage
print(is_prime(17))  # True
print(is_prime(18))  # False
print(is_prime(23))  # True

Line-by-Line Walkthrough

The √n optimization reduces checks significantly. For n=100, we check up to 10 instead of 99. The +6 pattern skips multiples of 2 and 3 for additional efficiency.

Complexity Analysis

  • Time: O(√n)
  • Space: O(1)
  • Check up to square root of n, constant space.

Common Mistakes

  • Checking divisibility up to n/2
    Only need to check up to √n

Try It Yourself

Implement the Sieve of Eratosthenes to find all primes up to n.

146. Write a program to find the largest element in an array.

What You'll Learn

Array traversal basics.

Real-World Context

"Finding maximum value in an array."

Breaking It Down

  • Initialize max with first element
  • Iterate through array
  • Update max if current element is larger
  • Return max value
Python 3
def find_max(arr):
    if not arr: return None
    max_val = arr[0]
    for num in arr:
        if num > max_val:
            max_val = num
    return max_val

# Example usage
print(find_max([3, 7, 2, 9, 1]))  # Output: 9

Line-by-Line Walkthrough

Simple linear search through array. This is the basic pattern for finding min/max in collections.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • Single pass through array, constant extra space.

Common Mistakes

  • Initializing max to 0
    Use first element to handle negative numbers

Try It Yourself

Find both min and max in single pass.

147. Write a program to find the second largest element in an array.

What You'll Learn

Multiple variable tracking.

Real-World Context

"Finding runner-up maximum value."

Breaking It Down

  • Initialize first and second max variables
  • Iterate through array once
  • Update both variables as needed
  • Handle edge cases (array size < 2)
Python 3
def second_largest(arr):
    if len(arr) < 2:
        return None
    
    first = second = float('-inf')
    for num in arr:
        if num > first:
            second = first
            first = num
        elif num > second and num != first:
            second = num
    
    return second if second != float('-inf') else None

# Example usage
print(second_largest([3, 7, 2, 9, 1]))  # Output: 7
print(second_largest([9, 9, 8]))  # Output: 8

Line-by-Line Walkthrough

Maintains two variables to track the largest and second largest. Handles duplicates correctly by checking num != first.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • Single pass, constant extra space.

Common Mistakes

  • Not handling duplicates
    Check num != first when updating second

Try It Yourself

Find kth largest element using sorting or heap.

148. Write a program to reverse an array.

What You'll Learn

In-place array manipulation.

Real-World Context

"Reversing elements without extra space."

Breaking It Down

  • Use two pointers (start and end)
  • Swap elements at pointers
  • Move pointers towards center
  • Stop when pointers meet or cross
Python 3
def reverse_array(arr):
    left, right = 0, len(arr) - 1
    while left < right:
        arr[left], arr[right] = arr[right], arr[left]
        left += 1
        right -= 1
    return arr

# Example usage
arr = [1, 2, 3, 4, 5]
reverse_array(arr)
print(arr)  # Output: [5, 4, 3, 2, 1]

Line-by-Line Walkthrough

Two-pointer technique swaps elements from outside in. This is O(n) time and O(1) extra space, much better than creating a new array.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • In-place reversal with constant extra space.

Common Mistakes

  • Using extra array
    Swap in-place using two pointers

Try It Yourself

Reverse only part of the array (between given indices).

149. Write a program to check if an array is sorted.

What You'll Learn

Sequential comparison.

Real-World Context

"Verifying if array elements are in ascending order."

Breaking It Down

  • Iterate through array from index 1 to end
  • Compare each element with previous
  • Return false if any element is smaller than previous
  • Return true if all comparisons pass
Python 3
def is_sorted(arr):
    if not arr:
        return True  # Empty array is considered sorted
    
    for i in range(1, len(arr)):
        if arr[i] < arr[i-1]:
            return False
    return True

# Example usage
print(is_sorted([1, 2, 3, 4, 5]))  # True
print(is_sorted([1, 3, 2, 4, 5]))  # False

Line-by-Line Walkthrough

Simple linear scan checking adjacent elements. This assumes ascending order - modify the comparison for descending order.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • Single pass through array.

Common Mistakes

  • Starting loop from index 0
    Start from index 1 to compare with previous element

Try It Yourself

Check if array is sorted in descending order.

150. Write a program to remove duplicates from an array.

What You'll Learn

In-place deduplication.

Real-World Context

"Removing duplicate elements while preserving order."

Breaking It Down

  • Use two pointers (slow and fast)
  • Slow pointer tracks unique elements position
  • Fast pointer scans for new unique elements
  • When unique element found, place at slow+1 and increment slow
Python 3
def remove_duplicates(arr):
    if not arr:
        return 0
    
    slow = 0
    for fast in range(1, len(arr)):
        if arr[fast] != arr[slow]:
            slow += 1
            arr[slow] = arr[fast]
    
    return slow + 1  # Length of unique elements

# Example usage
arr = [1, 1, 2, 2, 3, 4, 4, 5]
length = remove_duplicates(arr)
print(arr[:length])  # [1, 2, 3, 4, 5]

Line-by-Line Walkthrough

Two-pointer technique maintains relative order while removing duplicates in-place. This is efficient for sorted arrays and preserves the original order.

Complexity Analysis

  • Time: O(n)
  • Space: O(1)
  • Single pass, in-place modification.

Common Mistakes

  • Using a set or new array
    Use two pointers for in-place deduplication

Try It Yourself

Remove duplicates allowing at most two occurrences of each element.

161. Write a program to count words in a string.

What You'll Learn

String manipulation and splitting.

Real-World Context

"Counting words in a sentence or paragraph."

Breaking It Down

  • Split string by whitespace
  • Count the resulting list elements
  • Handle edge cases (empty string, multiple spaces)
Python 3
def count_words(text):
    if not text.strip():
        return 0
    return len(text.split())

# Example usage
print(count_words("Hello world"))  # 2
print(count_words("This is a test sentence."))  # 5
print(count_words("   "))  # 0

Line-by-Line Walkthrough

Python's split() method handles multiple spaces automatically. C++ uses stringstream to extract words, which also handles whitespace correctly.

Complexity Analysis

  • Time: O(n)
  • Space: O(n)
  • Need to process entire string and store words temporarily.

Common Mistakes

  • Not handling multiple spaces
    Use split() or stringstream which handle whitespace automatically

Try It Yourself

Count words ignoring punctuation marks.

162. Write a program to check if two strings are anagrams.

What You'll Learn

Character frequency counting.

Real-World Context

"Checking if two strings contain the same characters with same frequencies."

Breaking It Down

  • Remove spaces and convert to lowercase
  • Count frequency of each character in both strings
  • Compare the frequency maps
Python 3
from collections import Counter

def are_anagrams(s1, s2):
    # Remove spaces and convert to lowercase
    s1 = ''.join(s1.split()).lower()
    s2 = ''.join(s2.split()).lower()
    
    return Counter(s1) == Counter(s2)

# Example usage
print(are_anagrams("listen", "silent"))  # True
print(are_anagrams("hello", "world"))  # False
print(are_anagrams("Astronomer", "Moon starer"))  # True

Line-by-Line Walkthrough

Anagrams have the same character frequencies. Counter in Python makes this trivial. In C++, we use a frequency map and check if all counts are zero after incrementing and decrementing.

Complexity Analysis

  • Time: O(n)
  • Space: O(k)
  • n is string length, k is unique characters (typically small).

Common Mistakes

  • Not handling case sensitivity
    Convert both strings to lowercase
  • Not removing spaces
    Remove spaces before comparison

Try It Yourself

Find all anagrams of a word from a list of words.

Operating System Concepts (201-250)

Essential OS concepts for technical interviews.

201. What is a deadlock? Explain with an example.

What You'll Learn

Resource allocation and synchronization.

Real-World Context

"Deadlock occurs when two or more processes are waiting for resources held by each other."

Breaking It Down

  • Process A holds resource X, waits for Y
  • Process B holds resource Y, waits for X
  • Both processes are blocked forever
Python 3
# Deadlock example (conceptual)
import threading
import time

lock1 = threading.Lock()
lock2 = threading.Lock()

def thread1():
    lock1.acquire()
    print("Thread 1: Got lock 1")
    time.sleep(1)
    lock2.acquire()  # Waits for lock 2
    print("Thread 1: Got lock 2")
    lock2.release()
    lock1.release()

def thread2():
    lock2.acquire()
    print("Thread 2: Got lock 2")
    time.sleep(1)
    lock1.acquire()  # Waits for lock 1
    print("Thread 2: Got lock 1")
    lock1.release()
    lock2.release()

# This will cause deadlock
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()

Line-by-Line Walkthrough

Deadlock requires four conditions: mutual exclusion, hold and wait, no preemption, and circular wait. The example shows two threads acquiring locks in different orders, creating a circular dependency.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • OS concept, not algorithmic complexity.

Common Mistakes

  • Thinking deadlock is the same as starvation
    Deadlock is permanent blocking, starvation is temporary unfairness

Try It Yourself

How would you prevent this deadlock?

202. What are the four necessary conditions for deadlock?

What You'll Learn

Deadlock prevention strategies.

Real-World Context

"Coffman conditions that must all be present for deadlock to occur."

Breaking It Down

  • Mutual Exclusion: Resources can't be shared
  • Hold and Wait: Process holds one resource while waiting for another
  • No Preemption: Resources can't be forcibly taken
  • Circular Wait: Processes form a circular chain of waiting
Python 3
# Deadlock prevention strategies
"""
1. Mutual Exclusion - Can't prevent (some resources are inherently non-shareable)
2. Hold and Wait - Require processes to request all resources at once
3. No Preemption - Allow preemption of resources
4. Circular Wait - Impose ordering on resource acquisition
"""

# Example of ordered resource acquisition (prevents circular wait)
import threading

lock1 = threading.Lock()
lock2 = threading.Lock()

def safe_thread():
    # Always acquire locks in same order
    lock1.acquire()
    print("Got lock 1")
    lock2.acquire()
    print("Got lock 2")
    
    # Critical section
    print("Doing work...")
    
    lock2.release()
    lock1.release()

Line-by-Line Walkthrough

To prevent deadlock, you need to break at least one of these four conditions. The most practical approaches are resource ordering and avoiding hold-and-wait.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • OS design principle.

Common Mistakes

  • Thinking you can prevent mutual exclusion
    Some resources (like printers) must be mutually exclusive

Try It Yourself

Implement banker's algorithm for deadlock avoidance.

203. What is virtual memory?

What You'll Learn

Memory management concepts.

Real-World Context

"Technique that gives processes the illusion of large contiguous memory."

Breaking It Down

  • Physical memory is limited
  • OS maps virtual addresses to physical addresses
  • Uses disk as extension of RAM
  • Provides memory protection and isolation
Python 3
# Virtual memory simulation (conceptual)
class VirtualMemory:
    def __init__(self, physical_pages=4):
        self.physical_pages = physical_pages
        self.page_table = {}  # virtual -> physical mapping
        self.disk = {}  # swapped out pages
        
    def access(self, virtual_addr):
        if virtual_addr in self.page_table:
            return f"Physical page: {self.page_table[virtual_addr]}"
        elif virtual_addr in self.disk:
            # Page fault - load from disk
            return f"Page fault! Loading from disk..."
        else:
            return "Segmentation fault!"

vm = VirtualMemory()
print(vm.access(0x1000))  # Page fault
vm.page_table[0x1000] = 0  # Map to physical page 0
print(vm.access(0x1000))  # Hit

Line-by-Line Walkthrough

Virtual memory allows programs to use more memory than physically available. When a page isn't in physical memory, a page fault occurs and the page is loaded from disk.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • Hardware/OS feature.

Common Mistakes

  • Confusing virtual memory with physical memory
    Virtual addresses are translated to physical addresses by MMU

Try It Yourself

Explain the difference between paging and segmentation.

204. What is a process vs thread?

What You'll Learn

Concurrency fundamentals.

Real-World Context

"Processes are independent execution units, threads are lightweight subprocesses."

Breaking It Down

  • Process: Independent program execution with own memory space
  • Thread: Lightweight process that shares memory with parent
  • Processes communicate via IPC, threads via shared memory
  • Context switching is more expensive for processes
Python 3
import multiprocessing
import threading
import time

def worker(name):
    print(f"{name} starting")
    time.sleep(1)
    print(f"{name} finishing")

# Process example (separate memory)
if __name__ == "__main__":
    p = multiprocessing.Process(target=worker, args=("Process",))
    p.start()
    p.join()

# Thread example (shared memory)
t = threading.Thread(target=worker, args=("Thread",))
t.start()
t.join()

Line-by-Line Walkthrough

Processes are isolated and communicate via pipes/sockets. Threads share memory but need synchronization. Use processes for isolation, threads for performance.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • OS abstraction.

Common Mistakes

  • Thinking threads are just lightweight processes
    Threads share memory, processes have separate address spaces

Try It Yourself

When would you use multiprocessing vs multithreading?

205. What is a semaphore?

What You'll Learn

Synchronization primitives.

Real-World Context

"Variable that controls access to shared resources."

Breaking It Down

  • Integer variable that can be accessed by multiple processes
  • P operation (wait): decrement and block if zero
  • V operation (signal): increment and wake waiting process
  • Binary semaphore (0-1) acts like mutex
Python 3
import threading
import time

# Binary semaphore (mutex)
semaphore = threading.Semaphore(1)  # Only 1 process at a time

def critical_section(name):
    semaphore.acquire()
    try:
        print(f"{name} in critical section")
        time.sleep(1)
        print(f"{name} leaving critical section")
    finally:
        semaphore.release()

# Usage
t1 = threading.Thread(target=critical_section, args=("Thread 1",))
t2 = threading.Thread(target=critical_section, args=("Thread 2",))
t1.start()
t2.start()
t1.join()
t2.join()

Line-by-Line Walkthrough

Semaphores prevent race conditions by controlling concurrent access. Binary semaphores (0-1) work like mutexes, counting semaphores allow multiple concurrent accesses.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • Synchronization primitive.

Common Mistakes

  • Using semaphore when mutex would suffice
    Use mutex for mutual exclusion, semaphore for resource counting

Try It Yourself

Implement producer-consumer problem using semaphores.

206. What is paging?

What You'll Learn

Memory management technique.

Real-World Context

"Dividing memory into fixed-size pages for efficient allocation."

Breaking It Down

  • Memory divided into fixed-size pages (usually 4KB)
  • Logical address = page number + offset
  • Page table maps logical pages to physical frames
  • Allows non-contiguous physical memory allocation
Python 3
# Simple paging simulation
class PageTable:
    def __init__(self, page_size=4096):
        self.page_size = page_size
        self.table = {}  # page_number -> frame_number
        
    def translate_address(self, logical_addr):
        page_number = logical_addr // self.page_size
        offset = logical_addr % self.page_size
        
        if page_number in self.table:
            frame_number = self.table[page_number]
            physical_addr = frame_number * self.page_size + offset
            return f"Physical address: {physical_addr}"
        else:
            return "Page fault!"
    
    def map_page(self, page_num, frame_num):
        self.table[page_num] = frame_num

pt = PageTable()
pt.map_page(0, 5)  # Page 0 -> Frame 5
print(pt.translate_address(0))      # Physical: 0
print(pt.translate_address(4096))   # Physical: 20480 (5*4096)
print(pt.translate_address(8192))   # Page fault

Line-by-Line Walkthrough

Paging eliminates external fragmentation by using fixed-size pages. The page table translation happens in hardware (MMU) for performance.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • Memory management technique.

Common Mistakes

  • Confusing paging with segmentation
    Paging uses fixed-size pages, segmentation uses variable-size segments

Try It Yourself

Explain how TLB (Translation Lookaside Buffer) improves paging performance.

207. What is a race condition?

What You'll Learn

Concurrency bugs.

Real-World Context

"When multiple threads access shared data simultaneously, leading to inconsistent results."

Breaking It Down

  • Multiple threads access shared variable
  • At least one thread modifies the variable
  • Operations are not atomic
  • Final result depends on execution order
Python 3
import threading

counter = 0

def increment():
    global counter
    for _ in range(100000):
        # Race condition: read-modify-write is not atomic
        temp = counter
        temp += 1
        counter = temp

t1 = threading.Thread(target=increment)
t2 = threading.Thread(target=increment)

t1.start()
t2.start()
t1.join()
t2.join()

print(f"Expected: 200000, Actual: {counter}")  # Usually less due to race condition

Line-by-Line Walkthrough

Race conditions occur when operations that should be atomic are split into multiple steps. Use locks or atomic operations to prevent them.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • Concurrency issue.

Common Mistakes

  • Thinking race conditions are always obvious
    They often only appear under specific timing conditions

Try It Yourself

Fix this race condition using a mutex.

208. What is context switching?

What You'll Learn

Process scheduling.

Real-World Context

"Saving current process state and loading another process's state."

Breaking It Down

  • Save current process's registers and program counter
  • Save current process's memory mappings
  • Load new process's state from PCB
  • Resume execution of new process
Python 3
# Conceptual context switch
class Process:
    def __init__(self, pid, state="ready"):
        self.pid = pid
        self.state = state
        self.registers = {"PC": 0, "SP": 0}  # Program counter, stack pointer
        
    def save_context(self):
        print(f"Saving context for process {self.pid}")
        # In reality, this saves to PCB (Process Control Block)
        
    def load_context(self):
        print(f"Loading context for process {self.pid}")
        # In reality, this loads from PCB

def context_switch(from_process, to_process):
    print(f"Context switch: {from_process.pid} -> {to_process.pid}")
    from_process.save_context()
    to_process.load_context()

p1 = Process(1, "running")
p2 = Process(2, "ready")
context_switch(p1, p2)

Line-by-Line Walkthrough

Context switching is expensive because it involves saving/restoring CPU registers, memory mappings, and cache flushing. This is why threads (which share address space) have faster context switches than processes.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Fixed-size register save/restore.

Common Mistakes

  • Thinking context switching is instant
    It takes microseconds but adds up with frequent switching

Try It Yourself

Explain why thread context switches are faster than process context switches.

209. What is starvation?

What You'll Learn

Scheduling fairness.

Real-World Context

"When a process never gets CPU time due to scheduling policy."

Breaking It Down

  • Process is ready to run but never gets scheduled
  • Usually due to priority-based scheduling
  • Low-priority processes can starve behind high-priority ones
  • Different from deadlock (processes are not blocked)
Python 3
# Starvation example with priority scheduling
import threading
import time

high_priority_event = threading.Event()
low_priority_counter = 0

def high_priority_task():
    while True:
        high_priority_event.set()  # Signal completion
        time.sleep(0.01)  # Short burst

def low_priority_task():
    global low_priority_counter
    while low_priority_counter < 10:
        if high_priority_event.wait(timeout=0.1):  # Wait for signal
            low_priority_counter += 1
            print(f"Low priority task: {low_priority_counter}")
            high_priority_event.clear()

# High priority thread runs continuously
# Low priority thread may never get CPU time
t_high = threading.Thread(target=high_priority_task, daemon=True)
t_low = threading.Thread(target=low_priority_task)

t_high.start()
t_low.start()
t_low.join(timeout=5)  # May timeout due to starvation

Line-by-Line Walkthrough

Starvation is solved with aging (gradually increasing priority of waiting processes) or round-robin scheduling. It's different from deadlock because starved processes are ready but never selected.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • Scheduling policy issue.

Common Mistakes

  • Confusing starvation with deadlock
    Deadlock is blocking, starvation is unfair scheduling

Try It Yourself

How does aging prevent starvation in priority scheduling?

210. What is thrashing?

What You'll Learn

Memory performance issue.

Real-World Context

"When system spends more time paging than executing."

Breaking It Down

  • Too many processes competing for limited memory
  • High page fault rate
  • CPU spends time waiting for pages instead of executing
  • System appears frozen despite having work to do
Python 3
# Thrashing simulation (conceptual)
class MemoryManager:
    def __init__(self, total_frames=10):
        self.total_frames = total_frames
        self.page_faults = 0
        
    def access_page(self, page, process_frames):
        if page not in process_frames:
            self.page_faults += 1
            if len(process_frames) >= self.total_frames // 2:  # Thrashing condition
                print(f"THRASHING: High page fault rate! Faults: {self.page_faults}")
                # System would start swapping processes
        return page in process_frames

# Simulate multiple processes with insufficient memory
mm = MemoryManager()
processes = [{"frames": [1, 2]} for _ in range(8)]  # 8 processes, 10 frames total

for i in range(100):
    for p in processes:
        mm.access_page(3, p["frames"])  # Page 3 not in memory - page fault

Line-by-Line Walkthrough

Thrashing occurs when the working set of all processes exceeds physical memory. Solutions include reducing multiprogramming level or increasing RAM.

Complexity Analysis

  • Time: N/A
  • Space: N/A
  • System performance issue.

Common Mistakes

  • Thinking thrashing is the same as paging
    Paging is normal, thrashing is excessive paging that hurts performance

Try It Yourself

How does the working set model help prevent thrashing?

Additional OOP Questions (16-20, 26-35)

Advanced OOP concepts and practical implementations.

16. What is the final keyword used for in Java?

What You'll Learn

Immutability and inheritance control.

Real-World Context

"The final keyword prevents modification or extension."

Breaking It Down

  • final class: Cannot be extended
  • final method: Cannot be overridden
  • final variable: Cannot be reassigned
  • final parameters: Cannot be modified in method
Python 3
# Python equivalent using naming conventions and properties
class FinalClass:
    def __init__(self):
        self._value = 42
    
    @property
    def value(self):
        return self._value
    
    # No setter = final-like behavior

# Usage
obj = FinalClass()
print(obj.value)  # 42
# obj.value = 50  # AttributeError

Line-by-Line Walkthrough

Final prevents inheritance (classes), overriding (methods), and reassignment (variables). It's used for security, performance, and design clarity.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Compile-time restriction.

Common Mistakes

  • Thinking final means immutable objects
    Final only prevents reassignment of reference, object can still be modified

Try It Yourself

Create a class with final methods and show inheritance attempt.

17. What are static methods and when to use them?

What You'll Learn

Class-level methods vs instance methods.

Real-World Context

"Static methods belong to the class, not instances."

Breaking It Down

  • Declared with static keyword
  • Called on class, not object
  • Cannot access instance variables/methods
  • Cannot use 'this' keyword
Python 3
class MathUtils:
    @staticmethod
    def add(x, y):
        return x + y
    
    @staticmethod
    def factorial(n):
        if n <= 1:
            return 1
        return n * MathUtils.factorial(n - 1)

# Usage
print(MathUtils.add(5, 3))  # 8
print(MathUtils.factorial(5))  # 120

Line-by-Line Walkthrough

Static methods are utility functions that don't need object state. They're called on the class itself and are shared across all instances.

Complexity Analysis

  • Time: O(1) for add, O(n) for factorial
  • Space: O(1)
  • No object creation needed.

Common Mistakes

  • Trying to access instance variables in static method
    Static methods can only access static variables

Try It Yourself

Create a static counter that tracks total objects created.

18. What is a friend function in C++?

What You'll Learn

Accessing private members from outside the class.

Real-World Context

"Friend functions can access private/protected members."

Breaking It Down

  • Declare friend function in class
  • Function definition outside class
  • Has access to private members
  • Not a member function itself
Python 3
# Python doesn't have friends, but similar with underscore convention
class MyClass:
    def __init__(self):
        self.__private = 42
    
    def get_private(self):
        return self.__private

def friend_function(obj):
    # In Python, we respect privacy but can access via name mangling
    return obj._MyClass__private

obj = MyClass()
print(friend_function(obj))  # 42

Line-by-Line Walkthrough

Friend functions break encapsulation for specific functions that need access. Use sparingly as it violates OOP principles.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Direct access to members.

Common Mistakes

  • Overusing friend functions
    Use only when necessary, prefer public methods

Try It Yourself

Create two classes where one is friend of another.

26. How does HashMap work internally?

What You'll Learn

Hash table implementation and collision handling.

Real-World Context

"HashMap uses hashing for O(1) average case operations."

Breaking It Down

  • Key passed to hash function
  • Hash code determines bucket index
  • Handle collisions (separate chaining/open addressing)
  • Store key-value pairs in buckets
Python 3
# Python dict implementation concept
class SimpleHashMap:
    def __init__(self, size=10):
        self.size = size
        self.buckets = [[] for _ in range(size)]
    
    def _hash(self, key):
        return hash(key) % self.size
    
    def put(self, key, value):
        index = self._hash(key)
        bucket = self.buckets[index]
        
        # Check if key exists
        for i, (k, v) in enumerate(bucket):
            if k == key:
                bucket[i] = (key, value)
                return
        
        bucket.append((key, value))
    
    def get(self, key):
        index = self._hash(key)
        bucket = self.buckets[index]
        
        for k, v in bucket:
            if k == key:
                return v
        return None

# Usage
hm = SimpleHashMap()
hm.put("name", "John")
print(hm.get("name"))  # John

Line-by-Line Walkthrough

HashMap uses hash function to map keys to indices. Collisions are handled by storing multiple entries in same bucket (separate chaining).

Complexity Analysis

  • Time: O(1) average, O(n) worst case
  • Space: O(n)
  • Hash function distributes keys evenly.

Common Mistakes

  • Assuming O(1) worst case
    Worst case is O(n) due to collisions

Try It Yourself

Implement collision resolution using linear probing.

27. How does exception handling work?

What You'll Learn

Error handling and recovery mechanisms.

Real-World Context

"Exceptions handle runtime errors gracefully."

Breaking It Down

  • Throw exception when error occurs
  • Exception propagates up call stack
  • Catch block handles specific exception types
  • Finally block executes regardless
Python 3
def divide(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError as e:
        print(f"Error: {e}")
        return None
    except TypeError as e:
        print(f"Type error: {e}")
        return None
    finally:
        print("Division operation completed")

# Usage
print(divide(10, 2))   # 5.0
print(divide(10, 0))   # Error: division by zero
print(divide(10, "a")) # Type error

Line-by-Line Walkthrough

Exceptions allow clean error handling without checking return codes everywhere. They separate error-handling from normal logic.

Complexity Analysis

  • Time: O(1) for throwing, O(depth) for stack unwinding
  • Space: O(depth) for stack trace
  • Exception objects and stack unwinding overhead.

Common Mistakes

  • Using exceptions for normal flow control
    Use exceptions only for exceptional situations

Try It Yourself

Create custom exception class and handle it.

28. What is the difference between Comparable and Comparator?

What You'll Learn

Object comparison interfaces.

Real-World Context

"Two ways to define object ordering in Java."

Breaking It Down

  • Comparable: Natural ordering, implements compareTo()
  • Comparator: Custom ordering, separate comparator class
  • Comparable affects class itself
  • Comparator is external comparison logic
Python 3
# Python equivalent using key functions and functools
from functools import total_ordering

@total_ordering
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __eq__(self, other):
        return self.age == other.age
    
    def __lt__(self, other):
        return self.age < other.age  # Natural ordering by age

# Custom comparator (key function)
def by_name(person):
    return person.name

people = [Person("Bob", 25), Person("Alice", 30), Person("Charlie", 20)]

# Natural ordering (by age)
print("By age:", [p.name for p in sorted(people)])

# Custom ordering (by name)
print("By name:", [p.name for p in sorted(people, key=by_name)])

Line-by-Line Walkthrough

Comparable defines natural ordering within the class. Comparator allows multiple comparison strategies without modifying the class.

Complexity Analysis

  • Time: O(n log n) for sorting
  • Space: O(1) extra
  • Comparison operations during sorting.

Common Mistakes

  • Implementing Comparable when Comparator would be better
    Use Comparator for multiple sort orders

Try It Yourself

Sort objects by multiple criteria using Comparator chain.

29. What is serialization and deserialization?

What You'll Learn

Object persistence and network transfer.

Real-World Context

"Converting objects to byte streams and back."

Breaking It Down

  • Serialization: Object → byte stream
  • Deserialization: byte stream → Object
  • Used for persistence, network transfer
  • Requires Serializable interface in Java
Python 3
import pickle

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"{self.name}, {self.age}"

# Serialization
person = Person("Alice", 30)
with open('person.pkl', 'wb') as f:
    pickle.dump(person, f)

# Deserialization
with open('person.pkl', 'rb') as f:
    loaded_person = pickle.load(f)

print(loaded_person)  # Alice, 30

Line-by-Line Walkthrough

Serialization converts object state to a format that can be stored or transmitted. Deserialization recreates the object from that format.

Complexity Analysis

  • Time: O(n) where n is object size
  • Space: O(n)
  • Traversing all object fields and references.

Common Mistakes

  • Serializing sensitive data
    Use transient keyword to exclude sensitive fields

Try It Yourself

Serialize a complex object graph with references.

30. What are threads and how do they work?

What You'll Learn

Concurrent execution within a process.

Real-World Context

"Threads allow multiple execution paths in same process."

Breaking It Down

  • Thread: Lightweight process within a process
  • Share memory space with parent process
  • Have their own stack and registers
  • Concurrent execution on multiple cores
Python 3
import threading
import time

def worker(name, delay):
    print(f"Worker {name} starting")
    time.sleep(delay)
    print(f"Worker {name} finished")

# Create threads
thread1 = threading.Thread(target=worker, args=("A", 2))
thread2 = threading.Thread(target=worker, args=("B", 1))

# Start threads
thread1.start()
thread2.start()

# Wait for completion
thread1.join()
thread2.join()

print("All workers done")

Line-by-Line Walkthrough

Threads run concurrently and share the same memory space. They're lighter than processes but require synchronization to avoid race conditions.

Complexity Analysis

  • Time: Depends on work
  • Space: Shared heap, separate stacks
  • Concurrent execution with shared resources.

Common Mistakes

  • Not synchronizing shared data access
    Use locks, semaphores, or atomic operations

Try It Yourself

Create producer-consumer problem with threads.

Additional SQL Questions (53-56, 61-71, 78-100)

Advanced SQL queries and database concepts.

53. Write a SQL query to select all records from a table.

What You'll Learn

Basic SELECT statement syntax.

Real-World Context

"Retrieving all data from a database table."

Breaking It Down

  • Use SELECT * to get all columns
  • Specify table name after FROM
  • Optional: Add WHERE for filtering
  • Optional: Add ORDER BY for sorting
Python 3
import sqlite3

# Create and populate table
conn = sqlite3.connect(':memory:')
cursor = conn.cursor()

cursor.execute('''
    CREATE TABLE employees (
        id INTEGER PRIMARY KEY,
        name TEXT,
        salary REAL,
        department TEXT
    )
''')

# Insert sample data
cursor.executemany('''
    INSERT INTO employees (name, salary, department) 
    VALUES (?, ?, ?)
''', [
    ('Alice', 75000, 'Engineering'),
    ('Bob', 65000, 'Sales'),
    ('Charlie', 80000, 'Engineering')
])

# Select all records
cursor.execute('SELECT * FROM employees')
rows = cursor.fetchall()

print("All employees:")
for row in rows:
    print(row)

conn.close()

Line-by-Line Walkthrough

SELECT * FROM table_name retrieves all columns and rows. Use specific column names instead of * in production for better performance.

Complexity Analysis

  • Time: O(n) where n is number of rows
  • Space: O(n)
  • Reading all table data.

Common Mistakes

  • Using SELECT * in production
    Specify only needed columns

Try It Yourself

Select specific columns with aliases.

54. Write a SQL query with WHERE clause.

What You'll Learn

Filtering records based on conditions.

Real-World Context

"Retrieving specific records that match criteria."

Breaking It Down

  • Start with SELECT columns
  • Add FROM table_name
  • Add WHERE condition
  • Use comparison operators (=, >, <, etc.)
Python 3
import sqlite3

conn = sqlite3.connect(':memory:')
cursor = conn.cursor()

# Create and populate table
cursor.execute('CREATE TABLE products (id INTEGER, name TEXT, price REAL, category TEXT)')
cursor.executemany('INSERT INTO products VALUES (?, ?, ?, ?)', [
    (1, 'Laptop', 1200, 'Electronics'),
    (2, 'Book', 25, 'Education'),
    (3, 'Phone', 800, 'Electronics'),
    (4, 'Chair', 150, 'Furniture')
])

# WHERE examples
print("Electronics products:")
cursor.execute('SELECT * FROM products WHERE category = ?', ('Electronics',))
for row in cursor.fetchall():
    print(row)

print("\nExpensive products (> $500):")
cursor.execute('SELECT name, price FROM products WHERE price > 500')
for row in cursor.fetchall():
    print(row)

conn.close()

Line-by-Line Walkthrough

WHERE clause filters rows based on conditions. Multiple conditions can be combined with AND/OR. Always use parameterized queries to prevent SQL injection.

Complexity Analysis

  • Time: O(log n) with index, O(n) without
  • Space: O(k) where k is result size
  • Index lookup or table scan.

Common Mistakes

  • String concatenation in WHERE
    Use parameterized queries

Try It Yourself

Use WHERE with multiple conditions using AND/OR.

55. Write a SQL query with ORDER BY clause.

What You'll Learn

Sorting query results.

Real-World Context

"Ordering results by one or more columns."

Breaking It Down

  • Write basic SELECT query
  • Add ORDER BY column_name
  • Specify ASC (ascending) or DESC (descending)
  • Can order by multiple columns
Python 3
import sqlite3

conn = sqlite3.connect(':memory:')
cursor = conn.cursor()

cursor.execute('CREATE TABLE students (id INTEGER, name TEXT, grade REAL, subject TEXT)')
cursor.executemany('INSERT INTO students VALUES (?, ?, ?, ?)', [
    (1, 'Alice', 95, 'Math'),
    (2, 'Bob', 87, 'Math'),
    (3, 'Charlie', 92, 'Science'),
    (4, 'Diana', 88, 'Math')
])

print("Students ordered by grade (descending):")
cursor.execute('SELECT name, grade, subject FROM students ORDER BY grade DESC')
for row in cursor.fetchall():
    print(row)

print("\nStudents ordered by subject, then grade:")
cursor.execute('SELECT name, grade, subject FROM students ORDER BY subject ASC, grade DESC')
for row in cursor.fetchall():
    print(row)

conn.close()

Line-by-Line Walkthrough

ORDER BY sorts results after filtering. Multiple columns create hierarchical sorting. Default is ASC, use DESC for descending.

Complexity Analysis

  • Time: O(n log n) worst case
  • Space: O(n)
  • Sorting algorithm requirements.

Common Mistakes

  • Assuming ORDER BY sorts NULLs predictably
    Use NULLS FIRST/LAST if needed

Try It Yourself

Order by calculated expression (e.g., price * quantity).

61. Explain GROUP BY with examples.

What You'll Learn

Grouping rows for aggregate functions.

Real-World Context

"Grouping records by column values for summary operations."

Breaking It Down

  • Use GROUP BY column_name
  • Apply aggregate functions (COUNT, SUM, AVG, etc.)
  • SELECT clause can only contain grouped columns or aggregates
  • Use HAVING to filter groups
Python 3
import sqlite3

conn = sqlite3.connect(':memory:')
cursor = conn.cursor()

cursor.execute('CREATE TABLE sales (id INTEGER, product TEXT, amount REAL, region TEXT)')
cursor.executemany('INSERT INTO sales VALUES (?, ?, ?, ?)', [
    (1, 'Laptop', 1200, 'North'),
    (2, 'Phone', 800, 'South'),
    (3, 'Laptop', 1100, 'North'),
    (4, 'Tablet', 500, 'South'),
    (5, 'Phone', 900, 'North')
])

print("Total sales by product:")
cursor.execute('''
    SELECT product, COUNT(*) as count, SUM(amount) as total
    FROM sales 
    GROUP BY product
''')
for row in cursor.fetchall():
    print(row)

print("\nAverage sales by region:")
cursor.execute('''
    SELECT region, AVG(amount) as average, COUNT(*) as transactions
    FROM sales 
    GROUP BY region
''')
for row in cursor.fetchall():
    print(row)

conn.close()

Line-by-Line Walkthrough

GROUP BY creates groups of rows with same values. Aggregate functions operate on each group. Non-grouped columns in SELECT must use aggregates.

Complexity Analysis

  • Time: O(n log n) due to sorting for grouping
  • Space: O(g) where g is number of groups
  • Grouping requires sorting or hashing.

Common Mistakes

  • Selecting non-grouped, non-aggregate columns
    Only select grouped columns or use aggregates

Try It Yourself

Group by multiple columns and use HAVING clause.

78. Explain window functions with examples.

What You'll Learn

Advanced aggregation over result sets.

Real-World Context

"Performing calculations across related rows without grouping."

Breaking It Down

  • Use OVER() clause with aggregate functions
  • PARTITION BY divides result into partitions
  • ORDER BY defines window frame
  • ROW_NUMBER(), RANK(), DENSE_RANK() for ranking
Python 3
import sqlite3

conn = sqlite3.connect(':memory:')
cursor = conn.cursor()

# SQLite doesn't have window functions, so we'll simulate with Python
cursor.execute('CREATE TABLE employees (name TEXT, department TEXT, salary REAL)')
cursor.executemany('INSERT INTO employees VALUES (?, ?, ?)', [
    ('Alice', 'Engineering', 90000),
    ('Bob', 'Engineering', 80000),
    ('Charlie', 'Sales', 70000),
    ('Diana', 'Sales', 75000),
    ('Eve', 'Engineering', 85000)
])

# Simulate window functions with Python
cursor.execute('SELECT * FROM employees ORDER BY department, salary DESC')
rows = cursor.fetchall()

print("Employees with department running totals:")
current_dept = None
dept_total = 0
for row in rows:
    name, dept, salary = row
    if dept != current_dept:
        current_dept = dept
        dept_total = 0
    dept_total += salary
    print(f"{name} ({dept}): ${salary} | Dept Total: ${dept_total}")

conn.close()

Line-by-Line Walkthrough

Window functions perform calculations across a set of rows related to the current row. They're powerful for running totals, rankings, and moving averages.

Complexity Analysis

  • Time: O(n log n) for sorting partitions
  • Space: O(n)
  • Sorting within partitions required.

Common Mistakes

  • Confusing window functions with GROUP BY
    Window functions don't collapse rows

Try It Yourself

Calculate running totals and moving averages.

Additional System Concepts (103-115)

Web technologies and system design fundamentals.

103. What is REST API?

What You'll Learn

Representational State Transfer architecture.

Real-World Context

"Standard for web service communication."

Breaking It Down

  • RESTful web services use HTTP methods
  • Resources identified by URIs
  • Stateless communication
  • JSON/XML data formats
Python 3
# Flask REST API example
from flask import Flask, jsonify, request

app = Flask(__name__)

users = [
    {"id": 1, "name": "Alice", "email": "alice@example.com"},
    {"id": 2, "name": "Bob", "email": "bob@example.com"}
]

@app.route('/users', methods=['GET'])
def get_users():
    return jsonify(users)

@app.route('/users/<int:user_id>', methods=['GET'])
def get_user(user_id):
    user = next((u for u in users if u['id'] == user_id), None)
    if user:
        return jsonify(user)
    return jsonify({"error": "User not found"}), 404

@app.route('/users', methods=['POST'])
def create_user():
    new_user = request.get_json()
    new_user['id'] = len(users) + 1
    users.append(new_user)
    return jsonify(new_user), 201

if __name__ == '__main__':
    app.run(debug=True)

Line-by-Line Walkthrough

REST APIs use HTTP methods (GET, POST, PUT, DELETE) to manipulate resources. Each resource has a unique URI and operations are stateless.

Complexity Analysis

  • Time: O(1) to O(n) depending on operation
  • Space: O(n) for data storage
  • Basic CRUD operations on collections.

Common Mistakes

  • Making APIs stateful
    Keep APIs stateless - all state in requests

Try It Yourself

Implement PUT and DELETE methods for the API.

104. What is JSON?

What You'll Learn

JavaScript Object Notation format.

Real-World Context

"Lightweight data interchange format."

Breaking It Down

  • Human-readable text format
  • Key-value pairs and arrays
  • Language-independent
  • Commonly used in web APIs
Python 3
import json

# Python dict to JSON
person = {
    "name": "Alice",
    "age": 30,
    "skills": ["Python", "JavaScript", "SQL"],
    "address": {
        "street": "123 Main St",
        "city": "Anytown",
        "country": "USA"
    }
}

# Serialize to JSON string
json_string = json.dumps(person, indent=2)
print("JSON string:")
print(json_string)

# Deserialize from JSON string
parsed_person = json.loads(json_string)
print("\nParsed back:")
print(f"Name: {parsed_person['name']}")
print(f"Skills: {', '.join(parsed_person['skills'])}")

# Working with files
with open('person.json', 'w') as f:
    json.dump(person, f, indent=2)

with open('person.json', 'r') as f:
    loaded_person = json.load(f)

print(f"\nLoaded from file: {loaded_person['name']}")

Line-by-Line Walkthrough

JSON is a text format for storing and transporting data. It's self-describing and easy to understand, making it ideal for APIs and configuration files.

Complexity Analysis

  • Time: O(n) for serialization/deserialization
  • Space: O(n)
  • Processing all data elements.

Common Mistakes

  • Using single quotes instead of double quotes
    JSON requires double quotes for strings

Try It Yourself

Parse a complex nested JSON structure and extract specific values.

105. What are sessions and cookies?

What You'll Learn

State management in web applications.

Real-World Context

"Maintaining user state across HTTP requests."

Breaking It Down

  • HTTP is stateless - no memory of previous requests
  • Cookies: Small data stored on client browser
  • Sessions: Server-side storage referenced by session ID
  • Session ID stored in cookie for identification
Python 3
# Flask session and cookie example
from flask import Flask, session, request, make_response

app = Flask(__name__)
app.secret_key = 'your-secret-key-here'  # Required for sessions

@app.route('/login')
def login():
    # Set session data
    session['user_id'] = 123
    session['username'] = 'alice'
    
    # Create response with cookie
    resp = make_response("Logged in successfully")
    resp.set_cookie('last_login', '2024-01-15')
    
    return resp

@app.route('/profile')
def profile():
    # Access session data
    if 'user_id' in session:
        user_id = session['user_id']
        username = session['username']
        return f"Welcome {username} (ID: {user_id})"
    else:
        return "Please login first"

@app.route('/logout')
def logout():
    # Clear session
    session.clear()
    
    # Clear cookie
    resp = make_response("Logged out")
    resp.set_cookie('last_login', '', expires=0)
    
    return resp

@app.route('/check-cookie')
def check_cookie():
    # Read cookie
    last_login = request.cookies.get('last_login')
    if last_login:
        return f"Last login: {last_login}"
    else:
        return "No last login cookie found"

if __name__ == '__main__':
    app.run(debug=True)

Line-by-Line Walkthrough

Sessions store data on the server, cookies on the client. Session IDs in cookies link the two. Sessions are more secure for sensitive data.

Complexity Analysis

  • Time: O(1) for session operations
  • Space: O(n) for stored sessions
  • Hash map lookups for session data.

Common Mistakes

  • Storing sensitive data in cookies
    Use sessions for sensitive data, cookies for preferences

Try It Yourself

Implement session timeout and automatic cleanup.

106. What are HTTP status codes?

What You'll Learn

Standard response codes for web requests.

Real-World Context

"Indicating success, errors, and redirects."

Breaking It Down

  • 1xx: Informational responses
  • 2xx: Successful responses (200 OK, 201 Created)
  • 3xx: Redirection messages (301 Moved, 302 Found)
  • 4xx: Client error responses (400 Bad Request, 404 Not Found)
  • 5xx: Server error responses (500 Internal Server Error)
Python 3
# Flask HTTP status codes example
from flask import Flask, jsonify, request, abort

app = Flask(__name__)

users = {"alice": "password123", "bob": "password456"}

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    
    if not data or 'username' not in data or 'password' not in data:
        return jsonify({"error": "Missing username or password"}), 400
    
    username = data['username']
    password = data['password']
    
    if username not in users:
        return jsonify({"error": "User not found"}), 404
    
    if users[username] != password:
        return jsonify({"error": "Invalid password"}), 401
    
    return jsonify({"message": "Login successful", "user": username}), 200

@app.route('/users/<username>')
def get_user(username):
    if username not in users:
        abort(404, description="User not found")
    
    return jsonify({"username": username}), 200

@app.route('/admin')
def admin():
    # Simulate unauthorized access
    return jsonify({"error": "Admin access required"}), 403

@app.route('/redirect')
def redirect_example():
    return jsonify({"message": "This endpoint moved"}), 301

@app.route('/server-error')
def server_error():
    # Simulate server error
    return jsonify({"error": "Internal server error"}), 500

if __name__ == '__main__':
    app.run(debug=True)

Line-by-Line Walkthrough

HTTP status codes provide standardized way to indicate request outcomes. 2xx for success, 4xx for client errors, 5xx for server errors.

Complexity Analysis

  • Time: O(1)
  • Space: O(1)
  • Simple status code lookups.

Common Mistakes

  • Using wrong status codes
    Learn standard codes: 200 OK, 404 Not Found, 500 Server Error

Try It Yourself

Implement a function that returns appropriate status codes for different scenarios.

Data Structures Basics (251-270)

Fundamental data structures and their use cases.

251. Arrays vs Linked Lists - when to use which?

What You'll Learn

Linear data structure trade-offs.

Real-World Context

"Choosing between contiguous and linked storage."

Breaking It Down

  • Arrays: Fixed size, contiguous memory, O(1) access
  • Linked Lists: Dynamic size, scattered memory, O(n) access
  • Arrays for random access, linked lists for frequent insertions/deletions
  • Consider memory usage and operation patterns
Python 3
# Array (list) vs Linked List simulation
class Node:
    def __init__(self, data):
        self.data = data
        self.next = None

class LinkedList:
    def __init__(self):
        self.head = None
    
    def append(self, data):
        if not self.head:
            self.head = Node(data)
            return
        current = self.head
        while current.next:
            current = current.next
        current.next = Node(data)
    
    def get(self, index):
        current = self.head
        for i in range(index):
            if current is None:
                return None
            current = current.next
        return current.data if current else None

# Array usage - fast random access
arr = [1, 2, 3, 4, 5]
print("Array access arr[2]:", arr[2])  # O(1)

# Linked List usage - fast insertions
ll = LinkedList()
for i in range(1, 6):
    ll.append(i)
print("Linked list access ll.get(2):", ll.get(2))  # O(n)

# Array insertion (expensive)
arr.insert(0, 0)  # O(n) - shifts all elements
print("Array after insert:", arr)

# Linked List insertion (cheap at head)
ll.head = Node(0)  # O(1) - just update head pointer
print("Linked list after insert at head: done")

Line-by-Line Walkthrough

Arrays excel at random access but struggle with insertions. Linked lists handle dynamic sizing and frequent modifications well but have slow access.

Complexity Analysis

  • Time: Array: O(1) access, O(n) insert/delete | Linked List: O(n) access, O(1) insert/delete
  • Space: Array: contiguous | Linked List: scattered with pointers
  • Memory layout determines access patterns.

Common Mistakes

  • Using arrays for frequent insertions/deletions
    Use linked lists when modifications are common
  • Using linked lists when random access is needed
    Use arrays when access patterns are random

Try It Yourself

Implement a hybrid structure that combines benefits of both.

252. Stack implementation and use cases.

What You'll Learn

LIFO data structure.

Real-World Context

"Last In, First Out operations."

Breaking It Down

  • Push: Add element to top
  • Pop: Remove element from top
  • Peek: View top element without removing
  • isEmpty: Check if stack is empty
Python 3
class Stack:
    def __init__(self):
        self.items = []
    
    def push(self, item):
        self.items.append(item)
    
    def pop(self):
        if not self.is_empty():
            return self.items.pop()
        return None
    
    def peek(self):
        if not self.is_empty():
            return self.items[-1]
        return None
    
    def is_empty(self):
        return len(self.items) == 0
    
    def size(self):
        return len(self.items)

# Usage examples
stack = Stack()

# Browser back button simulation
stack.push("page1.html")
stack.push("page2.html")
stack.push("page3.html")

print("Current page:", stack.peek())
print("Going back...")
stack.pop()
print("Current page:", stack.peek())

# Expression evaluation (balanced parentheses)
def is_balanced(expression):
    stack = Stack()
    brackets = {')': '(', '}': '{', ']': '['}
    
    for char in expression:
        if char in '({[':
            stack.push(char)
        elif char in ')}]':
            if stack.is_empty() or stack.pop() != brackets[char]:
                return False
    return stack.is_empty()

print("Balanced: (a+b)", is_balanced("(a+b)"))
print("Unbalanced: (a+b", is_balanced("(a+b"))

Line-by-Line Walkthrough

Stack follows LIFO principle. Perfect for undo operations, function calls, expression evaluation, and backtracking algorithms.

Complexity Analysis

  • Time: O(1) for all operations
  • Space: O(n)
  • Simple array/list backing with constant-time operations.

Common Mistakes

  • Using stack when queue semantics are needed
    Use queue for FIFO requirements

Try It Yourself

Implement stack using two queues.

253. Queue implementation and use cases.

What You'll Learn

FIFO data structure.

Real-World Context

"First In, First Out operations."

Breaking It Down

  • Enqueue: Add element to rear
  • Dequeue: Remove element from front
  • Front: View front element without removing
  • isEmpty: Check if queue is empty
Python 3
from collections import deque

class Queue:
    def __init__(self):
        self.items = deque()
    
    def enqueue(self, item):
        self.items.append(item)
    
    def dequeue(self):
        if not self.is_empty():
            return self.items.popleft()
        return None
    
    def front(self):
        if not self.is_empty():
            return self.items[0]
        return None
    
    def is_empty(self):
        return len(self.items) == 0
    
    def size(self):
        return len(self.items)

# Usage examples
queue = Queue()

# Print job simulation
queue.enqueue("Document1.pdf")
queue.enqueue("Document2.pdf")
queue.enqueue("Document3.pdf")

print("Next job to print:", queue.front())
print("Processing:", queue.dequeue())
print("Next job:", queue.front())

# BFS (Breadth First Search) simulation
def bfs_simulation():
    queue = Queue()
    visited = set()
    
    # Start from node 1
    queue.enqueue(1)
    visited.add(1)
    
    while not queue.is_empty():
        current = queue.dequeue()
        print(f"Processing node: {current}")
        
        # Simulate neighbors (in real BFS, get actual neighbors)
        neighbors = [current * 2, current * 2 + 1]  # Example
        for neighbor in neighbors:
            if neighbor < 10 and neighbor not in visited:  # Limit for demo
                queue.enqueue(neighbor)
                visited.add(neighbor)

bfs_simulation()

Line-by-Line Walkthrough

Queue follows FIFO principle. Essential for task scheduling, breadth-first search, and any scenario requiring ordered processing.

Complexity Analysis

  • Time: O(1) for all operations
  • Space: O(n)
  • Deque backing provides efficient front/back operations.

Common Mistakes

  • Using queue when LIFO semantics are needed
    Use stack for LIFO requirements

Try It Yourself

Implement queue using two stacks.

254. HashMap internal working.

What You'll Learn

Hash table implementation.

Real-World Context

"Key-value storage with fast lookups."

Breaking It Down

  • Hash function converts key to index
  • Handle collisions (separate chaining/open addressing)
  • Store key-value pairs in buckets
  • Resize when load factor exceeds threshold
Python 3
# Simple HashMap implementation
class SimpleHashMap:
    def __init__(self, size=10):
        self.size = size
        self.buckets = [[] for _ in range(size)]
        self.count = 0
    
    def _hash(self, key):
        return hash(key) % self.size
    
    def put(self, key, value):
        index = self._hash(key)
        bucket = self.buckets[index]
        
        # Check if key exists
        for i, (k, v) in enumerate(bucket):
            if k == key:
                bucket[i] = (key, value)
                return
        
        bucket.append((key, value))
        self.count += 1
        
        # Resize if load factor > 0.75
        if self.count / self.size > 0.75:
            self._resize()
    
    def get(self, key):
        index = self._hash(key)
        bucket = self.buckets[index]
        
        for k, v in bucket:
            if k == key:
                return v
        return None
    
    def _resize(self):
        old_buckets = self.buckets
        self.size *= 2
        self.buckets = [[] for _ in range(self.size)]
        self.count = 0
        
        for bucket in old_buckets:
            for key, value in bucket:
                self.put(key, value)

# Usage
hm = SimpleHashMap()
hm.put("name", "Alice")
hm.put("age", 30)
hm.put("city", "NYC")

print("Name:", hm.get("name"))
print("Age:", hm.get("age"))
print("City:", hm.get("city"))

Line-by-Line Walkthrough

HashMap uses hash function for O(1) average case operations. Collisions are resolved through chaining. Load factor triggers resizing.

Complexity Analysis

  • Time: O(1) average, O(n) worst case
  • Space: O(n)
  • Hash function distributes keys, but collisions can degrade performance.

Common Mistakes

  • Using mutable objects as keys
    Use immutable keys to prevent hash changes

Try It Yourself

Implement different collision resolution strategies.

255. HashSet vs TreeSet performance.

What You'll Learn

Set implementation trade-offs.

Real-World Context

"Choosing between hash-based and tree-based sets."

Breaking It Down

  • HashSet: O(1) operations, unordered
  • TreeSet: O(log n) operations, ordered
  • HashSet for fast lookups, TreeSet for sorted iteration
  • Memory usage and null handling differences
Python 3
# Python set (hash-based) vs sorted containers
from sortedcontainers import SortedSet  # Would need external library

# HashSet equivalent (unordered)
hash_set = set()
hash_set.add(30)
hash_set.add(10)
hash_set.add(20)
hash_set.add(10)  # Duplicate, ignored

print("HashSet (unordered):", hash_set)
print("Contains 20:", 20 in hash_set)  # O(1)

# TreeSet equivalent (ordered) - simulate with sorted list
tree_set = []
def add_to_tree_set(value):
    if value not in tree_set:
        tree_set.append(value)
        tree_set.sort()

add_to_tree_set(30)
add_to_tree_set(10)
add_to_tree_set(20)
add_to_tree_set(10)

print("TreeSet (ordered):", tree_set)
print("Contains 20:", 20 in tree_set)  # O(n) for search

# Performance comparison
import time

# Large dataset
data = list(range(10000))

# HashSet performance
start = time.time()
hash_set = set(data)
for i in range(10000):
    i in hash_set
hash_time = time.time() - start

# TreeSet simulation
start = time.time()
tree_set = sorted(data)
for i in range(10000):
    i in tree_set  # Linear search
tree_time = time.time() - start

print(f"\nHashSet time: {hash_time:.4f}s")
print(f"TreeSet time: {tree_time:.4f}s")
print(f"HashSet is {tree_time/hash_time:.1f}x faster")

Line-by-Line Walkthrough

HashSet provides O(1) operations but no ordering. TreeSet maintains sorted order with O(log n) operations. Choose based on access patterns.

Complexity Analysis

  • Time: HashSet: O(1) | TreeSet: O(log n)
  • Space: HashSet: O(n) | TreeSet: O(n)
  • HashSet uses hashing, TreeSet uses balanced BST.

Common Mistakes

  • Using TreeSet when order doesn't matter
    Use HashSet for better performance when ordering is not needed

Try It Yourself

Implement a custom set that combines both approaches.

Design Patterns (271-280)

Common solutions to recurring design problems.

271. Singleton pattern.

What You'll Learn

Ensuring single instance of a class.

Real-World Context

"Global access to unique object instance."

Breaking It Down

  • Private constructor to prevent instantiation
  • Static method to get instance
  • Lazy initialization or eager initialization
  • Thread safety considerations
Python 3
class Singleton:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, '_initialized'):
            self._initialized = True
            self.data = "Singleton data"

# Usage
s1 = Singleton()
s1.data = "Modified by s1"

s2 = Singleton()
print("s2.data:", s2.data)  # Same instance
print("Same instance?", s1 is s2)  # True

# Real-world example: Database connection
class DatabaseConnection:
    _instance = None
    
    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance
    
    def __init__(self):
        if not hasattr(self, '_initialized'):
            self._initialized = True
            self.connection_string = "Connected to database"
            print("Database connection established")
    
    def query(self, sql):
        return f"Executing: {sql}"

# Only one connection created
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print("Same connection?", db1 is db2)
print(db1.query("SELECT * FROM users"))

Line-by-Line Walkthrough

Singleton ensures only one instance exists. Useful for database connections, configuration managers, and logging systems.

Complexity Analysis

  • Time: O(1) for getInstance()
  • Space: O(1) extra
  • Single instance with static access.

Common Mistakes

  • Not handling thread safety
    Use double-checked locking or static initialization

Try It Yourself

Implement thread-safe singleton with different initialization strategies.

272. Factory pattern.

What You'll Learn

Object creation through factory methods.

Real-World Context

"Delegating object creation to factory classes."

Breaking It Down

  • Define factory interface/abstract class
  • Concrete factories implement creation logic
  • Client uses factory without knowing concrete classes
  • Promotes loose coupling and extensibility
Python 3
from abc import ABC, abstractmethod

# Product interface
class Shape(ABC):
    @abstractmethod
    def draw(self):
        pass

# Concrete products
class Circle(Shape):
    def draw(self):
        return "Drawing a circle"

class Square(Shape):
    def draw(self):
        return "Drawing a square"

class Triangle(Shape):
    def draw(self):
        return "Drawing a triangle"

# Factory
class ShapeFactory:
    @staticmethod
    def create_shape(shape_type):
        if shape_type == "circle":
            return Circle()
        elif shape_type == "square":
            return Square()
        elif shape_type == "triangle":
            return Triangle()
        else:
            raise ValueError(f"Unknown shape type: {shape_type}")

# Usage
shapes = ["circle", "square", "triangle", "circle"]

for shape_type in shapes:
    shape = ShapeFactory.create_shape(shape_type)
    print(shape.draw())

# Real-world example: Database connection factory
class DatabaseConnection(ABC):
    @abstractmethod
    def connect(self):
        pass

class MySQLConnection(DatabaseConnection):
    def connect(self):
        return "Connected to MySQL database"

class PostgreSQLConnection(DatabaseConnection):
    def connect(self):
        return "Connected to PostgreSQL database"

class DatabaseFactory:
    @staticmethod
    def create_connection(db_type):
        if db_type == "mysql":
            return MySQLConnection()
        elif db_type == "postgresql":
            return PostgreSQLConnection()
        else:
            raise ValueError(f"Unsupported database: {db_type}")

# Usage
db = DatabaseFactory.create_connection("mysql")
print(db.connect())

Line-by-Line Walkthrough

Factory pattern encapsulates object creation logic. Client code depends on abstractions, making it easy to add new product types.

Complexity Analysis

  • Time: O(1) for creation
  • Space: O(1) per object
  • Simple instantiation logic.

Common Mistakes

  • Tightly coupling client to concrete classes
    Use factory to abstract object creation

Try It Yourself

Implement Abstract Factory pattern for creating families of related objects.

273. Observer pattern.

What You'll Learn

One-to-many dependency between objects.

Real-World Context

"Notifying multiple objects when state changes."

Breaking It Down

  • Subject maintains list of observers
  • Observer interface defines update method
  • Concrete observers implement update logic
  • Subject notifies all observers on state change
Python 3
from abc import ABC, abstractmethod

# Observer interface
class Observer(ABC):
    @abstractmethod
    def update(self, message):
        pass

# Subject
class NewsPublisher:
    def __init__(self):
        self._observers = []
        self._latest_news = None
    
    def subscribe(self, observer):
        self._observers.append(observer)
    
    def unsubscribe(self, observer):
        self._observers.remove(observer)
    
    def notify_observers(self):
        for observer in self._observers:
            observer.update(self._latest_news)
    
    def publish_news(self, news):
        self._latest_news = news
        print(f"Publishing news: {news}")
        self.notify_observers()

# Concrete observers
class EmailSubscriber(Observer):
    def __init__(self, email):
        self.email = email
    
    def update(self, message):
        print(f"Email to {self.email}: {message}")

class SMSSubscriber(Observer):
    def __init__(self, phone):
        self.phone = phone
    
    def update(self, message):
        print(f"SMS to {self.phone}: {message}")

# Usage
publisher = NewsPublisher()

email_sub = EmailSubscriber("user@example.com")
sms_sub = SMSSubscriber("+1234567890")

publisher.subscribe(email_sub)
publisher.subscribe(sms_sub)

publisher.publish_news("Breaking: New product launched!")

publisher.unsubscribe(email_sub)
publisher.publish_news("Update: Product selling fast!")

Line-by-Line Walkthrough

Observer pattern enables loose coupling between subject and observers. Subject doesn't need to know concrete observer types.

Complexity Analysis

  • Time: O(n) for notification
  • Space: O(n) for observer list
  • Iterating through all observers.

Common Mistakes

  • Memory leaks from observer references
    Use weak references or proper cleanup

Try It Yourself

Implement observer with different notification strategies (push vs pull).

Git & Version Control (281-295)

Essential version control concepts and commands.

281. git init, clone, add, commit.

What You'll Learn

Basic Git workflow commands.

Real-World Context

"Initializing repository and saving changes."

Breaking It Down

  • git init: Create new repository
  • git clone: Copy existing repository
  • git add: Stage files for commit
  • git commit: Save staged changes
Python 3
# Simulating Git workflow with file operations
import os
import shutil
import json
from datetime import datetime

class SimpleGit:
    def __init__(self, repo_path):
        self.repo_path = repo_path
        self.git_dir = os.path.join(repo_path, '.git')
        self.index = {}  # Staging area
        self.commits = []
    
    def init(self):
        os.makedirs(self.git_dir, exist_ok=True)
        os.makedirs(os.path.join(self.git_dir, 'objects'), exist_ok=True)
        with open(os.path.join(self.git_dir, 'HEAD'), 'w') as f:
            f.write('ref: refs/heads/master\n')
        print("Initialized empty Git repository")
    
    def add(self, file_path):
        if os.path.exists(file_path):
            with open(file_path, 'r') as f:
                content = f.read()
            self.index[file_path] = content
            print(f"Added {file_path} to staging area")
        else:
            print(f"File {file_path} does not exist")
    
    def commit(self, message):
        if not self.index:
            print("Nothing to commit")
            return
        
        commit = {
            'message': message,
            'timestamp': datetime.now().isoformat(),
            'files': self.index.copy()
        }
        
        self.commits.append(commit)
        self.index.clear()
        
        print(f"Committed: {message}")
        print(f"Files changed: {len(commit['files'])}")
    
    def status(self):
        print("Staged files:")
        for file in self.index:
            print(f"  {file}")
        
        if not self.index:
            print("  (nothing staged)")

# Usage demonstration
repo = SimpleGit("my_project")
repo.init()

# Create some files
with open("README.md", "w") as f:
    f.write("# My Project\n\nThis is a sample project.")

with open("app.py", "w") as f:
    f.write("print('Hello, World!')")

# Git workflow
repo.add("README.md")
repo.add("app.py")
repo.status()
repo.commit("Initial commit")

# Modify and commit again
with open("app.py", "w") as f:
    f.write("print('Hello, Git!')")

repo.add("app.py")
repo.commit("Update greeting message")

Line-by-Line Walkthrough

Git workflow: init/clone to start, add to stage changes, commit to save. This creates a history of your project's changes.

Complexity Analysis

  • Time: O(1) for add, O(n) for commit
  • Space: O(n) for repository
  • File operations and content storage.

Common Mistakes

  • Committing without adding files
    Use git add to stage changes before commit

Try It Yourself

Implement git log to show commit history.

282. git push, pull, fetch difference.

What You'll Learn

Synchronizing with remote repositories.

Real-World Context

"Working with remote Git repositories."

Breaking It Down

  • git push: Upload local commits to remote
  • git pull: Fetch and merge from remote
  • git fetch: Download remote changes without merging
  • Understanding remote tracking branches
Python 3
# Simulating remote operations
import json
import os
from datetime import datetime

class RemoteRepository:
    def __init__(self, name):
        self.name = name
        self.branches = {"main": []}
    
    def push(self, branch, commits):
        if branch not in self.branches:
            self.branches[branch] = []
        
        # Add new commits
        existing_count = len(self.branches[branch])
        self.branches[branch].extend(commits)
        
        print(f"Pushed {len(commits)} commits to {self.name}/{branch}")
        return existing_count
    
    def fetch(self, branch):
        if branch in self.branches:
            return self.branches[branch].copy()
        return []

class LocalRepository:
    def __init__(self, name):
        self.name = name
        self.local_commits = []
        self.remote_commits = []
        self.remote = None
    
    def set_remote(self, remote):
        self.remote = remote
    
    def commit(self, message):
        commit = {
            "message": message,
            "timestamp": datetime.now().isoformat(),
            "id": f"commit_{len(self.local_commits) + 1}"
        }
        self.local_commits.append(commit)
        print(f"Committed locally: {message}")
    
    def push(self, branch="main"):
        if not self.remote:
            print("No remote repository configured")
            return
        
        pushed_count = self.remote.push(branch, self.local_commits[len(self.remote_commits):])
        if pushed_count < len(self.local_commits):
            print(f"Successfully pushed to {self.remote.name}")
        else:
            print("Already up to date")
    
    def fetch(self, branch="main"):
        if not self.remote:
            print("No remote repository configured")
            return
        
        remote_commits = self.remote.fetch(branch)
        new_commits = len(remote_commits) - len(self.remote_commits)
        
        if new_commits > 0:
            self.remote_commits = remote_commits
            print(f"Fetched {new_commits} new commits from {self.remote.name}")
        else:
            print("Already up to date")
    
    def pull(self, branch="main"):
        self.fetch(branch)
        # Simple merge simulation - just update local with remote
        if len(self.remote_commits) > len(self.local_commits):
            self.local_commits = self.remote_commits.copy()
            print(f"Merged remote changes into local branch")
    
    def status(self):
        print(f"Local repository: {self.name}")
        print(f"Local commits: {len(self.local_commits)}")
        print(f"Remote commits: {len(self.remote_commits)}")
        if self.remote:
            print(f"Remote: {self.remote.name}")

# Demonstration
remote = RemoteRepository("origin")
local = LocalRepository("my_repo")
local.set_remote(remote)

# Make some commits
local.commit("Initial commit")
local.commit("Add feature A")
local.commit("Fix bug B")

print("\nBefore push:")
local.status()

# Push to remote
local.push()

print("\nAfter push:")
local.status()

# Simulate another developer pushing
remote.push("main", [{"message": "Remote commit", "timestamp": datetime.now().isoformat(), "id": "remote_1"}])

print("\nAfter remote push:")
local.fetch()
local.status()

print("\nPull to merge:")
local.pull()
local.status()

Line-by-Line Walkthrough

Push uploads your commits, pull downloads and merges, fetch only downloads without merging. Use fetch to see changes before merging.

Complexity Analysis

  • Time: O(n) for transfer operations
  • Space: O(n) for commit storage
  • Network transfer and merge operations.

Common Mistakes

  • Using pull when you want to review changes first
    Use fetch to download, then merge manually if needed

Try It Yourself

Implement merge conflict detection and resolution.

283. Branching strategy.

What You'll Learn

Organizing development with branches.

Real-World Context

"Managing multiple lines of development."

Breaking It Down

  • main/master: Production-ready code
  • develop: Integration branch
  • feature branches: New features
  • release branches: Preparing releases
  • hotfix branches: Emergency fixes
Python 3
# Simulating Git Flow branching strategy
import json
from datetime import datetime

class GitBranch:
    def __init__(self, name):
        self.name = name
        self.commits = []
    
    def commit(self, message):
        commit = {
            "message": message,
            "timestamp": datetime.now().isoformat(),
            "branch": self.name
        }
        self.commits.append(commit)
        print(f"[{self.name}] Committed: {message}")

class GitRepository:
    def __init__(self):
        self.branches = {"main": GitBranch("main")}
        self.current_branch = "main"
    
    def create_branch(self, name, from_branch="main"):
        if name in self.branches:
            print(f"Branch {name} already exists")
            return
        
        # Copy commits from source branch
        self.branches[name] = GitBranch(name)
        self.branches[name].commits = self.branches[from_branch].commits.copy()
        print(f"Created branch '{name}' from '{from_branch}'")
    
    def checkout(self, branch_name):
        if branch_name not in self.branches:
            print(f"Branch {branch_name} does not exist")
            return
        self.current_branch = branch_name
        print(f"Switched to branch '{branch_name}'")
    
    def commit(self, message):
        self.branches[self.current_branch].commit(message)
    
    def merge(self, source_branch, target_branch="main"):
        if source_branch not in self.branches or target_branch not in self.branches:
            print("Branch does not exist")
            return
        
        source_commits = self.branches[source_branch].commits
        target_commits = self.branches[target_branch].commits
        
        # Simple merge - add new commits
        new_commits = []
        for commit in source_commits:
            if commit not in target_commits:
                new_commits.append(commit)
        
        self.branches[target_branch].commits.extend(new_commits)
        print(f"Merged {len(new_commits)} commits from '{source_branch}' to '{target_branch}'")

# Git Flow demonstration
repo = GitRepository()

# Start with main branch
repo.commit("Initial release")

# Create develop branch
repo.create_branch("develop")
repo.checkout("develop")
repo.commit("Start development")

# Create feature branch
repo.create_branch("feature/user-auth", "develop")
repo.checkout("feature/user-auth")
repo.commit("Add user authentication")
repo.commit("Add password hashing")
repo.commit("Add login/logout")

# Merge feature back to develop
repo.checkout("develop")
repo.merge("feature/user-auth", "develop")

# Create release branch
repo.create_branch("release/v1.1", "develop")
repo.checkout("release/v1.1")
repo.commit("Bump version to 1.1")
repo.commit("Update documentation")

# Merge release to main and develop
repo.checkout("main")
repo.merge("release/v1.1", "main")

repo.checkout("develop")
repo.merge("release/v1.1", "develop")

# Hotfix example
repo.create_branch("hotfix/security-patch", "main")
repo.checkout("hotfix/security-patch")
repo.commit("Fix security vulnerability")

# Merge hotfix to both main and develop
repo.checkout("main")
repo.merge("hotfix/security-patch", "main")

repo.checkout("develop")
repo.merge("hotfix/security-patch", "develop")

print("\nFinal state:")
for branch_name, branch in repo.branches.items():
    print(f"{branch_name}: {len(branch.commits)} commits")

Line-by-Line Walkthrough

Git Flow uses multiple branches for different purposes: main for production, develop for integration, feature branches for development, etc.

Complexity Analysis

  • Time: O(1) for branch operations, O(n) for merge
  • Space: O(n) for commit storage
  • Branch management and commit operations.

Common Mistakes

  • Committing directly to main branch
    Use feature branches and pull requests

Try It Yourself

Implement pull request workflow with code review simulation.