1

We have an array of numbers representing prices over time. For example, we have [10, 4, 6, 8, 2, 5, 3, 9, 1]. We want to know when would be the optimal time to buy and to sell to maximize profits. In this case, we would buy at time[4] = 2 and sell at time[7] = 9 for a profit of 9 - 2 = 7.

Mathematically, we are looking for a and b where a <= b and time[b] - time[a] is maximal.

Making an algorithm with complexity O(nlogn) using divide and conquer is somewhat trivial. I have been looking for an algorithm with worst-case O(n) for a while without any success. Any help would be appreciated.

3 Answers3

2

No need for divide and conquer here. Iterate over the array from oldest to newest prices and at each step compare the current price with the lowest price found so far.

hugomg
  • 68,213
  • 24
  • 160
  • 246
0

[10, 4, 6, 8, 2, 5, 3, 9, 1].

Convert the above list into its gradients in O(n). [-6,2,2,-6,3,-2,6,-8]

Apply Kadane's algorithm to find the maximum subarray O(n): http://en.wikipedia.org/wiki/Maximum_subarray_problem

^Modify to store the starting position and end position.

Use starting position from kadane's algorithm to find original array's starting and end position.

mattsap
  • 3,790
  • 1
  • 15
  • 36
0

If we as a first step pre-compute a vector that specifies the minimum price for each range (0, i-1) for index i, and then compute the the maximum we can make if we sell at index i, we can easily compute it in O(n) time:

Code:

#include <iostream>
#include <algorithm>
#include <limits>
#include <vector>
using namespace std;

int main() {
    vector<int> prices{10, 4, 6, 8, 2, 5, 3, 9, 1};
    // sell at time [i] requires buy at minimum in range [0,i-1]
    // create vector of index at minimum for each range
    int currentMinimumIndex = 0;
    vector<int> minimums{0};
    for(size_t i = 1; i < prices.size(); ++i)
    {
        if (prices[i] < prices[currentMinimumIndex])
        {
            minimums.emplace_back(i);
            currentMinimumIndex = i;
        }
        else
            minimums.emplace_back(currentMinimumIndex);
    } // O(n)

    // at this point we have a lookup table for minimum in every range

    // attempt to maximize amount we can get if we sell at time i buy buying at minimum for (0,i-1)
    vector<int> maxSales{std::numeric_limits<int>::min()};
    for(size_t i=1;i<prices.size();++i)
    {
        maxSales.emplace_back(prices[i] - prices[minimums[i]]);
    } // O(n)

    // find maximum element
    auto maxSum = std::max_element(std::begin(maxSales), std::end(maxSales)); // O(n)
    auto sellAt = std::distance(std::begin(maxSales), maxSum);
    auto buyAt = minimums[sellAt];
    std::cout << "Maximum profit is " << *maxSum << std::endl;
    std::cout << "If we buy at index " << buyAt << " (price: " << prices[buyAt] << ")"
    << " and sell at " << sellAt << " (price: " << prices[sellAt] << ")" << std::endl;

    return 0;
}

Output:

Maximum profit is 7 If we buy at index 4 (price: 2) and sell at 7 (price: 9)

Live Example

Edit: This is a dynamic-programming esque approach and I now realize it's a bit overkill. If we do what hugomg said Then we simply would iterate left to right, storing the minimum value found thus far. At each new index i, we perform a subtraction to see if we have a new largest price. Linear in time, constant in space.

Community
  • 1
  • 1
AndyG
  • 39,700
  • 8
  • 109
  • 143