LeetCode.134 加油站
题目链接 加油站
题解
class Solution {
public int canCompleteCircuit(int[] gas, int[] cost) {
int curSum = 0;
int targetSum = 0;
int startIndex = 0;
for(int i = 0;i<gas.length;i++){
curSum += gas[i] - cost[i];
targetSum += gas[i] - cost[i];
if(curSum < 0) {
curSum = 0;
startIndex = i + 1;
}
}
if(targetSum < 0) return -1;
return startIndex;
}
}
解题思路
问题描述:假设有一个环形路线上有 n 个加油站,每个加油站有汽油 gas [i],从第 i 个加油站开到下一个加油站需要消耗汽油 cost [i]。你需要判断是否存在一个起点,使得从该起点出发可以绕环形路线一周。如果存在,返回该起点的索引;否则返回 - 1。
算法思路:
- 定义两个变量:
curSum
用于累计当前路段的剩余油量,targetSum
用于累计全程的总剩余油量 startIndex
记录可能的起点位置- 遍历每个加油站:
- 计算当前加油站的剩余油量(gas [i] - cost [i]),并累加到
curSum
和targetSum
- 如果
curSum
小于 0,说明从当前起点到这里无法通过,需要重新设置起点为下一个加油站,并重置curSum
- 计算当前加油站的剩余油量(gas [i] - cost [i]),并累加到
- 遍历结束后,如果
targetSum
小于 0,说明全程总油量不足以支撑一圈,返回 - 1 - 否则,返回记录的起点
startIndex
这个算法的时间复杂度是 O (n),空间复杂度是 O (1),非常高效。其核心思想是:如果从起点 A 到 B 无法到达,那么 A 到 B 之间的任何一点都不能作为起点;如果全程总油量充足,那么必然存在一个可以完成环路的起点。
LeetCode.135 分发糖果
题目链接 分发糖果
题解
class Solution {
public int candy(int[] ratings) {
int n = ratings.length;
int[] res = new int[n];
res[0]=1;
for(int i = 1;i<n;i++){
if(ratings[i] > ratings[i-1]) {
res[i] = res[i-1] + 1;
} else res[i] = 1;
}
for(int i = n - 2;i>=0;i--){
if(ratings[i] > ratings[i+1]) res[i] = Math.max(res[i],res[i+1] + 1);
}
int result = 0;
for(int i = 0;i<n;i++) result += res[i];
return result;
}
}
解题思路
这段代码实现了一个解决 "分发糖果" 问题的算法。让我来解释一下它的工作原理:
问题描述:有 n 个孩子站成一排,每个孩子有一个评分。需要给这些孩子分发糖果,规则是:
- 每个孩子至少得 1 颗糖果
- 评分更高的孩子必须比他相邻的孩子得到更多的糖果
算法思路:
- 创建一个结果数组
res
,用于存储每个孩子应得的糖果数 - 首先从左到右遍历:
- 第一个孩子至少得 1 颗糖果
- 对于每个后续孩子,如果他的评分高于左边孩子,则他的糖果数比左边多 1
- 否则,他至少得 1 颗糖果
- 然后从右到左遍历:
- 对于每个孩子,如果他的评分高于右边孩子,则取他当前糖果数和右边孩子糖果数 + 1 中的较大值
- 最后将所有孩子的糖果数相加,得到总糖果数
这个算法的时间复杂度是 O (n),空间复杂度是 O (n)。其核心思想是通过两次遍历确保同时满足左右两边的条件:
- 左到右遍历确保每个孩子比左边评分低的孩子得到更多糖果
- 右到左遍历确保每个孩子比右边评分低的孩子得到更多糖果
代码逻辑清晰,通过两次单向遍历就解决了需要同时考虑左右邻居的问题,是这个问题的高效解法。
LeetCode.860 柠檬水找零
题目链接 柠檬水找零
题解
class Solution {
public boolean lemonadeChange(int[] bills) {
int five = 0;
int ten = 0;
for(int i = 0;i<bills.length;i++){
if(bills[i] == 5){
five ++;
}
if(bills[i] == 10){
ten ++;
five --;
}
if(bills[i] == 20){
if(ten > 0){
ten --;
five --;
}else {
five -=3;
}
}
if(ten < 0 || five < 0) return false;
}
return true;
}
}
解题思路
这段代码实现了一个解决 "柠檬水找零" 问题的算法。让我来解释一下它的工作原理:
问题描述:假设你正在经营一家柠檬水摊,每杯柠檬水售价 5 美元。顾客排队购买,每人只会付 5 美元、10 美元或 20 美元。你需要判断能否给每个顾客都正确找零(假设开始时你没有任何零钱)。
- 定义两个变量
five
和ten
,分别记录手中 5 美元和 10 美元钞票的数量 - 遍历每位顾客支付的钞票:
- 如果收到 5 美元,直接增加 5 美元钞票的数量
- 如果收到 10 美元,需要找零 5 美元,因此减少 1 张 5 美元钞票,增加 1 张 10 美元钞票
- 如果收到 20 美元,优先用 1 张 10 美元 + 1 张 5 美元找零(因为 5 美元更灵活),如果没有 10 美元则用 3 张 5 美元找零
- 每次交易后检查钞票数量是否为负数,如果是则说明无法正确找零,返回 false
- 遍历结束后如果都能正确找零,返回 true
这个算法的时间复杂度是 O (n),空间复杂度是 O (1)。其核心思想是:
- 尽量保留更多的 5 美元钞票,因为它可以用于各种面额的找零
- 20 美元钞票无法用于找零,所以不需要记录其数量
LeetCode.406 根据身高重建队列
题目链接 根据身高重建队列
题解
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][]);
}
}
解题思路
这段代码实现了一个解决 "根据身高重建队列" 问题的算法。让我来解释一下它的工作原理:
问题描述:假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中的一些人的属性(每个 people [i] = [h, k] 表示第 i 个人的身高为 h,前面正好有 k 个身高大于或等于 h 的人)。请你重新构造并返回输入数组 people 所表示的队列。
- 首先对人群进行排序:
- 按照身高 h 从高到低排序(降序)
- 当身高相同时,按照前面应有的人数 k 从小到大排序(升序)
- 创建一个链表作为队列容器
- 遍历排序后的人群,按照每个人的 k 值将其插入到链表的对应位置
- 最后将链表转换为数组返回
这个算法的时间复杂度是 O (n²)(排序 O (nlogn) + 插入操作 O (n²)),空间复杂度是 O (n)。