[一文搞定股票買賣問題]: 刷通[買賣股票]

121. 買賣一次:買賣股票的最佳時機

Difficulty: 簡單

給定一個數組,它的第 i 個元素是一支給定股票第 i 天的價格。

如果你最多隻允許完成一筆交易(即買入和賣出一支股票),設計一個算法來計算你所能獲取的最大利潤。

注意你不能在買入股票前賣出股票。

示例 1:

輸入: [7,1,5,3,6,4]
輸出: 5
解釋: 在第 2 天(股票價格 = 1)的時候買入,在第 5 天(股票價格 = 6)的時候賣出,最大利潤 = 6-1 = 5 。
     注意利潤不能是 7-1 = 6, 因爲賣出價格需要大於買入價格。

示例 2:

輸入: [7,6,4,3,1]
輸出: 0
解釋: 在這種情況下, 沒有交易完成, 所以最大利潤爲 0。

Solution

轉化爲最大子序和問題,詳解戳開鏈接

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0) return 0;
    	int[] fluc=new int[prices.length-1];
    	for(int i=1;i!=prices.length;i++) {
    		fluc[i-1]=prices[i]-prices[i-1];
    	}
    	int curMaxWithRight=0;
    	int res=0;
    	for(int j=0;j!=fluc.length;j++) {
    		curMaxWithRight=Math.max(curMaxWithRight+fluc[j],fluc[j]);
    		res=Math.max(res, curMaxWithRight);
    	}
    	return res;
    }
}

存儲波底和最大收益 O(n)

我們需要找到最小的谷之後的最大的峯。
我們可以維持兩個變量——minprice 和 maxprofit,它們分別對應迄今爲止所得到的最小的谷值和最大的利潤(賣出價格與最低價格之間的最大差值)。

class Solution {
    public int maxProfit(int[] prices) {
    	int minPrice=Integer.MAX_VALUE;//波底
    	int maxProfit=0;//最大獲利
    	for(int i=0;i!=prices.length;i++) {
    		minPrice=Math.min(minPrice, prices[i]);
    		maxProfit=Math.max(maxProfit, prices[i]-minPrice);
    	}
    	return maxProfit;
    }
}

自底向上動態優化問題

O(n)空間複雜度

class Solution {
    public int maxProfit(int[] prices) {
    	//爲空賺個屁
    	if(prices==null||prices.length==0) return 0;
    	//動態規劃 狀態轉移
    	int[][]dp=new int[prices.length][2];
    	//第一天不買 獲利0元 ; 第一天手裏已經買了 獲利第一天股票的負數
    	dp[0][0]=0;dp[0][1]=-prices[0];
    	for(int i=1;i!=prices.length;i++) {
    		//前一天也是沒有買 or前一天買了但今天賣出去了
    		dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
    		//前一天時就已經買了or前一天還沒買,今天才買的(本金爲0)
    		dp[i][1]=Math.max(dp[i-1][1],0-prices[i]);
    	}
    	return dp[prices.length-1][0];
    }
}

O(1)空間複雜度

class Solution {
    public int maxProfit(int[] prices) {
    	//爲空賺個屁
    	if(prices==null||prices.length==0) return 0;
    	//第一天不買 獲利0元 ; 第一天手裏已經買了 獲利第一天股票的負數
    	int yesterdayYes=-prices[0];//昨天買了
    	int yesterdayNo=0;//昨天沒有買
    	for(int i=1;i!=prices.length;i++) {
    		//前一天也是沒有買 or前一天買了但今天賣出去了
    		yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
    		//前一天時就已經買了or前一天還沒買,今天才買的(本金爲0)
    		yesterdayYes=Math.max(yesterdayYes,0-prices[i]);
    	}
    	//最終返回的是最後一天,手中沒有持有股票的情況
    	return yesterdayNo;
    }
}

122. 買賣次數不限:買賣股票的最佳時機 II

Difficulty: 簡單

給定一個數組,它的第 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。

Solution

動態優化自底向上

O(n)空間複雜度

class Solution {
    public int maxProfit(int[] prices) {
    	if(prices==null||prices.length==0) return 0;
    	int[][]dp=new int[prices.length][2];
    	dp[0][0]=0;dp[0][1]=-prices[0];
    	for(int i=1;i!=prices.length;i++) {
    		dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
    		//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
    		dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);	
    	}
    	return dp[prices.length-1][0];
    }
}

