本来前段时间打算每天刷2道题,后来不想刷了,出现了刷题厌恶症,经过一番斗争,就打算随意刷了,隔两天会刷刷题,今天终于刷了2道,可喜可贺,再记录下:
题目一 238. 除自身以外数组的乘积
这个题一看,好像不难,主要是题目限制不能用除法,还要求O(n),想了一会,无解,就看题解了,
题解是用两个数组,分别记录当前位置的左乘积和、右乘积和,然后再遍历一遍就出来了,看了之后还是挺简单的,这道题无痛刷题,还好还好。
var productExceptSelf = function(nums) {
const len = nums.length;
const left = [1];
for (let i = 1; i < len; i++) {
left[i] = left[i - 1] * nums[i - 1];
}
const right = [];
right[len - 1] = 1;
for (let i = len - 2; i >= 0; i--) {
right[i] = right[i + 1] * nums[i + 1];
}
const result = [];
for (let i = 0; i < len; i++) {
result[i] = left[i] * right[i];
}
return result;
};
题目二 134. 加油站
写了个暴力算法,超时了,看题解是贪心算法,算法思路,不断计算gas[j] - cost[j]的和,如果和小于0了,则说明无法到达j,则从之前的所有节点出发都无法到达j,需要更新出发点和当前的和。
为什么小于0就说明前面的节点都不能到达了呢?假如中间某个k可以到达j,则说明该段累加和大于0,这将路程分为2段,从开始i到k,从k到j,从i到j的累加和小于0,而k到j大于0,则肯定i到k小于0,那就矛盾了,应该从k开始了。
var canCompleteCircuit = function(gas, cost) {
let curSum = 0;
let totalSum = 0;
let start = 0;
for (let i = 0; i < gas.length; i++) {
curSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if (curSum < 0) {
start = i + 1;
curSum = 0;
}
}
if (totalSum < 0) {
return -1;
}
return start;
};
ps: 算法还是难啊!!
题目三、135. 分发糖果
困难题,之前好像看过解法,又忘了,说明木有掌握。。。这次又直接看答案,看了答案倒是不难,但是自己想确实想不出来。
如果在考虑局部的时候想两边兼顾,就会顾此失彼。
那么本题采用了两次贪心的策略:
- 一次是从左到右遍历,只比较右边孩子评分比左边大的情况。
- 一次是从右到左遍历,只比较左边孩子评分比右边大的情况。
这样从局部最优推出了全局最优,即:相邻的孩子中,评分高的孩子获得更多的糖果。
但其实我在想,这种思路到底是怎么想出来的?为什么是这样的思路?
#
var candy = function(ratings) {
const len = ratings.length;
const left = [1];
for (let i = 1; i < len; i++) {
if (ratings[i] > ratings[i - 1]) {
left[i] = left[i - 1] + 1;
} else {
left[i] = 1;
}
}
let right = 0, ret = 0;
for (let i = len - 1; i >= 0; i--) {
if (i < len - 1 && ratings[i] > ratings[i + 1]) {
right++;
} else {
right = 1;
}
ret += Math.max(left[i], right);
}
return ret;
};