121
方法1:暴力搜索及时间优化
暴力搜索:从左向右遍历,以每个元素开头的所有情况都要进行计算比较。maxTemp代表当下遍历序列的利润,将现有最大利润和新遍历后的数组序列表示的利润进行比较,用maxPrice记录每次遍历比较后的最大利润。当所有情况都计算比较之后,自然有了最大利润。
class Solution {
public int maxProfit(int[] prices) {
int maxPrice =0;//记录最大利润,初始为0
int maxTemp ;
for(int i=0;i<prices.length;i++){
for(int j=i;j<prices.length;j++){
maxTemp=prices[j]-prices[i];
maxPrice=Math.max(maxPrice,maxTemp);
}
}
return maxPrice;
}
}
时间复杂度:O(n^2)
空间复杂度:O(1)
[7,1,5,3,6,4],以7开头的遍历:[7]、[7,1]、[7,1,5] ,当遍历到这我们就发现相比于[7,1,5] ,直接遍历[1,5] 我们会获得一个“正利润数组前缀”(类似子网前缀、栈前缀,我们也定义一个前缀来更好地表达如何降低时间复杂度)。
如何在暴力搜索基础上降低时间复杂度? 在遍历过程中发现正利润数组前缀,或者说是发现[7,1]这种负利润前缀,直接从1开始遍历。
for(int j=i+1;j<prices.length;j++){
maxTemp=prices[j]-prices[i];
if(maxTemp<0)
break; //发现负利润前缀跳出
maxPrice=Math.max(maxPrice,maxTemp);
}
遍历的序列[1,5,3]—>[1,5,3,6],计算了6-1,但在以5或者3开头数组序列计算例如[3、6] 计算了6-3。显然这有了不必要的计算,实际上并不必须第二层for循环。
那如何减少不必要的计算来降低时间复杂度?
方法2:动态规划及空间优化
定义一个变量minprice来记录利润折线图中的相对最低点,maxprice记录相对最高点 。建立状态转移:maxprice[i]=Math.max(maxprice[i-1],prices[i]-minprice); 只进行一次遍历。
class Solution {
public int maxProfit(int[] prices) {
int n =prices.length;
if(n==0) return 0;
int minprice =prices[0];
int maxprice[]=new int[n]; //默认为0
for(int i=1;i<n;i++){
minprice = Math.min(minprice,prices[i]);
maxprice[i]=Math.max(maxprice[i-1],prices[i]-minprice);
}
return maxprice[n-1];
}
}
时间复杂度:O(n)
空间复杂度:O(n)
只定义变量int maxprice就可以完成状态转移。空间复杂度:O(1)
方法3:贪心算法:从左向右遍历时,维护最小值low来计算差值利润,维护maxP记录所有差值中的最大值,在局部得到最大利润。(在每一次负利润前可能会有一次正利润数组,maxP记录下所有正利润中的最大值)。DP没有大量复用计算,贪心就和上面很类似了。
class Solution {
public int maxProfit(int[] prices) {
int maxP = 0;
int low = Integer.MAX_VALUE;
for(int p : prices) {
if(p < low) {
low = p;
}
maxP = Math.max(maxP, p - low);
}
return maxP;
}
}
122
直接看官答地址,DPGA走起。。
多次买卖利润最大,也就是低买高卖 找到价格折线图k>0的两端,minprice代表低端买入,条件prices[i+1]-prices[i] >0(k>0)找到高端i卖出作差。注意临界情况:prices[i+1]-prices[i] >0 &&i<prices.length-2 。[1,2,3] :2-1>0&&0<1 ,i++,3-2>0&&1==1 情况单独列出。
class Solution {
public int maxProfit(int[] prices) {
int minprice=0;
int sumprice=0; //总利润
if(prices.length<=1)
return 0;
for(int i=0;i<prices.length-1;i++){
if(prices[i+1]-prices[i] >0){
minprice=prices[i]; //股价升高的一开始,赋值给minprice代表买入
while(prices[i+1]-prices[i] >0 &&i<prices.length-2){ //寻找卖出点i;注意临界点prices.length-2
i++;
}
if(prices[i+1]-prices[i] >0 &&i==prices.length-2){ //临界情况
sumprice+=prices[i+1]-minprice;
break;
}
sumprice+=prices[i]-minprice;
}
}
return sumprice;
}
}
i++时,下标向前看会更简单。。。
public int maxProfit(int[] arr) {
if (arr == null || arr.length <= 1) return 0;
int ans = 0;
for (int i = 1; i < arr.length; i++) {
if (arr[i] > arr[i-1]) { // 卖出有利可图
ans += (arr[i] - arr[i-1]);
}
}
return ans;
}