O(1)空間複雜度

class Solution {
    public int maxProfit(int[] prices) {
    	if(prices==null||prices.length==0) return 0;
    	int yesterdayNo=0,yesterdayYes=-prices[0];
    	for(int i=1;i!=prices.length;i++) {
    		//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
    		yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
    		//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
    		yesterdayYes=Math.max(yesterdayYes,yesterdayNo-prices[i]);
    	}
    	return yesterdayNo;
    }
}

123. 最多2筆法:買賣股票的最佳時機 III

Difficulty: 困難

給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你最多可以完成 _兩筆 _交易。

注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

示例 1:

輸入: [3,3,5,0,0,3,1,4]
輸出: 6
解釋: 在第 4 天(股票價格 = 0)的時候買入,在第 6 天(股票價格 = 3)的時候賣出,這筆交易所能獲得利潤 = 3-0 = 3 。
     隨後,在第 7 天(股票價格 = 1)的時候買入,在第 8 天 (股票價格 = 4)的時候賣出,這筆交易所能獲得利潤 = 4-1 = 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。

Solution

動態優化 解決 k次買賣問題

空間 O(n*k)

class Solution {
    public int maxProfit(int[] prices) {
        if(prices==null||prices.length==0) return 0;
    	int K=2; //本題爲k次
    	//K+1是爲了後面k爲0時準備的(int數組初始化默認爲0)
    	int[][][]dp=new int[prices.length][K+1][2];
    	// 初始化第一天
    	for(int k=0;k!=K+1;k++) {
    		dp[0][k][0]=0;
    		dp[0][k][1]=-prices[0];
    	}
    	for(int i=1;i!=prices.length;i++) {
    		//k 表示 此時爲第k次買|賣
    		for(int k=K;k!=0;k--) {
    			//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
    			dp[i][k][0]=Math.max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]);
        		//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
    			dp[i][k][1]=Math.max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]);	
    		}
    	}
    	return dp[prices.length-1][K][0];
    }
}  

188. 最多k筆:買賣股票的最佳時機 IV

Difficulty: 困難

給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。

設計一個算法來計算你所能獲取的最大利潤。你最多可以完成 k 筆交易。

注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。

示例 1:

輸入: [2,4,1], k = 2
輸出: 2
解釋: 在第 1 天 (股票價格 = 2) 的時候買入,在第 2 天 (股票價格 = 4) 的時候賣出,這筆交易所能獲得利潤 = 4-2 = 2 。

示例 2:

輸入: [3,2,6,5,0,3], k = 2
輸出: 7
解釋: 在第 2 天 (股票價格 = 2) 的時候買入,在第 3 天 (股票價格 = 6) 的時候賣出, 這筆交易所能獲得利潤 = 6-2 = 4 。
     隨後,在第 5 天 (股票價格 = 0) 的時候買入,在第 6 天 (股票價格 = 3) 的時候賣出, 這筆交易所能獲得利潤 = 3-0 = 3 。

Solution

Language: Java

class Solution {
    public int maxProfit(int k,int[] prices) {
        if(prices==null||prices.length==0) return 0;
        //如果k大於prices的一半了,不可能滿足 "必須在再次購買前出售掉之前的股票" 這個要求,相當於不限次數買賣
        if(k>prices.length/2) return maxProfitInf(prices);
    	//K+1是爲了後面k爲0時準備的(int數組初始化默認爲0)
    	int[][][]dp=new int[prices.length][k+1][2];
    	// 初始化第一天
    	for(int time=0;time!=k+1;time++) {
    		dp[0][time][0]=0;
    		dp[0][time][1]=-prices[0];
    	}
    	for(int i=1;i!=prices.length;i++) {
    		//k 表示 此時爲第k次買|賣
    		for(int time=k;time!=0;time--) {
    			//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
    			dp[i][time][0]=Math.max(dp[i-1][time][0],dp[i-1][time][1]+prices[i]);
        		//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
    			dp[i][time][1]=Math.max(dp[i-1][time][1],dp[i-1][time-1][0]-prices[i]);	
    		}
    	}
    	return dp[prices.length-1][k][0];
    }

	private int maxProfitInf(int[] prices) {
    	int yesterdayNo=0,yesterdayYes=-prices[0];
    	for(int i=1;i!=prices.length;i++) {
    		//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
    		yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
    		//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
    		yesterdayYes=Math.max(yesterdayYes,yesterdayNo-prices[i]);
    	}
    	return yesterdayNo;
	}
} 

