DSA Tutorial
🔍

How to Approach Problems

How to Approach Problems

Staring at a coding problem and not knowing where to start? You're not alone. The difference between struggling for hours and solving problems efficiently comes down to one thing: having a systematic approach.

This tutorial will teach you a proven framework that works for any data structures and algorithms problem.

Goal: Learn a step-by-step method to approach and solve any problem confidently.

Key Insight: Most people fail not because they lack knowledge, but because they lack a systematic approach. A good framework makes even hard problems manageable.

The 7-Step Problem-Solving Framework

Follow these steps for every problem:

Step 1: Understand the Problem

Step 2: Explore Examples

Step 3: Break It Down

Step 4: Solve a Simpler Version

Step 5: Implement the Solution

Step 6: Test and Debug

Step 7: Optimize

Let's explore each step in detail.

Step 1: Understand the Problem

Time to spend: 2-3 minutes
Goal: Fully understand what you're being asked to do

Questions to Ask:

  1. What are the inputs?

    • What type? (array, string, number, graph, etc.)
    • What size? (constraints)
    • What values? (range, negatives allowed?, duplicates?)
  2. What are the outputs?

    • Return value type
    • Format (single value, array, boolean, etc.)
  3. What are the constraints?

    • Time limit
    • Space limit
    • Edge cases
  4. Can I restate the problem in my own words?

    • If you can't explain it simply, you don't understand it

Example Problem:

Problem: "Find the maximum sum of any contiguous subarray"

Understanding Phase:

  • Input: Array of integers (can be negative)
  • Output: Single integer (the maximum sum)
  • Constraints: Array length 1 to 10^5
  • Restate: Find a slice of the array where the sum of elements is highest

Questions to clarify:

  • Can array have all negative numbers? (Yes)
  • Is empty array possible? (No, at least 1 element)
  • What if array has one element? (Return that element)

Step 2: Explore Examples

Time to spend: 3-5 minutes
Goal: Understand the problem through concrete cases

Types of Examples to Consider:

  1. Simple example - Small, easy to trace
  2. Complex example - Larger, tests your understanding
  3. Edge cases - Boundary conditions
  4. Invalid inputs - What if inputs don't meet constraints?

Example Problem: Maximum Subarray Sum

Input: [2, -1, 3, -2, 5]

Examples to trace:

Example 1 - Simple:

Input: [2, -1, 3] Possible subarrays: [2] → sum = 2 [-1] → sum = -1 [3] → sum = 3 [2, -1] → sum = 1 [-1, 3] → sum = 2 [2, -1, 3] → sum = 4 Maximum: 4

Example 2 - All negative:

Input: [-5, -2, -8, -1] Maximum single element: -1 Output: -1

Example 3 - Single element:

Input: [5] Output: 5

Example 4 - Complex:

Input: [2, -1, 3, -2, 5] Best subarray: [2, -1, 3, -2, 5] → sum = 7 Output: 7

Step 3: Break It Down

Time to spend: 2-3 minutes
Goal: Write pseudocode or outline

Breaking Down the Problem:

  1. Write the function signature
  2. Outline the main steps in comments
  3. Identify data structures needed
  4. Note any helper functions

Example: Maximum Subarray Sum

Python
1def max_subarray_sum(arr): 2 # Step 1: Handle edge case - single element 3 4 # Step 2: Initialize variables 5 # - max_sum: track best sum found 6 # - current_sum: track current subarray sum 7 8 # Step 3: Iterate through array 9 # - Add element to current_sum 10 # - Update max_sum if needed 11 # - Reset current_sum if it goes negative 12 13 # Step 4: Return max_sum 14 15 pass

Benefits:

  • Catches logical errors before coding
  • Gives you a roadmap to follow
  • Makes interviews smoother (show your thinking)

Step 4: Solve a Simpler Version

Time to spend: 5-10 minutes
Goal: Solve the problem ignoring some constraints

Simplification Strategies:

  1. Use brute force - Don't worry about efficiency yet
  2. Reduce input size - Solve for array of 3 elements
  3. Remove constraints - Assume all positive numbers
  4. Use extra space - Don't worry about O(1) space yet

Example: Maximum Subarray Sum (Brute Force)

Python
1def max_subarray_sum_brute(arr): 2 n = len(arr) 3 max_sum = float('-inf') 4 5 # Try all possible subarrays 6 for i in range(n): 7 for j in range(i, n): 8 # Calculate sum of subarray arr[i:j+1] 9 current_sum = sum(arr[i:j+1]) 10 max_sum = max(max_sum, current_sum) 11 12 return max_sum 13 14# Time: O(n³) - Very slow! 15# Space: O(1) 16# But it works correctly!

Why brute force first?

  • Validates your understanding
  • Provides a baseline solution
  • Helps you identify what to optimize

Step 5: Implement the Solution

Time to spend: 10-15 minutes
Goal: Write clean, working code

