前言
Weekly Contest 101的第二道题目,同样是分值4分且中等难度的题目股票价格跨度:
编写一个
StockSpanner
类,它收集某些股票的每日报价,并返回该股票当日价格的跨度。今天股票价格的跨度被定义为股票价格小于或等于今天价格的最大连续日数(从今天开始往回数,包括今天)。
例如,如果未来7天股票的价格是
[100, 80, 60, 70, 60, 75, 85]
,那么股票跨度将是[1, 1, 1, 2, 1, 4, 6]
。示例:
输入:["StockSpanner","next","next","next","next","next","next","next"], [[],[100],[80],[60],[70],[60],[75],[85]] 输出:[null,1,1,1,2,1,4,6] 解释: 首先,初始化 S = StockSpanner(),然后: S.next(100) 被调用并返回 1, S.next(80) 被调用并返回 1, S.next(60) 被调用并返回 1, S.next(70) 被调用并返回 2, S.next(60) 被调用并返回 1, S.next(75) 被调用并返回 4, S.next(85) 被调用并返回 6。 注意 (例如) S.next(75) 返回 4,因为截至今天的最后 4 个价格 (包括今天的价格 75) 小于或等于今天的价格。
提示:
- 调用
StockSpanner.next(int price)
时,将有1 <= price <= 10^5
。- 每个测试用例最多可以调用
10000
次StockSpanner.next
。- 在所有测试用例中,最多调用
150000
次StockSpanner.next
。- 此问题的总时间限制减少了
50%
。
解题思路
这道题目其实如果只是实现题目的功能要求的话是一道很简单的题目。只是不断获取一个数组从最后一个元素开始单调递增数列的长度。
但是有由于在提示内容中已经提到了执行时间限制的问题,就可以知道这个题目需要进行执行时间相关方面的优化。最终我决定使用的优化方案是参考跳表这种数据结构,利用空间换取时间。思路大致如下,详细内容可以参考实现代码的第二版:
- 定义一个存储递增数列的实体
StockPrices
,该实体还会记录最高位(第一个元素)和最低位(最后一个元素) -
StockSpanner
中存储的是StockPrices
的数组 - 每当有新股价进入,逆序(从最后一个元素开始)遍历
StockSpanner
的StockPrices
数组。然后根据是否在当前的递增数列的范围进行处理。
以示例作为例子:
初始化后
pricesList:[
{
left:0
right:0
prices:[]
}
next(100)
pricesList:[
{
left:100
right:100
prices:[100]
}
return 1
next(80)
pricesList:[
{
left:100
right:100
prices:[100]
},
{
left:80
right:80
prices:[80]
}]
return 1
next(60)
pricesList:[
{
left:100
right:100
prices:[100]
},
{
left:80
right:80
prices:[80]
}
{
left:60
right:60
prices:[60]
}]
return 1
next(70)
pricesList:[
{
left:100
right:100
prices:[100]
},
{
left:80
right:80
prices:[80]
},
{
left:60
right:70
prices:[60,70]
}]
return 2
next(60)
pricesList:[
{
left:100
right:100
prices:[100]
},
{
left:80
right:80
prices:[80]
},
{
left:60
right:70
prices:[60,70]
},
{
left:60
right:60
prices:[60]
}]
return 1
next(75)
pricesList:[
{
left:100
right:100
prices:[100]
},
{
left:80
right:80
prices:[80]
},
{
left:60
right:70
prices:[60,70]
},
{
left:60
right:75
prices:[60,75]
}]
return 4
next(85)
pricesList:[
{
left:100
right:100
prices:[100]
},
{
left:80
right:80
prices:[80]
},
{
left:60
right:70
prices:[60,70]
},
{
left:60
right:85
prices:[60,75,85]
}]
return 6
实现代码
第一版
这个版本是只实现功能的版本,所以提交上去基本都是执行超时
的结果。但是可以作为第二版的参考。
class StockSpanner {
private List<Integer> prices;
public StockSpanner() {
prices=new ArrayList<Integer>();
}
public int next(int price) {
int result=1;
prices.add(price);
int days=prices.size();
if(days>1){
int todayPrice=price;
for(int i=days-2;i>=0;i--){
if(todayPrice>=prices.get(i)){
++result;
}else{
break;
}
}
}
return result;
}
}
第二版
/**
* 股票价格跨度
* @author RJH
* create at 2018/9/9
*/
public class StockSpanner {
private List<StockPrices> pricesList;
/**
* 存储一个递增数列的实体
*/
class StockPrices{
/**
* 最低位
*/
int left;
/**
* 最高位
*/
int right;
/**
*
*/
List<Integer> prices=new ArrayList<>();
}
public StockSpanner() {
pricesList=new ArrayList<>();
StockPrices stockPrices=new StockPrices();
pricesList.add(stockPrices);
}
public int next(int price) {
int result=0;
StockPrices stockPrices=pricesList.get(pricesList.size()-1);
List<Integer> prices=stockPrices.prices;
if(prices.size()==0){
stockPrices.left=price;
stockPrices.right=price;
prices.add(price);
result+=prices.size();
return result;
}
if(stockPrices.right<=price){//在当前股价区间内
prices.add(price);
stockPrices.right=price;
result+=prices.size();
}else{//最高位大于当前股价,生成一个新的StockPrices
StockPrices newStockPrices=new StockPrices();
newStockPrices.prices=new ArrayList<>();
newStockPrices.prices.add(price);
newStockPrices.left=price;
newStockPrices.right=price;
result+=newStockPrices.prices.size();
pricesList.add(newStockPrices);
}
for(int i=pricesList.size()-2;i>=0;i--){
StockPrices sp=pricesList.get(i);
if(sp.right>price){
break;
}else if(sp.left>price){
for(int j=sp.prices.size()-1;j>=0;j--){
if(price<=sp.prices.get(j)){
++result;
}
}
}else if(sp.left<=price){
result+=sp.prices.size();
}
}
return result;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。