309. 冷凍期:最佳買賣股票時機含冷凍期

Difficulty: 中等

給定一個整數數組,其中第_ i_ 個元素代表了第 i 天的股票價格 。​

設計一個算法計算出最大利潤。在滿足以下約束條件下,你可以儘可能地完成更多的交易(多次買賣一支股票):

  • 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
  • 賣出股票後,你無法在第二天買入股票 (即冷凍期爲 1 天)。

示例:

輸入: [1,2,3,0,2]
輸出: 3 
解釋: 對應的交易狀態爲: [買入, 賣出, 冷凍期, 買入, 賣出]

Solution

O(n)空間複雜度的

class Solution {
    //多次買賣一支股票 相當於買賣不限次數(最大price.length/2)
    //冷凍期爲 1 天 所以狀態轉移時有i-2
    public int maxProfit(int[] prices) {
        //長度不夠 不能買賣
        if(prices==null||prices.length<=1) return 0;
        int[][]dp=new int[prices.length][2];
        dp[0][0]=0;dp[0][1]=-prices[0];
        //前一天沒持有股票OR 前一天持有股票了但今天賣了
        dp[1][0]=Math.max(0,-prices[0]+prices[1]);
        //前一天持有股票但今天還沒賣 OR 前一天沒持有股票,今天持有的
        dp[1][1]=Math.max(-prices[0],0-prices[1]);// 其實就是在第一天和第二天取小的那個買(這裏取負數了所以有點難理解)
        for(int i=2;i!=prices.length;i++) {
        	dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
        	// 昨天已經持有了OR前天已經前天前沒有持有(避開冷凍期),今天再買
        	dp[i][1]=Math.max(dp[i-1][1],dp[i-2][0]-prices[i]);
        }
        return dp[prices.length-1][0];
    }
} 

O(1)空間複雜度

class Solution {
    //多次買賣一支股票 相當於買賣不限次數(最大price.length/2)
    //冷凍期爲 1 天 所以狀態轉移時有i-2
    public int maxProfit(int[] prices) {
        //長度不夠 不能買賣
        if(prices==null||prices.length<=1) return 0;
        //還沒買呢, 怎麼可能擁有,爲負無窮,利於max操作
        int yesterdayYes=Integer.MIN_VALUE;
        int yesterdayNo=0;
        //前天賣了或者前天之前買了,初始化爲0(因爲沒有持有股票)
        int yesterday2No=0;
        for(int i=0;i!=prices.length;i++) {
        	int tmp=yesterdayNo;
        	//昨天沒有持有OR前天沒有持有昨天也沒持有,今天買了
        	yesterdayYes=Math.max(yesterdayYes,yesterday2No-prices[i]);
        	yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
        	yesterday2No=tmp;//昨天變前天了
        }
        return yesterdayNo;
    }
} 

714. 手續費:買賣股票的最佳時機含手續費

Difficulty: 中等

給定一個整數數組 prices,其中第 i 個元素代表了第 i 天的股票價格 ;非負整數 fee 代表了交易股票的手續費用。

你可以無限次地完成交易,但是你每次交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。

返回獲得利潤的最大值。

示例 1:

輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
輸出: 8
解釋: 能夠達到的最大利潤:  
在此處買入 prices[0] = 1
在此處賣出 prices[3] = 8
在此處買入 prices[4] = 4
在此處賣出 prices[5] = 9
總利潤: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.

注意:

  • 0 < prices.length <= 50000.
  • 0 < prices[i] < 50000.
  • 0 <= fee < 50000.

Solution

Language: Java

class Solution {
    //無限次地完成交易
    public int maxProfit(int[] prices, int fee) {
    	if(prices==null||prices.length<=1) return 0;
    	int[][]dp=new int[prices.length][2];
    	dp[0][0]=0;dp[0][1]=-prices[0]-fee;
    	for(int i=1;i!=prices.length;i++) {
    		dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
    		dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]-fee);
    	}
    	return dp[prices.length-1][0];
    }
}

參考link(這是真大佬)

  • https://github.com/labuladong/fucking-algorithm/blob/master/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%B3%BB%E5%88%97/%E5%9B%A2%E7%81%AD%E8%82%A1%E7%A5%A8%E9%97%AE%E9%A2%98.md
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章