Skip to content

Two Sum & Maximum Profit

Classic hash map problems for O(1) lookups

These two problems are foundational for understanding hash map usage and single-pass algorithms. Two Sum is often the first problem encountered on LeetCode (Problem #1), while Best Time to Buy and Sell Stock demonstrates elegant tracking of minimum/maximum values.


Two Sum

Problem Statement

Given an array of integers nums and an integer target, return indices of the two numbers such that they add up to target.

Constraints:

  • Each input has exactly one solution
  • You may not use the same element twice
  • You can return the answer in any order

Example

text
Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: nums[0] + nums[1] = 2 + 7 = 9, so we return [0, 1]
text
Input: nums = [3,2,4], target = 6
Output: [1,2]
Explanation: nums[1] + nums[2] = 2 + 4 = 6
text
Input: nums = [3,3], target = 6
Output: [0,1]
Explanation: nums[0] + nums[1] = 3 + 3 = 6

Approaches

Brute Force O(n2)

The most straightforward approach checks every pair of numbers using nested loops.

python
def twoSum_brute(nums: list[int], target: int) -> list[int]:
    """
    Check every pair of numbers.
    Time: O(n^2), Space: O(1)
    """
    for i in range(len(nums)):
        for j in range(i + 1, len(nums)):
            if nums[i] + nums[j] == target:
                return [i, j]
    return []

Complexity: Time O(n^2) · Space O(1)

  • Time: Nested loops iterate through all pairs - outer loop runs n times, inner loop runs n-i times, giving n*(n-1)/2 comparisons
  • Space: Only uses a constant number of variables (i, j) regardless of input size

Analysis:

  • Time Complexity: O(n2) due to nested loops
  • Space Complexity: O(1) - only uses constant extra space
  • Pros: Simple to implement, always finds solution if exists
  • Cons: Computationally expensive for large inputs

Hash Map O(n) - Optimal Solution

Use a hash map to store previously seen numbers and their indices. For each number, calculate its complement (target - num) and check if it exists in the map.

python
def twoSum(nums: list[int], target: int) -> list[int]:
    """
    Single-pass hash map solution.
    Time: O(n), Space: O(n)
    """
    seen = {}  # value -> index

    for i, num in enumerate(nums):
        complement = target - num
        if complement in seen:
            return [seen[complement], i]
        seen[num] = i

    return []
java
import java.util.*;

class Solution {
    public int[] twoSum(int[] nums, int target) {
        Map<Integer, Integer> seen = new HashMap<>();

        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (seen.containsKey(complement)) {
                return new int[]{seen.get(complement), i};
            }
            seen.put(nums[i], i);
        }

        return new int[]{};
    }
}

Complexity: Time O(n) · Space O(n)

  • Time: Single pass through array with O(1) hash map lookups and insertions per element
  • Space: Hash map stores up to n elements in worst case when no pair is found early

Why This Works:

  • Instead of looking for two numbers that sum to target, we look for each number's complement
  • Hash map provides O(1) lookup time
  • Single pass through the array is sufficient

Two-Pointer Approach (For Sorted Arrays)

If the array is sorted, use two pointers to find the pair in O(n) time with O(1) space.

python
def twoSum_sorted(nums: list[int], target: int) -> list[int]:
    """
    Two-pointer approach for sorted arrays.
    Time: O(n), Space: O(1)
    Note: Returns indices in sorted array, not original
    """
    left, right = 0, len(nums) - 1

    while left < right:
        current_sum = nums[left] + nums[right]
        if current_sum == target:
            return [left, right]
        elif current_sum < target:
            left += 1
        else:
            right -= 1

    return []

Complexity: Time O(n) · Space O(1)

  • Time: Two pointers traverse array at most once - each iteration moves one pointer closer, so at most n iterations
  • Space: Only uses two pointer variables regardless of input size

Mermaid Diagram

Complexity Analysis

ApproachTimeSpaceNotes
Brute ForceO(n2)O(1)Check all pairs
Hash MapO(n)O(n)Single pass, optimal time
Two PointersO(n)O(1)Requires sorted array

Variations

Two Sum II - Input Array Is Sorted (LeetCode 167)

  • Array is already sorted in ascending order
  • Use two-pointer technique for O(1) space
  • Return 1-indexed positions
python
def twoSum_ii(numbers: list[int], target: int) -> list[int]:
    left, right = 0, len(numbers) - 1
    while left < right:
        total = numbers[left] + numbers[right]
        if total == target:
            return [left + 1, right + 1]  # 1-indexed
        elif total < target:
            left += 1
        else:
            right -= 1
    return []

Complexity: Time O(n) · Space O(1)

  • Time: Two pointers converge in at most n steps since array is already sorted
  • Space: Constant space for two pointer variables only

Two Sum III - Data Structure Design (LeetCode 170)

  • Design a data structure that supports add and find operations
  • Trade-off between add-heavy vs find-heavy usage
