【算法】使用双指针解决字节一面股票交易问题
-
题前唠嗑:hello,早上坐高铁,无聊看看订阅号,看到这样一篇文章,一位面试字节的小伙伴遇到了股票交易这么一道题,我当时就联想到了LeetCode求水池最多容纳的水量问题,结合两题和大家伙分享一波拙见_
-
题目描述:给定一个数组prices,它的第i个元素 prices[i]表示一支给定股票第i天的价格。你只能选择某一天买入这只股票,并选择在未来的某一个不同的日子卖出该股票。设计一个算法来计算你所能获取的最大利润。返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0。
-
举例:pirces[]={10,8,6,2,5,4,8,3,7}
备注:不考虑实际股票交易中的细节问题
-
股票交易问题
- 图示
-
思路
- 设置两个指针left和right,left代表买入时机,right代表卖出时机,买入时机必早于卖出时机即left<right;买入价格为prices[left],卖出价格为prices[right];收益(E)为(卖出价格-买入价格)*(卖出时机-买入时机)即E=(prices[right]-prices[left])*(right-left);
- 初始位置left=0,right=prices.length-1;
- 确定移动哪个指针(更改买入或卖出时机获得更大的价差),即比较 prices[right-1]-prices[left] 和 prices[right]-prices[left+1]的大小;case:prices[right-1]-prices[left] > prices[right]-prices[left+1], 对应卖出时机提前一天获得更大价格差的场景,则:right–;case:prices[right-1]-prices[left] <= prices[right]-prices[left+1],对应买入时机后延一天获得更大价格差的场景,则:left++;
- 比较移动前移动后两者收益,取较大者,直至买入时机无法早于卖出时机即left不大于right,此时收益为最大收益。
-
注意事项:不要对prices[right]-prices[left]做取绝对值处理,这种情况对应的是收益为负。
-
代码
public int maxEarn(int [] prices){ if(prices==null||prices.length==0){ return 0; } int left=0; int right=prices.length-1; int maxearn=(prices[right]-prices[left])*(right-left); while (left<right){//控制买入时机必早于卖出时机 if((prices[right-1]-prices[left])>(prices[right]-prices[left+1])){ right--; }else { left++; } maxearn=Math.max(maxearn,(prices[right]-prices[left])*(right-left)); } //收益为负或0则都设为0 if(maxearn<=0){ maxearn=0; } return maxearn; }
-
求水池最多容纳的水量问题
官方题目描述:给你 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0) 。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
为了与上述prices区分,这里使用height作为传入参数的名字,值不变,即height={10,8,6,2,5,4,8,3,7}
-
图示
-
思路
- 设置两个指针left和right,left代表水池左边墙的位置,right代表水池右边墙的位置;左右墙高度分别为height[left],height[right];所能容纳的水量V=左右墙较矮的墙高*两墙间距,即V=min(height[left],heigth[right])*(right-left);
- 初始位置left=0,right=prices.length-1;
- 确定移动哪个指针,此处逻辑比较简单,移动两墙中矮的那个墙才有可能获得更大的水池容量,case:height[left]>height[right],左墙高于右墙,移动右墙,则:right–;case:height[left]<=height[right],左墙矮(或等)于右墙,移动左墙,则:left++;
- 比较移动前移动后两者容量,取较大者,直至左墙无法在右墙的左边,即left不大于right,此时容量为最大容量。
-
代码
-
public int maxArea(int[] height) {
if(height==null||height.length==0){
return 0;
}
int left=0;
int right=height.length-1;
int maxarea=Math.min(height[left],height[right])*(right-left);
while (left<right) {//左边的墙必在右边的墙左边
if(height[left]<=height[right]){
left++;
}else {
right--;
}
maxarea=Math.max(maxarea,Math.min(height[left],height[right])*(right-left));
}
return maxarea;
}
-
总结
- 在解决类似的问题时,应该把具体的问题抽象出来,买入时机、左墙抽象为左指针,卖出时机、右墙抽象为右指针;
- 先用文字描述出求取结果的表达式如:收益(E)为(卖出价格-买入价格)*(卖出时机-买入时机)、所能容纳的水量V=左右墙较矮的墙高*两墙间距,再将文字描述转化为数学描述;
- 使用双指针的关键点在于指针移动的终止条件和确定移动哪个指针。
鄙人拙见,如有不当之处,望各位不吝赐教,互相交流,共同成长。