买卖股票的最佳时机(多解法)
普通暴力解publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}intans0;for(inti0;iprices。length1;i){for(intji1;jprices。length;j){if(prices〔j〕prices〔i〕){ansMath。max(ans,prices〔j〕prices〔i〕);}}}returnans;}暴力递归解publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}returnprocess(prices,0,1,1);}publicintprocess(int〔〕prices,inti,intbuyDay,intsellDay){if(prices。lengthi){if(buyDay1buyDaysellDayprices〔buyDay〕prices〔sellDay〕){returnprices〔sellDay〕prices〔buyDay〕;}return0;}在第i天不操作intp0process(prices,i1,buyDay,sellDay);在第i天买入intp10;if(buyDay1sellDay1){p1process(prices,i1,i,sellDay);}在第i天卖出intp20;if(buyDay1sellDay1){p2prices〔i〕prices〔buyDay〕?prices〔i〕prices〔buyDay〕:0;}returnMath。max(p0,Math。max(p1,p2));}
另一种暴力递归可推导出动态规划:publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}returnprocess(prices,0,0);}publicintprocess(int〔〕prices,inti,intflag){basecaseif(i0){if(flag0){return0;}returnprices〔0〕;}if(flag0){第i天不持有股票returnMath。max(process(prices,i1,1)prices〔i〕,process(prices,i1,0));}else{returnMath。max(process(prices,i1,1),prices〔i〕);}}动态规划解
publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}intlenprices。length;dp〔i〕〔0〕下标为i这天结束的时候,不持股,手上拥有的现金数dp〔i〕〔1〕下标为i这天结束的时候,持股,手上拥有的现金数int〔〕〔〕dpnewint〔len〕〔2〕;初始化:不持股显然为0,持股就需要减去第1天(下标为0)的股价dp〔0〕〔0〕0;dp〔0〕〔1〕prices〔0〕;从第2天开始遍历for(inti1;ilen;i){dp〔i〕〔0〕Math。max(dp〔i1〕〔0〕,dp〔i1〕〔1〕prices〔i〕);dp〔i〕〔1〕Math。max(dp〔i1〕〔1〕,prices〔i〕);}returndp〔len1〕〔0〕;}
在暴力递归的基础上加缓存,转变成记忆化搜索:
最终版的动态规划是怎么写出来的呢?请看下图:
单调栈解
publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}intans0;intmin0;StackIntegerupStacknewStackInteger();inttopIndex1;记录栈顶下标int〔〕upStacknewint〔prices。length〕;for(inti0;iprices。length;i){由小到大的单调栈while(!upStack。empty()upStack。peek()prices〔i〕){while(topIndex1upStack〔topIndex〕prices〔i〕){intpupStack。pop();intpupStack〔topIndex〕;intminupStack〔0〕;if(pmin){ansMath。max(ans,pmin);}}if(upStack。empty()){minprices〔i〕;}upStack。push(prices〔i〕);upStack〔topIndex〕prices〔i〕;}栈顶元素未被处理if(upStack。size()1){if(topIndex0){intpupStack。pop();intpupStack〔topIndex〕;intminupStack〔0〕;if(pmin){ansMath。max(ans,pmin);}}returnans;}
直接使用java。util。Stack会对性能会造成影响,因此这里利用数组来优化。滑动窗口解publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}minWindow由小到大LinkedListIntegerminWindownewLinkedListInteger();for(intR0;Rprices。length;R){while(!minWindow。isEmpty()prices〔minWindow。peekLast()〕prices〔R〕){minWindow。pollLast();}minWindow。addLast(R);intbuyPriceprices〔minWindow。peekFirst()〕;intsellPriceprices〔R〕;if(sellPricebuyPrice){ansMath。max(ans,sellPricebuyPrice);}}returnans;}线段树解
publicintmaxProfit(int。。。prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}intans0;SegmentTreesegmentTreenewSegmentTree(prices);for(inti0;iprices。length;i){intbuyPriceprices〔i〕;intsellPricesegmentTree。query(i1,prices。length,1,prices。length,1);ansMath。max(ans,sellPricebuyPrice);}returnans;}publicclassSegmentTree{privateint〔〕data;privateint〔〕max;publicSegmentTree(int〔〕src){intNsrc。length1;datasrc;maxnewint〔N2〕;build(1,N1,1);}privatevoidpushUp(inti){max〔i〕Math。max(max〔i1〕,max〔i11〕);}privatevoidpushDown(inti){}privatevoidbuild(intl,intr,inti){if(lr){max〔i〕data〔l1〕;return;}intmid(lr)1;build(l,mid,i1);build(mid1,r,i11);pushUp(i);}publicintquery(intL,intR,intl,intr,inti){if(LlrR){returnmax〔i〕;}intmid(lr)1;pushDown(i);intleft0;intright0;if(Lmid){leftquery(L,R,l,mid,i1);}if(Rmid){rightquery(L,R,mid1,r,i11);}returnMath。max(left,right);}}树状数组解
publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}intans0;IndexTreeindexTreenewIndexTree(prices。length);for(inti0;iprices。length;i){indexTree。set(i1,prices〔i〕);}for(inti1;iprices。length;i){intminindexTree。min(i1);if(prices〔i〕min){ansMath。max(ans,prices〔i〕min);}}returnans;}publicstaticclassIndexTree{privateint〔〕tree;privateintN;0位置弃而不用publicIndexTree(intsize){Nsize;treenewint〔N1〕;for(inti1;iN;i){tree〔i〕Integer。MAXVALUE;}}返回〔1,index〕范围最小值publicintmin(intindex){intretInteger。MAXVALUE;while(index0){retMath。min(tree〔index〕,ret);indexindexindex;}returnret;}indexindex:提取出index最右侧的1出来假设index为:0011001000indexindex:0000001000publicvoidset(intindex,intv){while(indexN){tree〔index〕Math。min(tree〔index〕,v);indexindexindex;}}}最优解publicintmaxProfit(int〔〕prices){if(nullpricesprices。length2(prices。length2prices〔0〕prices〔1〕)){return0;}intans0;intminPreprices〔0〕;for(inti1;iprices。length;i){if(prices〔i〕minPre){ansMath。max(ans,prices〔i〕minPre);}else{minPreprices〔i〕;}}returnans;}
以上花式炫技,你学废了吗?