python
class TwoSum:
    def __init__(self):
        self.nums = {}  # number -> count

    def add(self, number: int) -> None:
        self.nums[number] = self.nums.get(number, 0) + 1

    def find(self, value: int) -> bool:
        for num in self.nums:
            complement = value - num
            if complement in self.nums:
                if complement != num or self.nums[num] > 1:
                    return True
        return False

Complexity: Time O(1) add / O(n) find · Space O(n)

  • Time: add is O(1) hash map insertion; find iterates through all unique numbers with O(1) lookups
  • Space: Hash map stores counts for each unique number added

3Sum (LeetCode 15)

  • Find all unique triplets that sum to zero
  • Sort + two pointers for each element
python
def threeSum(nums: list[int]) -> list[list[int]]:
    nums.sort()
    result = []

    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i-1]:
            continue  # Skip duplicates

        left, right = i + 1, len(nums) - 1
        while left < right:
            total = nums[i] + nums[left] + nums[right]
            if total == 0:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left + 1]:
                    left += 1
                while left < right and nums[right] == nums[right - 1]:
                    right -= 1
                left += 1
                right -= 1
            elif total < 0:
                left += 1
            else:
                right -= 1

    return result

Complexity: Time O(n^2) · Space O(log n) to O(n)

  • Time: Sorting takes O(n log n), then outer loop O(n) with inner two-pointer O(n) each = O(n^2) dominates
  • Space: Sorting requires O(log n) to O(n) depending on implementation; output space excluded

4Sum (LeetCode 18)

  • Extend 3Sum with an additional loop
  • Time: O(n^3)

Maximum Profit (Best Time to Buy and Sell Stock)

Problem Statement

You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximize your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

Example

