问题:
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
提示:
1 <= prices.length <= 3 * 10 ^ 4
0 <= prices[i] <= 10 ^ 4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii
解法:
一、暴力搜索
根据题意:因为不限制交易次数,在每一天,可以根据当前是否持有股票选择相应的操作。“暴力搜索” 也叫 “回溯搜索”、“回溯法”,首先来个示例。
[7,1,5,3,6,4]
第一次循环,第 i 天股票价格=7,第 (i+1) 天股票价格=1,当前买的股票价格为0,最大利润为0,不买入(因为第i天股票价格>第(i+1)天股票价格,并且当前没有买股票
第二次循环,第 i 天股票价格=1,第 (i+1) 天股票价格=5,买入*(因为第i天股票价格<第(i+1)天股票价格,并且当前没有买股票)*
第三次循环,第 i 天股票价格=5,第 (i+1) 天股票价格=3,卖出(因为第i天股票价格>第(i+1)天股票价格,并且当前买了股票)(收益增加:第i天股票价格 - 买时的股票价格)
第四次循环,第i天股票价格为3,第 (i+1) 天股票价格为6,买入*(因为第i天股票价格<第(i+1)天股票价格,并且当前没有买股票)*
第五次循环,第i天股票价格为6,第(i+1)天股票价格为4,卖出(因为第i天股票价格>第(i+1)天股票价格,并且当前买了股票)(收益增加:第i天股票价格 - 买时的股票价格)
第六次循环,因为第(i+1)天股票不存在,判断当前是否有买入股票,那么有的话直接卖出
由此,我们可以使用foreach循环,写出下面的代码:
class Solution
{
/**
* @param Integer[] $prices
* @return Integer
*/
function maxProfit($prices)
{
//赚的钱 初始值为0
$income = 0;
$currentPrice = 0;
//当前买入的价格$current_price=0
foreach ($prices as $k => $price) {
//如果是最后1天,直接跳过
if (!isset($prices[$k + 1])) {
if ($currentPrice > 0) {
$income += $price - $currentPrice;
}
continue;
}
//如果 第i天的价格>第(i+1)天的价格 并且 当前current_price==0
if ($price < $prices[$k + 1] && $currentPrice === 0) {
// 第i天买入,记录current_price=第i天的价格
$currentPrice = $prices[$k];
} elseif ($price > $prices[$k + 1] && $currentPrice > 0) {
//如果 第i天的价格<第(i+1)天的价格 并且当前买入了
// 第i天卖出,赚的钱+=第i天的价格-current_price
$income += $price - $currentPrice;
$currentPrice = 0;
}
}
return $income;
}
}
对于数组[7,1,5,3,6,4]
无疑是能够获得正确结果 7 的。
不过,算法就是这么的不符合实际,给了这么个数组
[2, 1, 2, 0, 1]
没办法,题目说了0 <= prices[i] <= 10 ^ 4
,股票是存在0元的情况的,题目是大爷,听他的来。
修改一下,使用false来代表没有买入股票。
版本1:
class Solution
{
/**
* @param Integer[] $prices
* @return Integer
*/
function maxProfit($prices)
{
//赚的钱 初始值为0
$income = 0;
$currentPrice = false;
//当前买入的价格$current_price=0
foreach ($prices as $k => $price) {
//如果是最后1天,直接跳过
if (!isset($prices[$k + 1])) {
if ($currentPrice !== false) {
$income += $price - $currentPrice;
}
continue;
}
//如果 第i天的价格>第(i+1)天的价格 并且 当前current_price==0
if ($price < $prices[$k + 1] && $currentPrice === false) {
// 第i天买入,记录current_price=第i天的价格
$currentPrice = $prices[$k];
} elseif ($price > $prices[$k + 1] && $currentPrice !== false) {
//如果 第i天的价格<第(i+1)天的价格 并且当前买入了
// 第i天卖出,赚的钱+=第i天的价格-current_price
$income += $price - $currentPrice;
$currentPrice = false;
}
}
return $income;
}
}
什么?战胜18.78%?不行,改改再来
思路:
- 慢的可能性有哪些呢?foreach运行效率较低。改用while来试一试。
- 内存使用能否再缩短?$currentPrice占了一小块内存,既然股票是一个数组,那改成存储数组下标来标识当前买入的股票。
版本2:
class Solution
{
/**
* @param Integer[] $prices
* @return Integer
*/
function maxProfit($prices)
{
//赚的钱 初始值为0
$i = 0;
$income = 0;
$currentPricePos = false;
//当前买入的价格$current_price=0
while (true) {
if (!isset($prices[$i + 1])) {
if ($currentPricePos !== false) {
$income += $prices[$i] - $prices[$currentPricePos];
}
break;
}
if ($currentPricePos === false && $prices[$i] < $prices[$i + 1]) {
$currentPricePos = $i;
} elseif ($prices[$i] > $prices[$i + 1] && $currentPricePos !== false) {
$income += $prices[$i] - $prices[$currentPricePos];
$currentPricePos = false;
}
$i++;
}
return $income;
}
}
这个效率还差不多。搜索算法的效率也大概就到这里,那有没有其他算法能够更好提高效率呢?
二、贪心算法
这道题使用贪心算法的流程是这样的:
从第 i 天(这里 i >= 1)开始,与第 i - 1 的股价进行比较,如果股价有上升(严格上升),就将升高的股价( prices[i] - prices[i- 1] )记入总利润,按照这种算法,得到的结果就是符合题意的最大利润。
下面对这个算法进行几点说明:
1、该算法仅可以用于计算,但计算的过程并不是真正交易的过程,但可以用贪心算法计算题目要求的最大利润。下面说明这个等价性:以 [1, 2, 3, 4] 为例,这 4 天的股价依次上升,按照贪心算法,得到的最大利润是:
$res = ($prices[3] - $prices[2]) + ($prices[2] - $prices[1]) + ($prices[1] - $prices[0]) = $prices[3] - $prices[0];
写成代码如下:
/**
* @param Integer[] $prices
* @return Integer
*/
function maxProfit($prices)
{
$income = 0;
for ($i = 1; $i < count($prices); $i++) {
$temp = $prices[$i] - $prices[$i - 1];
if ($temp > 0) {
$income += $temp;
}
}
//当前买入的价格$current_price=0
return $income;
}
这运行效率,杠杠的。
最后给大家安利下算法题来源好网站:力扣
来一起学学算法吧!