The problem is: assume there is a stock a, a [1... n] indicates the stock price of stock a corresponding to the nth day of Stock A. Try to find a pair of I, j, satisfying 1 <= I <= j <= n, and a [J]-A [I] gets the maximum value. The following table
| |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
| A |
2 |
7 |
1 |
8 |
2 |
8 |
4 |
5 |
9 |
0 |
4 |
5 |
In other words, you know the price of a stock in several days. Now you need to buy a stock in one day and sell it in another day to maximize the profits. As shown in the preceding table, we can buy the product on the seventh day and sell the product on the seventh day. In this way, a [9]-A [3] = 9-1 = 8, if you buy and sell at any other time point, the result is no more than 8. Therefore, 8 is the final answer to the problem. Of course, you may want to buy on the first day and sell on the second day. At this time, the profit is a [10]-A [9] = 0-9 =-9, the absolute value of the benefit obtained at this time reaches 9, but this is a negative benefit, not the expected result.
For a [1... n], the biggest positive benefit is not always possible, such as a [1... 9] = {9, 8, 7, 6, 5, 4, 3, 2, 1}, or a [9] = {0, 0, 0, 0, 0, 0, in any two days, if you buy or sell a product, the result is negative or zero. In this case, we allow I = J, that is, when I> J is not allowed to be sold on the day of purchase, that is, buy first before selling. Below are some solutions to this problem.
Exhaustion
This method is the easiest way to think... n] All the day arrays and (I, j) (1 <= I <= N, I <= j <= N ), calculate the values of a [J]-A [I] separately. The vertices in all results are the final results. The time complexity of the entire algorithm is O (n ^ 2) because we need to raise two groups of n data ). The Code is as follows:
int SingleSellProfit(const int array[], size_t array_size){unsigned int i, j;int profit = INT_MIN;for(i = 0; i < array_size; ++i){for(j = i; j < array_size; ++j){if(array[j] - array[i] > profit){profit = array[j] - array[i];}}}return profit;}
Divide and conquer Law
The time complexity of O (N ^ 2) obviously cannot satisfy us. Is there a better solution to this problem. For a [1... n]. If K (1 <= k <n) exists, divide the array into two parts: A1 [1... k], A2 [k + 1... n]. If we know the largest positive returns of A1, P1 and A2, and P2, can we calculate the largest positive returns of.
Here, we need to consider three situations: for the largest positive income P of A, if the time for buying and selling is between 1 and ~ In the range of K, P = p1. If the purchase and sale time are both in the range of K + 1 ~ In the range of N, P = P2; another case is in the range of 1 ~ Buy at a certain point in K range, in k + 1 ~ If J is sold at a point in the N range, the biggest positive benefit of A is k + 1 ~ The highest point of stock price in the N range and 1 ~ The difference between the lowest point of the stock price in the range of K. In fact, the value of P is equal to the maximum value of P1, P2, a [J]-A [I. With this conclusion, we can use the divide and conquer method ~ The problems in the N interval are divided into subproblems between two cells. The final answer is obtained by combining the results of the subproblems between the two cells. The following code is implemented using recursive algorithms:
Int max (int A, int B, int c) {if (a >= B & A >=c) {return ;} if (B >=a & B >=c) {return B ;}if (C >=a & C >= B) {return C ;}} typedef struct {int profit; unsigned int pmin; // minimum stock price date unsigned int Pmax; // maximum stock price date} ret; RET subroutine (const int array [], unsigned int begin, unsigned int end) {unsigned int middle; ret r, R1, R2; If (begin = END) {R. profit = 0; R. pmin = begin; R. pmax = begin; return r;} middle = (begin + E Nd)/2; R1 = subroutine (array, begin, middle); r2 = subroutine (array, middle + 1, end); R. pmin = array [r1.pmin] <array [r2.pmin]? R1.pmin: r2.pmin; R. Pmax = array [r1.pmax]> array [r2.pmax]? R1.pmax: r2.pmax; R. profit = max (r1.profit, r2.profit, array [r2.pmax]-array [r1.pmin]); return r;} int singlesellprofit (const int array [], size_t array_size) {RET r = subroutine (array, 0, array_size-1); return r. profit ;}
Note that the recursive termination condition is when a is decomposed into a single element array. For a single element array, such as a [2] = 7, the biggest positive benefit of A is a [2]-A [2] = 0, the highest share price is a [2] = 7, and the lowest share price is a [2] = 7. For the result of solving a subinterval, it cannot be determined whether it is used with the previous subinterval or the subinterval following it to generate a larger interval. Therefore, during the calculation process, returns the highest and lowest share prices for this interval. This section describes the recursive call process of the array given at the beginning of the article on the use of the divide and conquer method. The data content in the connection lines between adjacent layers is {maximum positive income of the lower-layer interval, minimum stock price of the lower-layer interval, and maximum stock price of the lower-layer interval}
The time complexity of this algorithm for solving a transaction problem is O (n), and the space complexity is O (logn ).
Progressive Method
At first glance, the division and control method is already an ideal solution for solving this problem, but in fact, there are more sharp algorithms for solving this problem. Suppose we have obtained a [1... k], how do we obtain the largest positive gain P when adding a [k + 1] at the end of the array? A [1... k + 1.
There are also three cases for a [k + 1] that need to be handled separately.
First, a [k + 1] is the selling point. In this case, assume a [1... if the lowest share price in K] Is I (1 <= I <= K), use a [k + 1]-A [I], the maximum positive benefit that can be obtained by using a [k + 1] As the selling point. If the value is greater than P, then a [k + 1]-A [I] is the array a [1... k + 1.
Second, a [k + 1] is the purchase point. If a [k + 1] is the largest possible purchase point in the world, then the corresponding selling point j> = k + 1, for a [1... k + 1], p is still the biggest positive benefit of this range. However, a [k + 1] Must be smaller than a [1... A [I] (1 <= I <= K ), otherwise, a [J]-A [I]> A [J]-A [k + 1], which corresponds to a [k + 1], it is a purchase point conflict of the largest positive benefit in the world.
Third, if the above two conditions are not met, a [k + 1] is neither a buy point nor a sell point.
With the analysis of the above three cases, we can give the recursive algorithm steps for this problem.
- Increase K from 1 to n
- When K = 1, record the current maximum positive income p = 0, the lowest point of the stock price I = 1, increasing K.
- Determine the relationship between a [k]-A [I] and P. If a [k]-A [I]> P, increase P = A [k]-A [I] by K.
- If a [k]-A [I] <= P, judge the relationship between a [k] And a [I]. If a [k] <A [I], increase I = K.
- Recurrence until the end of the array, p is the biggest positive benefit.
The code implementation of this algorithm is as follows:
int SingleSellProfit(const int array[], size_t array_size){unsigned int i, pmin;int profit = 0;for(i = 0, pmin = 0; i < array_size; ++i){if(array[i] - array[pmin] > profit){profit = array[i] - array[pmin];continue;}if(array[i] < array[pmin]){pmin = i;}}return profit;}
This algorithm can solve this problem in the case of O (n) time complexity and O (1) space complexity.
Postscript
The problem is not complex, but it is an excellent example to illustrate the exquisite algorithm. For a problem, the deeper the research, the more exciting the excellent solution. But in general, simple algorithms are intuitive and easy to understand, code implementation is easy, and correctness is easy to Ensure. The disadvantage is that the time or space complexity is high. Complex algorithms are often difficult to understand, coding implementation is prone to errors, and program correctness is not guaranteed. Therefore, we need to select a suitable solution for specific problems in specific applications.
Taking the problem in this article as an example, if the task only needs to analyze a batch of data, the exhaustive method is undoubtedly a good choice. You don't have to worry too much about how to develop a basic and correct program to complete data processing, if you use the divide and conquer method, maybe someone else has produced the analysis result, and you are still debugging the program. The advantages of the efficient algorithm are completely invisible. However, if you want to use a stock analysis software, you need to analyze the historical data of hundreds or thousands of stocks over the past few decades. The algorithm does not have to run several times, but hundreds or even hundreds of thousands of times, therefore, you must carefully consider the algorithm selection. in actual application, the running time of O (N) and O (N ^ 2) is not slightly different.
Of course, the third solution to this problem, the progressive method, seems to provide a perfect solution, both concise and efficient. However, in actual work, it is not easy to put forward such a beautiful solution for a problem. It is possible to complete the problem only after in-depth analysis of the problem itself. The beautiful solution to many problems is often discovered after several years of proposal. It can only be said to be an exception but not an issue. This is like the kadane Algorithm for Finding the maximum substring and introduced in my article. It was discovered seven years after the question was raised. Although the principle is not complex, however, it cannot be accomplished overnight. It can be seen that algorithms are an art, and applying algorithms to specific scenarios is also an art.