根据题目条件可以知道:一个孩子所得到的糖果由它前面的孩子和后面的孩子的评分共同决定,那么我们只要得到该孩子根据左边的孩子评分得到的糖果数,和根据右边的孩子评分得到的糖果数,相比较,取较大值,就能得到孩子最终得到的糖果数
最小糖果数就更容易了,开始每人分配一个糖果,评分高的就多分配一个糖果。
代码如下
int candy(int* ratings, int ratingsSize){
int left[ratingsSize],right[ratingsSize];
for(int i=0;i<ratingsSize;i++)
left[i]=right[i]=1;
int sum=0;
for(int i=1;i<ratingsSize;i++)
{
if(ratings[i]>ratings[i-1])
left[i]=left[i-1]+1;
}
for(int i=ratingsSize-2;i>=0;i--)
{
if(ratings[i]>ratings[i+1])
right[i]=right[i+1]+1;
}
for(int i=0;i<ratingsSize;i++)
{
sum+=fmax(left[i],right[i]);
}
return sum;
}
时间复杂度是O(n),空间复杂度是O(n)
进阶:如何将空间复杂度变为O(1)
这里介绍几种优化空间复杂度的方法:
1.原地算法---就是用原本就有的空间完成变化
2.滚动数组--就是用两个数组交替使用的方法,实现递推的过程---一般是二维数组的优化
3.模拟---通过模拟空间复杂度高的算法的规则,用几个变量模拟实现功能
那么这题又是何种思路?
这里我们选用方法三模拟,至于这是怎么选出来的,就只能是仁者见仁,智者见智(我现在的水平也说不太清楚,我只能说,题做多了,就会有感觉)
那么,怎么个模拟法?
其实单独模拟从左到右得到left[]数组的和很简单,单独模拟从右往左的right[]数组之和也很简单,但是两者要相互影响着实现就很难(两者需要在一次遍历中实现),因为我们只能用常量个空间,不能用数组记录数据。
那么我们看看上面算法中的两个数组left[]和right[],它们的实现其实和单调性有关,只要递增就比前一个大1,否则就是1,这其实在从左往右的遍历中很容易实现,但是从右往左的递增过程得变成从左往右的递减过程,而这就会导致该元素的大小由它后面的元素决定,其实本质是由递减的长度决定,那么怎么实现其实就很清晰了,只有有个变量记录递减的长度,就可以得到该元素的实时值
代码实现如下(需要仔细琢磨)
int candy(int* ratings, int ratingsSize){
int dec=0,inc=1;//dec是单调递减序列长度,inc是单调递增序列长度
//这里之所以赋值不同的原因在于,我们默认第一个数是递增序列中的数
int sum=1,n=1;//n代表每一个人基本糖果数为1,sum代表只有一个人时糖果数为1
for(int i=1;i<ratingsSize;i++)//这里从第二个数开始讨论与上面的sum和inc的赋值相对应
{
if(ratings[i-1]<ratings[i])
{
n++;//糖果数比前一个多一
sum+=n;//总数加上这个糖果数
inc=n;//这里是因为每次递增n++,n其实就是递增序列的长度
dec=0;//递增开始说明递减序列长度要从0开始算
}
else if(ratings[i-1]>ratings[i])
{
n=1;//递减开始
dec++;
//注意:假设评分是1 2 4 3 2 1,读者可以自己看看没有下面的if语句是否能行?
if(inc==dec)
dec++;
sum+=dec;//这里的加长度是给递减序列之前的元素一起+1的意思
}
else
{
n=1;
inc=1;
dec=0;
sum+=n;
}
}
return sum;
}