Implementation Tips:

  1. Start with the simpler solution from Step 4
  2. Use clear variable names (not x, y, z)
  3. Add comments for complex logic
  4. Handle edge cases as you go
  5. Think about optimization as you write

Example: Optimized Solution (Kadane's Algorithm)

Python
1def max_subarray_sum(arr): 2 # Edge case: empty array (shouldn't happen per constraints) 3 if not arr: 4 return 0 5 6 # Initialize with first element 7 max_sum = arr[0] 8 current_sum = arr[0] 9 10 # Iterate from second element 11 for i in range(1, len(arr)): 12 # Either extend current subarray or start new one 13 current_sum = max(arr[i], current_sum + arr[i]) 14 15 # Update maximum if needed 16 max_sum = max(max_sum, current_sum) 17 18 return max_sum 19 20# Time: O(n) - Much better! 21# Space: O(1) 22 23# Test 24print(max_subarray_sum([2, -1, 3, -2, 5])) # Output: 7 25print(max_subarray_sum([-5, -2, -8, -1])) # Output: -1

Key Insight: By keeping track of current_sum, we avoid recalculating sums. If current_sum becomes negative, we start fresh (because adding a negative makes things worse).

Step 6: Test and Debug

Time to spend: 5-10 minutes
Goal: Verify correctness with various inputs

Test Cases to Run:

  1. Example cases from Step 2
  2. Edge cases:
    • Empty input
    • Single element
    • All same values
    • Minimum/maximum values
  3. Your custom cases

Debugging Strategy:

If you get wrong output:

  1. Print intermediate values
  2. Trace through manually with a small example
  3. Check loop boundaries (off-by-one errors)
  4. Verify edge case handling

Example: Testing Maximum Subarray

Python
1def test_max_subarray(): 2 # Test 1: Regular case 3 assert max_subarray_sum([2, -1, 3, -2, 5]) == 7 4 5 # Test 2: All negative 6 assert max_subarray_sum([-5, -2, -8, -1]) == -1 7 8 # Test 3: Single element 9 assert max_subarray_sum([5]) == 5 10 11 # Test 4: All positive 12 assert max_subarray_sum([1, 2, 3, 4]) == 10 13 14 # Test 5: Alternating signs 15 assert max_subarray_sum([1, -3, 2, -1, 3]) == 4 16 17 print("All tests passed!") 18 19test_max_subarray()

Step 7: Optimize

Time to spend: Variable (if needed)
Goal: Make your solution more efficient

Optimization Checklist:

Time Complexity:

  • Can I eliminate redundant calculations?
  • Can I use a better data structure? (hash map, heap, etc.)
  • Can I use a different algorithm? (divide and conquer, dynamic programming)

Space Complexity:

  • Can I reuse the input array?
  • Can I use constant space instead of extra arrays?
  • Can I avoid recursion to save stack space?

Code Quality:

  • Can I make variable names clearer?
  • Can I extract repeated logic into functions?
  • Can I simplify complex conditionals?

Optimization Example: From O(n³) to O(n)

We already optimized maximum subarray from brute force O(n³) to Kadane's algorithm O(n). Here's the progression:

Version 1: O(n³) - Triple nested loop
Version 2: O(n²) - Calculate sums incrementally
Version 3: O(n) - Kadane's algorithm (keep running sum)

Complete Example: Two Sum Problem

Let's apply the entire framework to a classic problem.

Problem Statement

Given: An array of integers and a target sum
Find: Indices of two numbers that add up to target
Constraint: Each input has exactly one solution, can't use same element twice

Step 1: Understand

  • Input: Array of integers, target integer
  • Output: Array of two indices
  • Restate: Find positions i and j where arr[i] + arr[j] = target

Step 2: Examples

Input: [2, 7, 11, 15], target = 9 Output: [0, 1] (because 2 + 7 = 9) Input: [3, 2, 4], target = 6 Output: [1, 2] (because 2 + 4 = 6) Input: [3, 3], target = 6 Output: [0, 1]

Step 3: Break It Down

Python
1def two_sum(nums, target): 2 # Approach: Use hash map to store complements 3 4 # Step 1: Create hash map to store value → index 5 6 # Step 2: Iterate through array 7 # - Calculate complement (target - current) 8 # - Check if complement exists in hash map 9 # - If yes: return [complement_index, current_index] 10 # - If no: add current to hash map 11 12 # Step 3: Return result 13 pass

Step 4: Brute Force Solution

Python
1def two_sum_brute(nums, target): 2 n = len(nums) 3 4 # Try all pairs 5 for i in range(n): 6 for j in range(i + 1, n): 7 if nums[i] + nums[j] == target: 8 return [i, j] 9 10 return [] 11 12# Time: O(n²) 13# Space: O(1)

Step 5: Optimized Solution

Python
1def two_sum(nums, target): 2 # Hash map: value → index 3 seen = {} 4 5 for i, num in enumerate(nums): 6 complement = target - num 7 8 # Check if complement exists 9 if complement in seen: 10 return [seen[complement], i] 11 12 # Store current number 13 seen[num] = i 14 15 return [] 16 17# Time: O(n) 18# Space: O(n) 19 20# Test 21print(two_sum([2, 7, 11, 15], 9)) # [0, 1] 22print(two_sum([3, 2, 4], 6)) # [1, 2]

Step 6: Test

All tests pass! The hash map solution correctly handles all cases.

Step 7: Optimize

Already optimized from O(n²) to O(n) using a hash map. Can't do better than O(n) since we must look at each element at least once.

Common Problem Patterns

Recognizing patterns helps you choose the right approach faster.

Pattern 1: Two Pointers

When to use: Sorted array, finding pairs, palindromes

Example: Find if array has two numbers that sum to target (sorted array)

Python
1def two_sum_sorted(arr, target): 2 left = 0 3 right = len(arr) - 1 4 5 while left < right: 6 current_sum = arr[left] + arr[right] 7 8 if current_sum == target: 9 return [left, right] 10 elif current_sum < target: 11 left += 1 12 else: 13 right -= 1 14 15 return [] 16 17# Time: O(n), Space: O(1)

Pattern 2: Sliding Window

When to use: Contiguous subarrays, substrings, fixed/variable window size

Example: Maximum sum of k consecutive elements

Python
1def max_sum_k_elements(arr, k): 2 # Initial window 3 window_sum = sum(arr[:k]) 4 max_sum = window_sum 5 6 # Slide window 7 for i in range(k, len(arr)): 8 window_sum = window_sum - arr[i - k] + arr[i] 9 max_sum = max(max_sum, window_sum) 10 11 return max_sum 12 13# Time: O(n), Space: O(1)

Pattern 3: Fast and Slow Pointers

When to use: Linked lists, cycle detection, finding middle

Example: Detect cycle in linked list (concept)

Fast pointer moves 2 steps, slow moves 1 step If they meet, there's a cycle

Pattern 4: Hash Map/Set

When to use: Looking up values, counting occurrences, finding duplicates

Example: Find first duplicate

Python
1def first_duplicate(arr): 2 seen = set() 3 4 for num in arr: 5 if num in seen: 6 return num 7 seen.add(num) 8 9 return -1 10 11# Time: O(n), Space: O(n)

Pattern 5: Divide and Conquer

When to use: Can split problem into independent subproblems

Examples: Merge sort, binary search, quick sort

Problem-Solving Tips

Tip 1: Don't Rush to Code

Spend time understanding and planning. 5 minutes of planning saves 30 minutes of debugging.

Tip 2: Start Simple

Brute force is better than no solution. You can optimize later.

Tip 3: Talk Through Your Thinking

In interviews, explain your approach. Interviewers want to see your thought process.

Tip 4: Draw Diagrams

Visualize the problem. Draw arrays, trees, graphs, or state transitions.

Tip 5: Look for Patterns

Similar problems often have similar solutions. Build a mental library of patterns.

Tip 6: Test As You Go

Don't wait until the end to test. Verify each part works as you build it.

Tip 7: Ask Questions

Clarify ambiguities before coding. It's better to ask than to solve the wrong problem.

When You're Stuck

Strategy 1: Solve a Smaller Version

Can't handle array of size n? Try n = 3 first.

Strategy 2: Work Backwards

Start from the desired output and work back to the input.

Strategy 3: Look for Similar Problems

Have you solved something similar? Can you adapt that approach?

Strategy 4: Try a Different Data Structure

Hash map not working? Try a heap, queue, or stack.

Strategy 5: Take a Break

Step away for 5 minutes. Fresh perspective helps.

Key Takeaways

The 7-Step Framework:

  1. Understand the problem
  2. Explore examples
  3. Break it down
  4. Solve simpler version
  5. Implement solution
  6. Test and debug
  7. Optimize

Core Principles:

  • Plan before you code
  • Start with brute force
  • Test early and often
  • Recognize common patterns
  • Communicate your thinking

Success Formula: Understanding + Strategy + Practice = Problem-Solving Mastery

What's Next?

Now that you have a problem-solving framework:

  1. Array Problems - Practice the framework on array problems
  2. String Problems - Apply to string manipulation
  3. Two Pointers Pattern - Master common patterns

Practice Problem

Apply the 7-step framework to this problem:

Problem: Given a sorted array, remove duplicates in-place such that each element appears only once. Return the new length.

Example:

Input: [1, 1, 2, 2, 3, 4, 4] Output: 4 (array becomes [1, 2, 3, 4, _, _, _])

Your turn: Work through steps 1-7!

Hint: Two pointers pattern works well here.

Congratulations! You now have a systematic approach to solve any problem. Practice this framework on every problem you encounter!

How to Approach Problems