text
Input: prices = [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.
text
Input: prices = [7,6,4,3,1]
Output: 0
Explanation: No profitable transaction is possible (prices only decrease).

Approach

Key Insight: Track the minimum price seen so far, and at each day calculate the potential profit if we sold on that day.

Algorithm:

  1. Initialize min_price to infinity and max_profit to 0
  2. For each price in the array:
    • Update min_price if current price is lower
    • Calculate profit if we sold today: price - min_price
    • Update max_profit if this profit is higher
  3. Return max_profit

Solution

python
def maxProfit(prices: list[int]) -> int:
    """
    Track minimum price and maximum profit in single pass.
    Time: O(n), Space: O(1)
    """
    min_price = float('inf')
    max_profit = 0

    for price in prices:
        # Update minimum price seen so far
        min_price = min(min_price, price)
        # Calculate profit if we sold today
        profit = price - min_price
        # Update maximum profit
        max_profit = max(max_profit, profit)

    return max_profit
java
class Solution {
    public int maxProfit(int[] prices) {
        int minPrice = Integer.MAX_VALUE;
        int maxProfit = 0;

        for (int price : prices) {
            minPrice = Math.min(minPrice, price);
            maxProfit = Math.max(maxProfit, price - minPrice);
        }

        return maxProfit;
    }
}

Complexity: Time O(n) · Space O(1)

  • Time: Single pass through prices array with constant-time operations per element
  • Space: Only two variables (min_price, max_profit) regardless of input size

Alternative: Kadane's Algorithm Perspective

This problem can also be viewed through the lens of Kadane's algorithm for maximum subarray:

python
def maxProfit_kadane(prices: list[int]) -> int:
    """
    Transform to maximum subarray problem.
    Calculate daily gains/losses and find max contiguous sum.
    """
    if len(prices) < 2:
        return 0

    max_profit = 0
    current_profit = 0

    for i in range(1, len(prices)):
        daily_change = prices[i] - prices[i-1]
        current_profit = max(0, current_profit + daily_change)
        max_profit = max(max_profit, current_profit)

    return max_profit

Complexity: Time O(n) · Space O(1)

  • Time: Single pass computing daily differences and running maximum subarray sum
  • Space: Constant space for tracking current and maximum profit

Mermaid Diagram

Complexity Analysis

MetricValue
Time ComplexityO(n) - single pass through array
Space ComplexityO(1) - only two variables

Variations (Stock Series)

Stock II - Multiple Transactions (LeetCode 122)

You can complete as many transactions as you like (buy one, sell one, buy one, sell one).

python
def maxProfit_ii(prices: list[int]) -> int:
    """
    Collect all positive differences (upward movements).
    Time: O(n), Space: O(1)
    """
    profit = 0
    for i in range(1, len(prices)):
        if prices[i] > prices[i-1]:
            profit += prices[i] - prices[i-1]
    return profit

Complexity: Time O(n) · Space O(1)

  • Time: Single pass adding all positive day-over-day price increases
  • Space: Only one accumulator variable for total profit

Stock III - At Most Two Transactions (LeetCode 123)

python
def maxProfit_iii(prices: list[int]) -> int:
    """
    Track two transactions using state variables.
    Time: O(n), Space: O(1)
    """
    buy1, sell1 = float('-inf'), 0
    buy2, sell2 = float('-inf'), 0

    for price in prices:
        buy1 = max(buy1, -price)           # Max profit after 1st buy
        sell1 = max(sell1, buy1 + price)   # Max profit after 1st sell
        buy2 = max(buy2, sell1 - price)    # Max profit after 2nd buy
        sell2 = max(sell2, buy2 + price)   # Max profit after 2nd sell

    return sell2

Complexity: Time O(n) · Space O(1)

  • Time: Single pass updating four state variables with constant operations per price
  • Space: Fixed four variables tracking buy/sell states for two transactions

Stock IV - At Most K Transactions (LeetCode 188)

python
def maxProfit_iv(k: int, prices: list[int]) -> int:
    """
    DP solution for k transactions.
    Time: O(n*k), Space: O(k)
    """
    if not prices or k == 0:
        return 0

    n = len(prices)
    if k >= n // 2:  # Can make unlimited transactions
        return sum(max(0, prices[i] - prices[i-1]) for i in range(1, n))

    # buy[i] = max profit after i-th buy
    # sell[i] = max profit after i-th sell
    buy = [float('-inf')] * (k + 1)
    sell = [0] * (k + 1)

    for price in prices:
        for i in range(1, k + 1):
            buy[i] = max(buy[i], sell[i-1] - price)
            sell[i] = max(sell[i], buy[i] + price)

    return sell[k]

Complexity: Time O(n*k) · Space O(k)

  • Time: For each of n prices, update 2k state variables (k buys + k sells)
  • Space: Two arrays of size k+1 for buy and sell states

Stock with Cooldown (LeetCode 309)

After selling, you must wait one day before buying again.

python
def maxProfit_cooldown(prices: list[int]) -> int:
    """
    State machine: hold, sold, rest
    Time: O(n), Space: O(1)
    """
    if not prices:
        return 0

    hold = float('-inf')  # Holding stock
    sold = 0              # Just sold
    rest = 0              # Cooldown/ready to buy

    for price in prices:
        prev_hold = hold
        hold = max(hold, rest - price)    # Keep holding or buy
        rest = max(rest, sold)            # Keep resting or finish cooldown
        sold = prev_hold + price          # Sell today

    return max(sold, rest)

Complexity: Time O(n) · Space O(1)

  • Time: Single pass with state machine transitions - constant operations per price
  • Space: Three state variables (hold, sold, rest) regardless of input size

Stock with Transaction Fee (LeetCode 714)

Each transaction incurs a fee.

python
def maxProfit_fee(prices: list[int], fee: int) -> int:
    """
    Subtract fee when selling.
    Time: O(n), Space: O(1)
    """
    cash = 0              # Not holding stock
    hold = float('-inf')  # Holding stock

    for price in prices:
        cash = max(cash, hold + price - fee)  # Sell (pay fee)
        hold = max(hold, cash - price)        # Buy

    return cash

Complexity: Time O(n) · Space O(1)

  • Time: Single pass updating two states with constant-time operations per price
  • Space: Two variables (cash, hold) tracking profit in each state

Interview Follow-ups

Two Sum Follow-ups

  1. What if the array is sorted?

    • Use two-pointer technique for O(1) space
  2. What if there are multiple valid pairs?

    • Return all pairs or the first pair found
  3. What if elements can be used multiple times?

    • Modify the hash map logic
  4. Design a Two Sum data structure with streaming data

    • Use Two Sum III approach
  5. Extend to 3Sum or 4Sum

    • Reduce to Two Sum with additional loops

Maximum Profit Follow-ups

  1. What if you can do unlimited transactions?

    • Sum all positive daily differences (Stock II)
  2. What if there's a cooldown period?

    • Use state machine DP (Stock with Cooldown)
  3. What if there's a transaction fee?

    • Subtract fee when calculating profit (Stock with Fee)
  4. What if you can short sell?

    • Track both long and short positions
  5. What if you can only make k transactions?

    • Use Stock IV DP approach
  6. What if you want to find the buy and sell days?

    • Track indices alongside min_price
python
def maxProfit_with_days(prices: list[int]) -> tuple[int, int, int]:
    """Return (profit, buy_day, sell_day)"""
    min_price = float('inf')
    max_profit = 0
    buy_day = sell_day = 0
    min_day = 0

    for i, price in enumerate(prices):
        if price < min_price:
            min_price = price
            min_day = i
        profit = price - min_price
        if profit > max_profit:
            max_profit = profit
            buy_day = min_day
            sell_day = i

    return max_profit, buy_day, sell_day

Complexity: Time O(n) · Space O(1)

  • Time: Single pass tracking minimum price and optimal buy/sell days
  • Space: Fixed number of variables to track prices and day indices

Common Mistakes to Avoid

Two Sum

  • Forgetting that the same element cannot be used twice
  • Not handling the case where complement equals the current number
  • Returning values instead of indices

Maximum Profit

  • Selling before buying (must buy first, then sell in the future)
  • Not returning 0 when no profit is possible
  • Off-by-one errors with day indices

Key Takeaways

ConceptTwo SumMax Profit
Core TechniqueHash map for O(1) lookupTrack min/max in single pass
Time ComplexityO(n)O(n)
Space ComplexityO(n)O(1)
PatternComplement searchSliding window / Kadane's
Interview FrequencyVery HighVery High

References