Receiving an invitation to be interviewed by a fortune 50 (fifty) company located 11,000+ kilometers away can turn an average day of a software developer into an exciting one. Especially, if it's one of those companies that everybody knows, unless they've been living under a rock.
An online coding exercise and a phone conversation after, I received a confirmation, along with my flight itinerary and a hotel booking – all organized for me on a short notice, which I thought was a nice gesture on their behalf. Indeed, everyone involved was really polite and made an excellent impression. I wish this hadn't coincided with one of my busiest times in the year, being assigned to a major project one week before it was due, with only one day to get up to speed and deliver a functional authentication module. Thankfully, that wasn't much of a challenge, and I used whatever time I could find to prepare.
There were 4 interviews, with 4 professionals in total. Some general questions, which didn't allow me to talk much about my experience, and technical exercises in the style of Silicon Valley: here's the problem, how would you solve it to perform in O(N) time?
I've done my share of real life diverse and fairly complex algorithms, as having an intuitive affinity for performance and clever code can make it satisfying as hell to see 2 gigabytes of data processed in less than a second on an average computer, for instance. When something's not fast enough, I just create my version of it, if I can get away with it. The C# code highlighter, for instance, used for coloring the actual examples below was completely written from scratch, including the lexer, when I couldn't find an accurate and fast highlighter that produced the results I needed.
The first three vis-a-vis meetings were a delight. The fourth one consisted of a rather brief introduction (or a lack of) and a rather abrupt transition to a coding challenge. Writing parsers, approximate search engines, image filters and serializers at a leisurely pace is one thing. Standing at the whiteboard, having to start narratively working to arrive at a solution, with little time to gather thoughts and a rather unsympathetic examinator is not quite the same. A little reminiscent of school days, but that was more than a half life ago.
It was later revealed, by virtue of reciprocal questions, that the interviewer was someone who, to paraphrase, solves problems that no one else can. FTL immediately sprung to mind, along with string theory, cancer cure, Baryon Asymmetry, revealing the events before the Planck Epoch... At the very least, it should be a patented way to calculate prime numbers. But I digress.
The problem – finding the lowest stock price to buy at and the highest to sell at
Given an array of a certain length, with the indexes corresponding to days and values – to average stock prices, find two days that would yield the biggest profit.
So, clearly, we must buy on one day and find another to sell. Easy enough? It is, until you start thinking about how to solve it in the most efficient way. Despite starting on the right track, I ended up going down a O(N^2) path, narrating over my incomplete solution hastily scribbled on the board. When asked if there's an O(N) method, I couldn't see one fast enough to answer. It's a tough balance between interview preparations, meeting the deadline and getting enough rest to last through 4 sessions on an empty stomach. Something in that equation didn't quite work in favor of a clear head.
What's the solution already?
Ok, ok. All I needed was to sit down and gather my thoughts. Something like this should be as simple as 5*5. Not sure why it trips up so many people.
The O(N) time solution with O(1) space complexity is simpler than anything else. So, let's talk through it (where's my whiteboard):
What if the new minimum is larger than the original minimum? Doesn't matter! If the difference is larger – that's what we're looking for!
We only update the potential maximum if a larger value than the original maximum has been found. Otherwise – it'll always be the initial element, as in the case of: 1,2,3,4,5.
The minimum is updated every time that the difference with the potential maximum is larger than the original difference. If each value decreases when going backwards, as in the example above, it'll happen on each cycle. However, if the sequence is inverted, the minimum will never be updated. Thus, no good time to buy/sell would be found.
The algorithm:
public BestProfit FindMaximumProfit(double[] input) { if (input == null) return null; //or throw a null argument exception int i = input.Length; int bestMinIndex = i - 1; int bestMaxIndex = i - 1; double candidateMaxValue = 0; int candidateMaxIndex = 0; double bestDifference = 0; double difference = 0; while (--i >= 0) { if (input[i] > candidateMaxValue) { candidateMaxValue = input[i]; candidateMaxIndex = i; } difference = candidateMaxValue - input[i]; if (difference > bestDifference) { bestDifference = difference; bestMinIndex = i; bestMaxIndex = candidateMaxIndex; } } if(bestMaxIndex != bestMinIndex) return new BestProfit(bestMinIndex, bestMaxIndex, input[bestMinIndex], input[bestMaxIndex]); return null; } public class BestProfit { public int BuyIndex { get; set; } public int SellIndex { get; set; } public double BuyPrice { get; set; } public double SellPrice { get; set; } public BestProfit(int buyIndex, int sellIndex, double buyPrice, double sellPrice) { BuyIndex = buyIndex; SellIndex = sellIndex; BuyPrice = buyPrice; SellPrice = sellPrice; } }