1. 加油站
从第一个开始遍历所有加油站,记录当前站点的油量变化量gas[i] - cost[i],并加到currentSum和totalSum上。如果在某一站currentSum<0,证明无法到达当前节点,就要将下一节点i+1当作start节点,并将currentSum置零。遍历完后如果totalSum>0证明可以环游一圈,当前start即为开始节点。
本题有以下两个可能的疑问:
1.会不会存在0-start中间的某个节点,从那里到当前节点也可以实现currtSum>0?
实际上不会,假设存在这个节点,区间2>0,那么区间和1必定<0,根据我们定的规则仍然是从start也就是该节点下一个开始。
2.假如从中途某个节点开始,那currentSum只记录了后面的变化和,能否保证走完前面的路程?
假设start前面区间和为A,后面区间和为B,A<0,B>0,A+B=totalSum>0,那么A的绝对值一定小于B,即可以走完前面的路程。
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int currentSum = 0;
int totalSum = 0;
int start = 0;
for(int i = 0; i < gas.length; i++){
currentSum += gas[i] - cost[i];
totalSum += gas[i] - cost[i];
if(currentSum < 0){
start = i + 1;
currentSum = 0;
}
}
if(totalSum < 0){
return -1;
}
return start;
}
}
2. 分发糖果
力扣
本题涉及的思想是要处理好一边再去处理另一边
先从左向右遍历贪心一遍,即右边大于左边的情况。如果当前的比前一个大就加一,否则就置为1。再从右向左遍历一遍,即左边大于右边的情况。这个遍历不能做左向右,因为当前比较要根据右边的比较结果,如下图。遍历时如果当前节点比左边大,那么有两个取值:原本的candyVec[i]和新的candyVec[i+1]+1,取二者中大的那个,这样保证对左右都是更大的那个。
class Solution {
public int candy(int[] ratings) {
int[] candyVec = new int[ratings.length];
candyVec[0] = 1;
for(int i = 1; i < candyVec.length; i++){
candyVec[i] = ratings[i] > ratings[i-1] ? candyVec[i-1] + 1 : 1;
}
for(int i = candyVec.length - 2; i >= 0; i--){
if(ratings[i] > ratings[i + 1]){
candyVec[i] = Math.max(candyVec[i+1] + 1, candyVec[i]);
}
}
int count = 0;
for(int num : candyVec){
count += num;
}
return count;
}
}
3. 柠檬水找零
力扣
如果对一道题没有思路,将所有的情况都罗列出来,想好对应的处理方法,会找到一些解决方法。
用三个数记录不同面额钱的数量,遍历数组,对每个元素分别讨论,如果无法处理就返回false。
class Solution {
public boolean lemonadeChange(int[] bills) {
if(bills[0] != 5){
return false;
}
int five = 0;
int ten = 0;
int twenty = 0;
for(int i = 0; i < bills.length; i++){
if(bills[i] == 5){
five++;
}
if(bills[i] == 10){
if(five <= 0){
return false;
}
five--;
ten++;
}
if(bills[i] == 20){
if(ten > 0 && five > 0){
ten--;
five--;
}else if(five >= 3){
five -= 3;
}else{
return false;
}
}
}
return true;
}
}
4. 根据身高重建队列
力扣
本题和分发糖果有一点类似,都是先处理一边再处理另一边。
本题要求重新排列二维数组,对每个数组中的两个元素[身高,前面大于等于本身高的人数]都有要求,要先处理好一个再处理另一个。发现先按从大到小排列身高可以满足要求。
按从大到小排列身高,如果说身高相同元素[2]小的在前面。按照身高排好后,从前往后遍历,如果当前元素的[2]不符合直接插到对应下标即可。因为后面的都比前面的小,插到前面也不会影响原本已插入的节点。java中可以用链表实现。
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (a,b) ->{
if(a[0] == b[0]){
return a[1] - b[1];
}
return b[0] - a[0];
});
LinkedList<int[]> que = new LinkedList<>();
for(int[] p : people){
que.add(p[1], p);
}
return que.toArray(new int[people.length][]);
}
}